mirror of
https://github.com/go-task/task.git
synced 2026-06-22 04:05:53 +00:00
Compare commits
119 Commits
split-exec
...
v3.45.5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
72e25a25fd | ||
|
|
a496ee5fcb | ||
|
|
ef4292c42f | ||
|
|
dc315efc7f | ||
|
|
a3a3e7fb0b | ||
|
|
ee99849b1d | ||
|
|
bf9dc3f662 | ||
|
|
94f82cbc5a | ||
|
|
b14318ed3f | ||
|
|
17757c0c15 | ||
|
|
19f72b7eb0 | ||
|
|
0052ad2309 | ||
|
|
af1e755196 | ||
|
|
43074c20f2 | ||
|
|
39c86992bd | ||
|
|
c71241bcbd | ||
|
|
7c2bb78540 | ||
|
|
32e675895a | ||
|
|
786813d95d | ||
|
|
f7287c503a | ||
|
|
413574e3ee | ||
|
|
4b39becf65 | ||
|
|
15b7e3c69a | ||
|
|
7c93ea8b44 | ||
|
|
6a7cfa58f9 | ||
|
|
74b93f6eef | ||
|
|
88101613c8 | ||
|
|
599591ad3c | ||
|
|
348158a5f6 | ||
|
|
c3e410e95a | ||
|
|
42bcd5406a | ||
|
|
ba23aca631 | ||
|
|
5ef245a4bd | ||
|
|
036a60f517 | ||
|
|
9c969541a5 | ||
|
|
a52b483dd0 | ||
|
|
4e84c6bb76 | ||
|
|
0f9baf62a1 | ||
|
|
979ad523ef | ||
|
|
975c07688e | ||
|
|
67a02255b5 | ||
|
|
028ae1a660 | ||
|
|
68b1d2783d | ||
|
|
12793c350d | ||
|
|
8716ab81be | ||
|
|
c2a4e4470b | ||
|
|
f5a8ec8a0c | ||
|
|
048d92709a | ||
|
|
8dc9637e7a | ||
|
|
700bf00107 | ||
|
|
4836d42828 | ||
|
|
5762d5ef8e | ||
|
|
9f2fe0da61 | ||
|
|
d1a5771839 | ||
|
|
7663abdcde | ||
|
|
1e42e1f817 | ||
|
|
5f7ae5d32e | ||
|
|
17db402e4b | ||
|
|
f2242958a6 | ||
|
|
ea4b695b5a | ||
|
|
209c88c341 | ||
|
|
bd94f9f607 | ||
|
|
9f6b78ec84 | ||
|
|
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 |
@@ -8,6 +8,6 @@ charset = utf-8
|
|||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
|
|
||||||
[*.{md,mdx,yml,yaml,json,toml,htm,html,js,ts,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_style = space
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ jobs:
|
|||||||
issue-awaiting-response:
|
issue-awaiting-response:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/github-script@v7
|
- uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
github-token: ${{secrets.GH_PAT}}
|
github-token: ${{secrets.GH_PAT}}
|
||||||
script: |
|
script: |
|
||||||
|
|||||||
2
.github/workflows/issue-closed.yml
vendored
2
.github/workflows/issue-closed.yml
vendored
@@ -8,7 +8,7 @@ jobs:
|
|||||||
issue-closed:
|
issue-closed:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/github-script@v7
|
- uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
github-token: ${{secrets.GH_PAT}}
|
github-token: ${{secrets.GH_PAT}}
|
||||||
script: |
|
script: |
|
||||||
|
|||||||
14
.github/workflows/issue-experiment.yml
vendored
14
.github/workflows/issue-experiment.yml
vendored
@@ -9,7 +9,7 @@ jobs:
|
|||||||
if: github.event.label.name == format('status{0} proposed', ':')
|
if: github.event.label.name == format('status{0} proposed', ':')
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/github-script@v7
|
- uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
github-token: ${{secrets.GH_PAT}}
|
github-token: ${{secrets.GH_PAT}}
|
||||||
script: |
|
script: |
|
||||||
@@ -23,7 +23,7 @@ jobs:
|
|||||||
if: github.event.label.name == format('status{0} draft', ':')
|
if: github.event.label.name == format('status{0} draft', ':')
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/github-script@v7
|
- uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
github-token: ${{secrets.GH_PAT}}
|
github-token: ${{secrets.GH_PAT}}
|
||||||
script: |
|
script: |
|
||||||
@@ -37,7 +37,7 @@ jobs:
|
|||||||
if: github.event.label.name == format('status{0} candidate', ':')
|
if: github.event.label.name == format('status{0} candidate', ':')
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/github-script@v7
|
- uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
github-token: ${{secrets.GH_PAT}}
|
github-token: ${{secrets.GH_PAT}}
|
||||||
script: |
|
script: |
|
||||||
@@ -51,7 +51,7 @@ jobs:
|
|||||||
if: github.event.label.name == format('status{0} stable', ':')
|
if: github.event.label.name == format('status{0} stable', ':')
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/github-script@v7
|
- uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
github-token: ${{secrets.GH_PAT}}
|
github-token: ${{secrets.GH_PAT}}
|
||||||
script: |
|
script: |
|
||||||
@@ -65,7 +65,7 @@ jobs:
|
|||||||
if: github.event.label.name == format('status{0} released', ':')
|
if: github.event.label.name == format('status{0} released', ':')
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/github-script@v7
|
- uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
github-token: ${{secrets.GH_PAT}}
|
github-token: ${{secrets.GH_PAT}}
|
||||||
script: |
|
script: |
|
||||||
@@ -85,7 +85,7 @@ jobs:
|
|||||||
if: github.event.label.name == format('status{0} abandoned', ':')
|
if: github.event.label.name == format('status{0} abandoned', ':')
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/github-script@v7
|
- uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
github-token: ${{secrets.GH_PAT}}
|
github-token: ${{secrets.GH_PAT}}
|
||||||
script: |
|
script: |
|
||||||
@@ -105,7 +105,7 @@ jobs:
|
|||||||
if: github.event.label.name == format('status{0} superseded', ':')
|
if: github.event.label.name == format('status{0} superseded', ':')
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/github-script@v7
|
- uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
github-token: ${{secrets.GH_PAT}}
|
github-token: ${{secrets.GH_PAT}}
|
||||||
script: |
|
script: |
|
||||||
|
|||||||
2
.github/workflows/issue-needs-triage.yml
vendored
2
.github/workflows/issue-needs-triage.yml
vendored
@@ -8,7 +8,7 @@ jobs:
|
|||||||
issue-needs-triage:
|
issue-needs-triage:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/github-script@v7
|
- uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
github-token: ${{secrets.GH_PAT}}
|
github-token: ${{secrets.GH_PAT}}
|
||||||
script: |
|
script: |
|
||||||
|
|||||||
45
.github/workflows/lint.yml
vendored
45
.github/workflows/lint.yml
vendored
@@ -13,14 +13,14 @@ jobs:
|
|||||||
name: Lint
|
name: Lint
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go-version: [1.23.x, 1.24.x]
|
go-version: [1.24.x, 1.25.x]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: ${{matrix.go-version}}
|
go-version: ${{matrix.go-version}}
|
||||||
|
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v8
|
uses: golangci/golangci-lint-action@v8
|
||||||
@@ -30,45 +30,14 @@ jobs:
|
|||||||
lint-jsonschema:
|
lint-jsonschema:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
python-version: 3.12
|
python-version: 3.14
|
||||||
|
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: install check-jsonschema
|
- name: install check-jsonschema
|
||||||
run: python -m pip install 'check-jsonschema==0.27.3'
|
run: python -m pip install 'check-jsonschema==0.27.3'
|
||||||
|
|
||||||
- name: check-jsonschema (metaschema)
|
- name: check-jsonschema (metaschema)
|
||||||
run: check-jsonschema --check-metaschema website/static/schema.json
|
run: check-jsonschema --check-metaschema website/src/public/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.')
|
|
||||||
|
|||||||
9
.github/workflows/release-nightly.yml
vendored
9
.github/workflows/release-nightly.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: Realease nightly
|
name: Release nightly
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
@@ -9,14 +9,14 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: 1.24.x
|
go-version: 1.25.x
|
||||||
|
|
||||||
- name: Run GoReleaser
|
- name: Run GoReleaser
|
||||||
uses: goreleaser/goreleaser-action@v6
|
uses: goreleaser/goreleaser-action@v6
|
||||||
@@ -27,3 +27,4 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{secrets.GH_PAT}}
|
GITHUB_TOKEN: ${{secrets.GH_PAT}}
|
||||||
GORELEASER_KEY: ${{secrets.GORELEASER_KEY}}
|
GORELEASER_KEY: ${{secrets.GORELEASER_KEY}}
|
||||||
|
CLOUDSMITH_TOKEN: ${{secrets.CLOUDSMITH_TOKEN}}
|
||||||
|
|||||||
24
.github/workflows/release.yml
vendored
24
.github/workflows/release.yml
vendored
@@ -10,14 +10,26 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v6
|
||||||
with:
|
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
|
||||||
|
with:
|
||||||
|
package_json_file: 'website/package.json'
|
||||||
|
run_install: 'true'
|
||||||
|
|
||||||
- name: Run GoReleaser
|
- name: Run GoReleaser
|
||||||
uses: goreleaser/goreleaser-action@v6
|
uses: goreleaser/goreleaser-action@v6
|
||||||
@@ -28,3 +40,9 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{secrets.GH_PAT}}
|
GITHUB_TOKEN: ${{secrets.GH_PAT}}
|
||||||
GORELEASER_KEY: ${{secrets.GORELEASER_KEY}}
|
GORELEASER_KEY: ${{secrets.GORELEASER_KEY}}
|
||||||
|
CLOUDSMITH_TOKEN: ${{secrets.CLOUDSMITH_TOKEN}}
|
||||||
|
|
||||||
|
- name: Deploy Website
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
task website:deploy:prod
|
||||||
|
|||||||
6
.github/workflows/test.yml
vendored
6
.github/workflows/test.yml
vendored
@@ -13,18 +13,18 @@ jobs:
|
|||||||
name: Test
|
name: Test
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go-version: [1.23.x, 1.24.x]
|
go-version: [1.24.x, 1.25.x]
|
||||||
platform: [ubuntu-latest, macos-latest, windows-latest]
|
platform: [ubuntu-latest, macos-latest, windows-latest]
|
||||||
runs-on: ${{matrix.platform}}
|
runs-on: ${{matrix.platform}}
|
||||||
steps:
|
steps:
|
||||||
- name: Set up Go ${{matrix.go-version}}
|
- name: Set up Go ${{matrix.go-version}}
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version: ${{matrix.go-version}}
|
go-version: ${{matrix.go-version}}
|
||||||
id: go
|
id: go
|
||||||
|
|
||||||
- name: Check out code into the Go module directory
|
- name: Check out code into the Go module directory
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v5
|
||||||
|
|
||||||
- name: Download Go modules
|
- name: Download Go modules
|
||||||
run: go mod download
|
run: go mod download
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ nfpms:
|
|||||||
formats:
|
formats:
|
||||||
- deb
|
- deb
|
||||||
- rpm
|
- rpm
|
||||||
|
- apk
|
||||||
file_name_template: '{{.ProjectName}}_{{.Os}}_{{.Arch}}'
|
file_name_template: '{{.ProjectName}}_{{.Os}}_{{.Arch}}'
|
||||||
contents:
|
contents:
|
||||||
- src: completion/bash/task.bash
|
- src: completion/bash/task.bash
|
||||||
@@ -135,3 +136,37 @@ winget:
|
|||||||
owner: microsoft
|
owner: microsoft
|
||||||
name: winget-pkgs
|
name: winget-pkgs
|
||||||
branch: master
|
branch: master
|
||||||
|
|
||||||
|
|
||||||
|
npms:
|
||||||
|
- name: "@go-task/cli"
|
||||||
|
repository: "git+https://github.com/go-task/task.git"
|
||||||
|
bugs: https://github.com/go-task/task/issues
|
||||||
|
description: A task runner / simpler Make alternative written in Go
|
||||||
|
homepage: https://taskfile.dev
|
||||||
|
license: MIT
|
||||||
|
author: "The Task authors"
|
||||||
|
access: public
|
||||||
|
keywords:
|
||||||
|
- "task"
|
||||||
|
- "taskfile"
|
||||||
|
- "build-tool"
|
||||||
|
- "task-runner"
|
||||||
|
|
||||||
|
|
||||||
|
cloudsmiths:
|
||||||
|
- organization: "task"
|
||||||
|
repository: "{{if not .IsNightly}}task{{end}}"
|
||||||
|
formats:
|
||||||
|
- deb
|
||||||
|
- rpm
|
||||||
|
- apk
|
||||||
|
distributions:
|
||||||
|
deb:
|
||||||
|
- "any-distro/any-version"
|
||||||
|
rpm:
|
||||||
|
- "any-distro/any-version"
|
||||||
|
alpine:
|
||||||
|
- "alpine/any-version"
|
||||||
|
component: main
|
||||||
|
republish: true
|
||||||
|
|||||||
13
.vscode/settings-sample.json
vendored
13
.vscode/settings-sample.json
vendored
@@ -1,15 +1,12 @@
|
|||||||
{
|
{
|
||||||
"yaml.schemas": {
|
"yaml.schemas": {
|
||||||
"./website/static/schema.json": [
|
"./website/src/public/schema.json": [
|
||||||
"Taskfile.yml",
|
"Taskfile.yml",
|
||||||
"tmp/**/*.yml"
|
"Taskfile.yaml",
|
||||||
|
"taskfile.yml",
|
||||||
|
"taskfile.yaml"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"search.exclude": {
|
|
||||||
"**/versioned_docs": true,
|
|
||||||
"**/versioned_sidesbars": true,
|
|
||||||
"**/i18n": true
|
|
||||||
},
|
|
||||||
"gopls": {
|
"gopls": {
|
||||||
"formatting.local": "github.com/go-task"
|
"formatting.local": "github.com/go-task"
|
||||||
},
|
},
|
||||||
|
|||||||
88
CHANGELOG.md
88
CHANGELOG.md
@@ -1,5 +1,93 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## v3.45.5 - 2025-11-11
|
||||||
|
|
||||||
|
- Fixed bug that made a generic message, instead of an useful one, appear when a
|
||||||
|
Taskfile could not be found (#2431 by @andreynering).
|
||||||
|
- Fixed a bug that caused an error when including a Remote Git Taskfile (#2438
|
||||||
|
by @twelvelabs).
|
||||||
|
- Fixed issue where `.taskrc.yml` was not returned if reading it failed, and
|
||||||
|
corrected handling of remote entrypoint Taskfiles (#2460, #2461 by @vmaerten).
|
||||||
|
- Improved performance of `--list` and `--list-all` by introducing a faster
|
||||||
|
compilation method that skips source globbing and checksum updates (#1322,
|
||||||
|
#2053 by @vmaerten).
|
||||||
|
- Fixed a concurrency bug with `output: group`. This ensures that begin/end
|
||||||
|
parts won't be mixed up from different tasks (#1208, #2349, #2350 by
|
||||||
|
@trulede).
|
||||||
|
- Do not re-evaluate variables for `defer:` (#2244, #2418 by @trulede).
|
||||||
|
- Improve error message when a Taskfile is not found (#2441, #2494 by @vmaerten).
|
||||||
|
- Fixed generic error message `exit status 1` when a dependency task failed
|
||||||
|
(#2286 by @GrahamDennis).
|
||||||
|
- Fixed YAML library from the unmaintained `gopkg.in/yaml.v3` to the new fork
|
||||||
|
maintained by the official YAML org (#2171, #2434 by @andreynering).
|
||||||
|
- On Windows, the built-in version of the `rm` core utils contains a fix related
|
||||||
|
to the `-f` flag (#2426,
|
||||||
|
[u-root/u-root#3464](https://github.com/u-root/u-root/pull/3464),
|
||||||
|
[mvdan/sh#1199](https://github.com/mvdan/sh/pull/1199),
|
||||||
|
#2506 by @andreynering).
|
||||||
|
|
||||||
|
## v3.45.4 - 2025-09-17
|
||||||
|
|
||||||
|
- Fixed a bug where `cache-expiry` could not be defined in `.taskrc.yml` (#2423
|
||||||
|
by @vmaerten).
|
||||||
|
- Fixed a bug where `.taskrc.yml` files in parent folders were not read
|
||||||
|
correctly (#2424 by @vmaerten).
|
||||||
|
- Fixed a bug where autocomplete in subfolders did not work with zsh (#2425 by
|
||||||
|
@vmaerten).
|
||||||
|
|
||||||
|
## v3.45.3 - 2025-09-15
|
||||||
|
|
||||||
|
- Task now includes built-in core utilities to greatly improve compatibility on
|
||||||
|
Windows. This means that your commands that uses `cp`, `mv`, `mkdir` or any
|
||||||
|
other common core utility will now work by default on Windows, without extra
|
||||||
|
setup. This is something we wanted to address for many many years, and it's
|
||||||
|
finally being shipped!
|
||||||
|
[Read our blog post this the topic](https://taskfile.dev/blog/windows-core-utils).
|
||||||
|
(#197, #2360 by @andreynering).
|
||||||
|
- :sparkles: Built and deployed a [brand new website](https://taskfile.dev)
|
||||||
|
using [VitePress](https://vitepress.dev) (#2359, #2369, #2371, #2375, #2378 by
|
||||||
|
@vmaerten, @andreynering, @pd93).
|
||||||
|
- Began releasing
|
||||||
|
[nightly builds](https://github.com/go-task/task/releases/tag/nightly). This
|
||||||
|
will allow people to test our changes before they are fully released and
|
||||||
|
without having to install Go to build them (#2358 by @vmaerten).
|
||||||
|
- Added support for global config files in `$XDG_CONFIG_HOME/task/taskrc.yml` or
|
||||||
|
`$HOME/.taskrc.yml`. Check out our new
|
||||||
|
[configuration guide](https://taskfile.dev/docs/reference/config) for more
|
||||||
|
details (#2247, #2380, #2390, #2391 by @vmaerten, @pd93).
|
||||||
|
- Added experiments to the taskrc schema to clarify the expected keys and values
|
||||||
|
(#2235 by @vmaerten).
|
||||||
|
- Added support for new properties in `.taskrc.yml`: insecure, verbose,
|
||||||
|
concurrency, remote offline, remote timeout, and remote expiry. :warning:
|
||||||
|
Note: setting offline via environment variable is no longer supported. (#2389
|
||||||
|
by @vmaerten)
|
||||||
|
- Added a `--nested` flag when outputting tasks using `--list --json`. This will
|
||||||
|
output tasks in a nested structure when tasks are namespaced (#2415 by @pd93).
|
||||||
|
- Enhanced support for tasks with wildcards: they are now logged correctly, and
|
||||||
|
wildcard parameters are fully considered during fingerprinting (#1808, #1795
|
||||||
|
by @vmaerten).
|
||||||
|
- Fixed panic when a variable was declared as an empty hash (`{}`) (#2416, #2417
|
||||||
|
by @trulede).
|
||||||
|
|
||||||
|
#### Package API
|
||||||
|
|
||||||
|
- Bumped the minimum version of Go to 1.24 (#2358 by @vmaerten).
|
||||||
|
|
||||||
|
#### Other news
|
||||||
|
|
||||||
|
We recently released our
|
||||||
|
[official GitHub Action](https://github.com/go-task/setup-task). This is based
|
||||||
|
on the fantastic work by the Arduino team who created and maintained the
|
||||||
|
community version. Now that this is officially adopted, fixes/updates should be
|
||||||
|
more timely. We have already merged a couple of longstanding PRs in our
|
||||||
|
[first release](https://github.com/go-task/setup-task/releases/tag/v1.0.0) (by
|
||||||
|
@pd93, @shrink, @trim21 and all the previous contributors to
|
||||||
|
[arduino/setup-task](https://github.com/arduino/setup-task/)).
|
||||||
|
|
||||||
|
## v3.45.0-v3.45.2 - 2025-09-15
|
||||||
|
|
||||||
|
Failed due to an issue with our release process.
|
||||||
|
|
||||||
## v3.44.1 - 2025-07-23
|
## v3.44.1 - 2025-07-23
|
||||||
|
|
||||||
- Internal tasks will no longer be shown as suggestions since they cannot be
|
- Internal tasks will no longer be shown as suggestions since they cannot be
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<div align="center">
|
<div align="center">
|
||||||
<a href="https://taskfile.dev">
|
<a href="https://taskfile.dev">
|
||||||
<img src="website/static/img/logo.svg" width="200px" height="200px" />
|
<img src="website/src/public/img/logo.svg" width="200px" height="200px" />
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<h1>Task</h1>
|
<h1>Task</h1>
|
||||||
@@ -19,7 +19,12 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td align="center" valign="middle">
|
<td align="center" valign="middle">
|
||||||
<a target="_blank" href="https://devowl.io">
|
<a target="_blank" href="https://devowl.io">
|
||||||
<img src="/website/static/img/devowl.io.svg" height="100px" title="devowl.io" />
|
<img src="website/src/public/img/devowl.io.svg" height="100px" width="200px" title="devowl.io" />
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td align="center" valign="middle">
|
||||||
|
<a target="_blank" href="https://magic.dev/">
|
||||||
|
<img src="website/src/public/img/magic.png" height="100px" width="200px" title="Magic" />
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
25
Taskfile.yml
25
Taskfile.yml
@@ -8,6 +8,7 @@ includes:
|
|||||||
|
|
||||||
vars:
|
vars:
|
||||||
BIN: "{{.ROOT_DIR}}/bin"
|
BIN: "{{.ROOT_DIR}}/bin"
|
||||||
|
GOTESTSUM_FORMAT: '{{if .CI}}github-actions{{else}}pkgname{{end}}'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CGO_ENABLED: '0'
|
CGO_ENABLED: '0'
|
||||||
@@ -131,29 +132,37 @@ tasks:
|
|||||||
test:
|
test:
|
||||||
desc: Runs test suite
|
desc: Runs test suite
|
||||||
aliases: [t]
|
aliases: [t]
|
||||||
|
deps: [gotestsum:install]
|
||||||
sources:
|
sources:
|
||||||
- "**/*.go"
|
- "**/*.go"
|
||||||
- "testdata/**/*"
|
- "testdata/**/*"
|
||||||
cmds:
|
cmds:
|
||||||
- go test ./...
|
- gotestsum -f '{{.GOTESTSUM_FORMAT}}' ./...
|
||||||
|
|
||||||
test:watch:
|
test:watch:
|
||||||
desc: Runs test suite with watch tests included
|
desc: Runs test suite with watch tests included
|
||||||
deps: [sleepit:build]
|
deps: [sleepit:build, gotestsum:install]
|
||||||
cmds:
|
cmds:
|
||||||
- go test ./... -tags 'watch'
|
- gotestsum -f '{{.GOTESTSUM_FORMAT}}' ./... -tags 'watch'
|
||||||
|
|
||||||
test:all:
|
test:all:
|
||||||
desc: Runs test suite with signals and watch tests included
|
desc: Runs test suite with signals and watch tests included
|
||||||
deps: [sleepit:build]
|
deps: [sleepit:build, gotestsum:install]
|
||||||
cmds:
|
cmds:
|
||||||
- go test -tags 'signals watch' ./...
|
- gotestsum -f '{{.GOTESTSUM_FORMAT}}' -tags 'signals watch' ./...
|
||||||
|
|
||||||
goreleaser:test:
|
goreleaser:test:
|
||||||
desc: Tests release process without publishing
|
desc: Tests release process without publishing
|
||||||
cmds:
|
cmds:
|
||||||
- goreleaser --snapshot --clean
|
- goreleaser --snapshot --clean
|
||||||
|
|
||||||
|
gotestsum:install:
|
||||||
|
desc: Installs gotestsum
|
||||||
|
status:
|
||||||
|
- command -v gotestsum
|
||||||
|
cmds:
|
||||||
|
- go install gotest.tools/gotestsum@latest
|
||||||
|
|
||||||
goreleaser:install:
|
goreleaser:install:
|
||||||
desc: Installs goreleaser
|
desc: Installs goreleaser
|
||||||
cmds:
|
cmds:
|
||||||
@@ -203,7 +212,6 @@ tasks:
|
|||||||
Please wait for the CI to finish and then do the following:
|
Please wait for the CI to finish and then do the following:
|
||||||
|
|
||||||
- Copy the changelog for v{{.VERSION}} to the GitHub release
|
- Copy the changelog for v{{.VERSION}} to the GitHub release
|
||||||
- Publish the package to NPM with `task npm:publish`
|
|
||||||
- Update and push the snapcraft manifest in https://github.com/go-task/snap/blob/main/snap/snapcraft.yaml
|
- Update and push the snapcraft manifest in https://github.com/go-task/snap/blob/main/snap/snapcraft.yaml
|
||||||
preconditions:
|
preconditions:
|
||||||
- sh: test $(git rev-parse --abbrev-ref HEAD) = "main"
|
- sh: test $(git rev-parse --abbrev-ref HEAD) = "main"
|
||||||
@@ -222,8 +230,3 @@ tasks:
|
|||||||
- "git push origin tag v{{.VERSION}}"
|
- "git push origin tag v{{.VERSION}}"
|
||||||
- cmd: printf "%s" '{{.COMPLETE_MESSAGE}}'
|
- cmd: printf "%s" '{{.COMPLETE_MESSAGE}}'
|
||||||
silent: true
|
silent: true
|
||||||
|
|
||||||
npm:publish:
|
|
||||||
desc: Publish release to npm
|
|
||||||
cmds:
|
|
||||||
- npm publish --access=public
|
|
||||||
|
|||||||
@@ -3,33 +3,23 @@ package main
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Masterminds/semver/v3"
|
"github.com/Masterminds/semver/v3"
|
||||||
"github.com/otiai10/copy"
|
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/errors"
|
"github.com/go-task/task/v3/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
changelogSource = "CHANGELOG.md"
|
changelogSource = "CHANGELOG.md"
|
||||||
changelogTarget = "website/docs/changelog.mdx"
|
changelogTarget = "website/src/docs/changelog.md"
|
||||||
docsSource = "website/docs"
|
versionFile = "internal/version/version.txt"
|
||||||
docsTarget = "website/versioned_docs/version-latest"
|
|
||||||
schemaSource = "website/static/next-schema.json"
|
|
||||||
schemaTarget = "website/static/schema.json"
|
|
||||||
schemaTaskrcSource = "website/static/next-schema-taskrc.json"
|
|
||||||
schemaTaskrcTarget = "website/static/schema-taskrc.json"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var changelogReleaseRegex = regexp.MustCompile(`## Unreleased`)
|
||||||
changelogReleaseRegex = regexp.MustCompile(`## Unreleased`)
|
|
||||||
versionRegex = regexp.MustCompile(`(?m)^ "version": "\d+\.\d+\.\d+",$`)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Flags
|
// Flags
|
||||||
var (
|
var (
|
||||||
@@ -53,7 +43,7 @@ func release() error {
|
|||||||
return errors.New("error: expected version number")
|
return errors.New("error: expected version number")
|
||||||
}
|
}
|
||||||
|
|
||||||
version, err := getVersion()
|
version, err := getVersion(versionFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -71,36 +61,18 @@ func release() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := setVersionFile("internal/version/version.txt", version); err != nil {
|
if err := setVersionFile(versionFile, version); err != nil {
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := setJSONVersion("package.json", version); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := setJSONVersion("package-lock.json", version); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := docs(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := schema(); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getVersion() (*semver.Version, error) {
|
func getVersion(filename string) (*semver.Version, error) {
|
||||||
cmd := exec.Command("git", "describe", "--tags", "--abbrev=0")
|
b, err := os.ReadFile(filename)
|
||||||
b, err := cmd.Output()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return semver.NewVersion(strings.TrimSpace(string(b)))
|
return semver.NewVersion(strings.TrimSpace(string(b)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,37 +131,3 @@ func changelog(version *semver.Version) error {
|
|||||||
func setVersionFile(fileName string, version *semver.Version) error {
|
func setVersionFile(fileName string, version *semver.Version) error {
|
||||||
return os.WriteFile(fileName, []byte(version.String()+"\n"), 0o644)
|
return os.WriteFile(fileName, []byte(version.String()+"\n"), 0o644)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setJSONVersion(fileName string, version *semver.Version) error {
|
|
||||||
// Read the JSON file
|
|
||||||
b, err := os.ReadFile(fileName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace the version
|
|
||||||
new := versionRegex.ReplaceAllString(string(b), fmt.Sprintf(` "version": "%s",`, version.String()))
|
|
||||||
|
|
||||||
// Write the JSON file
|
|
||||||
return os.WriteFile(fileName, []byte(new), 0o644)
|
|
||||||
}
|
|
||||||
|
|
||||||
func docs() error {
|
|
||||||
if err := os.RemoveAll(docsTarget); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := copy.Copy(docsSource, docsTarget); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func schema() error {
|
|
||||||
if err := copy.Copy(schemaSource, schemaTarget); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := copy.Copy(schemaTaskrcSource, schemaTaskrcTarget); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import (
|
|||||||
"github.com/go-task/task/v3/internal/flags"
|
"github.com/go-task/task/v3/internal/flags"
|
||||||
"github.com/go-task/task/v3/internal/logger"
|
"github.com/go-task/task/v3/internal/logger"
|
||||||
"github.com/go-task/task/v3/internal/version"
|
"github.com/go-task/task/v3/internal/version"
|
||||||
"github.com/go-task/task/v3/taskfile"
|
|
||||||
"github.com/go-task/task/v3/taskfile/ast"
|
"github.com/go-task/task/v3/taskfile/ast"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -111,63 +110,16 @@ func run() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := experiments.Validate(); err != nil {
|
e := task.NewExecutor(
|
||||||
log.Warnf("%s\n", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new root node for the given entrypoint
|
|
||||||
node, err := taskfile.NewRootNode(
|
|
||||||
flags.Entrypoint,
|
|
||||||
flags.Dir,
|
|
||||||
flags.Insecure,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
tempDir, err := task.NewTempDir(node.Dir())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
reader := taskfile.NewReader(
|
|
||||||
flags.WithFlags(),
|
flags.WithFlags(),
|
||||||
taskfile.WithTempDir(tempDir.Remote),
|
task.WithVersionCheck(true),
|
||||||
taskfile.WithDebugFunc(func(s string) {
|
|
||||||
log.VerboseOutf(logger.Magenta, s)
|
|
||||||
}),
|
|
||||||
taskfile.WithPromptFunc(func(s string) error {
|
|
||||||
return log.Prompt(logger.Yellow, s, "n", "y", "yes")
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
|
if err := e.Setup(); err != nil {
|
||||||
ctx, cf := context.WithTimeout(context.Background(), flags.Timeout)
|
|
||||||
defer cf()
|
|
||||||
graph, err := reader.Read(ctx, node)
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, context.DeadlineExceeded) {
|
|
||||||
return &errors.TaskfileNetworkTimeoutError{URI: node.Location(), Timeout: flags.Timeout}
|
|
||||||
}
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
executor, err := task.NewExecutor(graph,
|
|
||||||
flags.WithFlags(),
|
|
||||||
task.WithDir(node.Dir()),
|
|
||||||
task.WithTempDir(tempDir),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the download flag is specified, we should stop execution as soon as
|
|
||||||
// taskfile is downloaded
|
|
||||||
if flags.Download {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if flags.ClearCache {
|
if flags.ClearCache {
|
||||||
cachePath := filepath.Join(executor.TempDir.Remote, "remote")
|
cachePath := filepath.Join(e.TempDir.Remote, "remote")
|
||||||
return os.RemoveAll(cachePath)
|
return os.RemoveAll(cachePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,12 +128,13 @@ func run() error {
|
|||||||
flags.ListAll,
|
flags.ListAll,
|
||||||
flags.ListJson,
|
flags.ListJson,
|
||||||
flags.NoStatus,
|
flags.NoStatus,
|
||||||
|
flags.Nested,
|
||||||
)
|
)
|
||||||
if listOptions.ShouldListTasks() {
|
if listOptions.ShouldListTasks() {
|
||||||
if flags.Silent {
|
if flags.Silent {
|
||||||
return executor.ListTaskNames(flags.ListAll)
|
return e.ListTaskNames(flags.ListAll)
|
||||||
}
|
}
|
||||||
foundTasks, err := executor.ListTasks(listOptions)
|
foundTasks, err := e.ListTasks(listOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -213,17 +166,17 @@ func run() error {
|
|||||||
globals.Set("CLI_SILENT", ast.Var{Value: flags.Silent})
|
globals.Set("CLI_SILENT", ast.Var{Value: flags.Silent})
|
||||||
globals.Set("CLI_VERBOSE", ast.Var{Value: flags.Verbose})
|
globals.Set("CLI_VERBOSE", ast.Var{Value: flags.Verbose})
|
||||||
globals.Set("CLI_OFFLINE", ast.Var{Value: flags.Offline})
|
globals.Set("CLI_OFFLINE", ast.Var{Value: flags.Offline})
|
||||||
executor.Taskfile.Vars.Merge(globals, nil)
|
e.Taskfile.Vars.Merge(globals, nil)
|
||||||
|
|
||||||
if !flags.Watch {
|
if !flags.Watch {
|
||||||
executor.InterceptInterruptSignals()
|
e.InterceptInterruptSignals()
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx = context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
if flags.Status {
|
if flags.Status {
|
||||||
return executor.Status(ctx, calls...)
|
return e.Status(ctx, calls...)
|
||||||
}
|
}
|
||||||
|
|
||||||
return executor.Run(ctx, calls...)
|
return e.Run(ctx, calls...)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond)
|
|
||||||
defer cancel()
|
|
||||||
if err := run(ctx); err != nil {
|
|
||||||
fmt.Println(ctx.Err())
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func run(ctx context.Context) error {
|
|
||||||
req, err := http.NewRequest("GET", "https://taskfile.dev/schema.json", nil)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(1)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := http.DefaultClient.Do(req.WithContext(ctx))
|
|
||||||
if err != nil {
|
|
||||||
if ctx.Err() != nil {
|
|
||||||
fmt.Println(2)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Println(3)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -16,21 +16,18 @@ function __task_list() {
|
|||||||
|
|
||||||
|
|
||||||
if [[ -n "$taskfile" && -f "$taskfile" ]]; then
|
if [[ -n "$taskfile" && -f "$taskfile" ]]; then
|
||||||
enabled=1
|
|
||||||
cmd+=(--taskfile "$taskfile")
|
cmd+=(--taskfile "$taskfile")
|
||||||
else
|
fi
|
||||||
for taskfile in {T,t}askfile{,.dist}.{yaml,yml}; do
|
|
||||||
if [[ -f "$taskfile" ]]; then
|
|
||||||
enabled=1
|
if output=$("${cmd[@]}" $_GO_TASK_COMPLETION_LIST_OPTION 2>/dev/null); then
|
||||||
break
|
enabled=1
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
(( enabled )) || return 0
|
(( enabled )) || return 0
|
||||||
|
|
||||||
scripts=()
|
scripts=()
|
||||||
for item in "${(@)${(f)$("${cmd[@]}" $_GO_TASK_COMPLETION_LIST_OPTION)}[2,-1]#\* }"; do
|
for item in "${(@)${(f)output}[2,-1]#\* }"; do
|
||||||
task="${item%%:[[:space:]]*}"
|
task="${item%%:[[:space:]]*}"
|
||||||
desc="${item##[^[:space:]]##[[:space:]]##}"
|
desc="${item##[^[:space:]]##[[:space:]]##}"
|
||||||
scripts+=( "${task//:/\\:}:$desc" )
|
scripts+=( "${task//:/\\:}:$desc" )
|
||||||
|
|||||||
@@ -5,15 +5,12 @@ import (
|
|||||||
"cmp"
|
"cmp"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"gopkg.in/yaml.v3"
|
"go.yaml.in/yaml/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
var typeErrorRegex = regexp.MustCompile(`line \d+: (.*)`)
|
|
||||||
|
|
||||||
type (
|
type (
|
||||||
TaskfileDecodeError struct {
|
TaskfileDecodeError struct {
|
||||||
Message string
|
Message string
|
||||||
@@ -53,10 +50,10 @@ func (err *TaskfileDecodeError) Error() string {
|
|||||||
if len(te.Errors) > 1 {
|
if len(te.Errors) > 1 {
|
||||||
fmt.Fprintln(buf, color.RedString("errs:"))
|
fmt.Fprintln(buf, color.RedString("errs:"))
|
||||||
for _, message := range te.Errors {
|
for _, message := range te.Errors {
|
||||||
fmt.Fprintln(buf, color.RedString("- %s", extractTypeErrorMessage(message)))
|
fmt.Fprintln(buf, color.RedString("- %s", message.Err.Error()))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintln(buf, color.RedString("err: %s", extractTypeErrorMessage(te.Errors[0])))
|
fmt.Fprintln(buf, color.RedString("err: %s", te.Errors[0].Err.Error()))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Otherwise print the error message normally
|
// Otherwise print the error message normally
|
||||||
@@ -128,11 +125,3 @@ func (err *TaskfileDecodeError) WithFileInfo(location string, snippet string) *T
|
|||||||
err.Snippet = snippet
|
err.Snippet = snippet
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractTypeErrorMessage(message string) string {
|
|
||||||
matches := typeErrorRegex.FindStringSubmatch(message)
|
|
||||||
if len(matches) == 2 {
|
|
||||||
return matches[1]
|
|
||||||
}
|
|
||||||
return message
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -54,6 +54,10 @@ func (err *TaskRunError) TaskExitCode() int {
|
|||||||
return err.Code()
|
return err.Code()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (err *TaskRunError) Unwrap() error {
|
||||||
|
return err.Err
|
||||||
|
}
|
||||||
|
|
||||||
// TaskInternalError when the user attempts to invoke a task that is internal.
|
// TaskInternalError when the user attempts to invoke a task that is internal.
|
||||||
type TaskInternalError struct {
|
type TaskInternalError struct {
|
||||||
TaskName string
|
TaskName string
|
||||||
@@ -162,7 +166,7 @@ func (v MissingVar) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (err *TaskMissingRequiredVarsError) Error() string {
|
func (err *TaskMissingRequiredVarsError) Error() string {
|
||||||
var vars []string
|
vars := make([]string, 0, len(err.MissingVars))
|
||||||
for _, v := range err.MissingVars {
|
for _, v := range err.MissingVars {
|
||||||
vars = append(vars, v.String())
|
vars = append(vars, v.String())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,14 +11,18 @@ import (
|
|||||||
// TaskfileNotFoundError is returned when no appropriate Taskfile is found when
|
// TaskfileNotFoundError is returned when no appropriate Taskfile is found when
|
||||||
// searching the filesystem.
|
// searching the filesystem.
|
||||||
type TaskfileNotFoundError struct {
|
type TaskfileNotFoundError struct {
|
||||||
URI string
|
URI string
|
||||||
Walk bool
|
Walk bool
|
||||||
|
AskInit bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (err TaskfileNotFoundError) Error() string {
|
func (err TaskfileNotFoundError) Error() string {
|
||||||
var walkText string
|
var walkText string
|
||||||
if err.Walk {
|
if err.Walk {
|
||||||
walkText = " (or any of the parent directories)"
|
walkText = " (or any of the parent directories)."
|
||||||
|
}
|
||||||
|
if err.AskInit {
|
||||||
|
walkText += " Run `task --init` to create a new Taskfile."
|
||||||
}
|
}
|
||||||
return fmt.Sprintf(`task: No Taskfile found at %q%s`, err.URI, walkText)
|
return fmt.Sprintf(`task: No Taskfile found at %q%s`, err.URI, walkText)
|
||||||
}
|
}
|
||||||
|
|||||||
150
executor.go
150
executor.go
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -27,21 +26,27 @@ type (
|
|||||||
// within them.
|
// within them.
|
||||||
Executor struct {
|
Executor struct {
|
||||||
// Flags
|
// Flags
|
||||||
Dir string
|
Dir string
|
||||||
TempDir *TempDir
|
Entrypoint string
|
||||||
Force bool
|
TempDir TempDir
|
||||||
ForceAll bool
|
Force bool
|
||||||
Watch bool
|
ForceAll bool
|
||||||
Verbose bool
|
Insecure bool
|
||||||
Silent bool
|
Download bool
|
||||||
AssumeYes bool
|
Offline bool
|
||||||
AssumeTerm bool // Used for testing
|
Timeout time.Duration
|
||||||
Dry bool
|
CacheExpiryDuration time.Duration
|
||||||
Summary bool
|
Watch bool
|
||||||
Parallel bool
|
Verbose bool
|
||||||
Color bool
|
Silent bool
|
||||||
Concurrency int
|
AssumeYes bool
|
||||||
Interval time.Duration
|
AssumeTerm bool // Used for testing
|
||||||
|
Dry bool
|
||||||
|
Summary bool
|
||||||
|
Parallel bool
|
||||||
|
Color bool
|
||||||
|
Concurrency int
|
||||||
|
Interval time.Duration
|
||||||
|
|
||||||
// I/O
|
// I/O
|
||||||
Stdin io.Reader
|
Stdin io.Reader
|
||||||
@@ -67,17 +72,17 @@ type (
|
|||||||
executionHashesMutex sync.Mutex
|
executionHashesMutex sync.Mutex
|
||||||
watchedDirs *xsync.MapOf[string, bool]
|
watchedDirs *xsync.MapOf[string, bool]
|
||||||
}
|
}
|
||||||
|
TempDir struct {
|
||||||
|
Remote string
|
||||||
|
Fingerprint string
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewExecutor creates a new [Executor] and applies the given functional options
|
// NewExecutor creates a new [Executor] and applies the given functional options
|
||||||
// to it.
|
// to it.
|
||||||
func NewExecutor(graph *ast.TaskfileGraph, opts ...ExecutorOption) (*Executor, error) {
|
func NewExecutor(opts ...ExecutorOption) *Executor {
|
||||||
tf, err := graph.Merge()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
e := &Executor{
|
e := &Executor{
|
||||||
Taskfile: tf,
|
Timeout: time.Second * 10,
|
||||||
Stdin: os.Stdin,
|
Stdin: os.Stdin,
|
||||||
Stdout: os.Stdout,
|
Stdout: os.Stdout,
|
||||||
Stderr: os.Stderr,
|
Stderr: os.Stderr,
|
||||||
@@ -95,10 +100,7 @@ func NewExecutor(graph *ast.TaskfileGraph, opts ...ExecutorOption) (*Executor, e
|
|||||||
executionHashesMutex: sync.Mutex{},
|
executionHashesMutex: sync.Mutex{},
|
||||||
}
|
}
|
||||||
e.Options(opts...)
|
e.Options(opts...)
|
||||||
if err := e.setup(); err != nil {
|
return e
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return e, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Options loops through the given [ExecutorOption] functions and applies them
|
// Options loops through the given [ExecutorOption] functions and applies them
|
||||||
@@ -120,23 +122,33 @@ type dirOption struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (o *dirOption) ApplyToExecutor(e *Executor) {
|
func (o *dirOption) ApplyToExecutor(e *Executor) {
|
||||||
absDir, err := filepath.Abs(o.dir)
|
e.Dir = o.dir
|
||||||
if err != nil {
|
}
|
||||||
e.Dir = o.dir
|
|
||||||
return
|
// WithEntrypoint sets the entrypoint (main Taskfile) of the [Executor]. By
|
||||||
}
|
// default, Task will search for one of the default Taskfiles in the given
|
||||||
e.Dir = absDir
|
// directory.
|
||||||
|
func WithEntrypoint(entrypoint string) ExecutorOption {
|
||||||
|
return &entrypointOption{entrypoint}
|
||||||
|
}
|
||||||
|
|
||||||
|
type entrypointOption struct {
|
||||||
|
entrypoint string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *entrypointOption) ApplyToExecutor(e *Executor) {
|
||||||
|
e.Entrypoint = o.entrypoint
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithTempDir sets the temporary directory that will be used by [Executor] for
|
// WithTempDir sets the temporary directory that will be used by [Executor] for
|
||||||
// storing temporary files like checksums and cached remote files. By default,
|
// storing temporary files like checksums and cached remote files. By default,
|
||||||
// the temporary directory is set to the user's temporary directory.
|
// the temporary directory is set to the user's temporary directory.
|
||||||
func WithTempDir(tempDir *TempDir) ExecutorOption {
|
func WithTempDir(tempDir TempDir) ExecutorOption {
|
||||||
return &tempDirOption{tempDir}
|
return &tempDirOption{tempDir}
|
||||||
}
|
}
|
||||||
|
|
||||||
type tempDirOption struct {
|
type tempDirOption struct {
|
||||||
tempDir *TempDir
|
tempDir TempDir
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *tempDirOption) ApplyToExecutor(e *Executor) {
|
func (o *tempDirOption) ApplyToExecutor(e *Executor) {
|
||||||
@@ -171,6 +183,76 @@ func (o *forceAllOption) ApplyToExecutor(e *Executor) {
|
|||||||
e.ForceAll = o.forceAll
|
e.ForceAll = o.forceAll
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithInsecure allows the [Executor] to make insecure connections when reading
|
||||||
|
// remote taskfiles. By default, insecure connections are rejected.
|
||||||
|
func WithInsecure(insecure bool) ExecutorOption {
|
||||||
|
return &insecureOption{insecure}
|
||||||
|
}
|
||||||
|
|
||||||
|
type insecureOption struct {
|
||||||
|
insecure bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *insecureOption) ApplyToExecutor(e *Executor) {
|
||||||
|
e.Insecure = o.insecure
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithDownload forces the [Executor] to download a fresh copy of the taskfile
|
||||||
|
// from the remote source.
|
||||||
|
func WithDownload(download bool) ExecutorOption {
|
||||||
|
return &downloadOption{download}
|
||||||
|
}
|
||||||
|
|
||||||
|
type downloadOption struct {
|
||||||
|
download bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *downloadOption) ApplyToExecutor(e *Executor) {
|
||||||
|
e.Download = o.download
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithOffline stops the [Executor] from being able to make network connections.
|
||||||
|
// It will still be able to read local files and cached copies of remote files.
|
||||||
|
func WithOffline(offline bool) ExecutorOption {
|
||||||
|
return &offlineOption{offline}
|
||||||
|
}
|
||||||
|
|
||||||
|
type offlineOption struct {
|
||||||
|
offline bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *offlineOption) ApplyToExecutor(e *Executor) {
|
||||||
|
e.Offline = o.offline
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithTimeout sets the [Executor]'s timeout for fetching remote taskfiles. By
|
||||||
|
// default, the timeout is set to 10 seconds.
|
||||||
|
func WithTimeout(timeout time.Duration) ExecutorOption {
|
||||||
|
return &timeoutOption{timeout}
|
||||||
|
}
|
||||||
|
|
||||||
|
type timeoutOption struct {
|
||||||
|
timeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *timeoutOption) ApplyToExecutor(e *Executor) {
|
||||||
|
e.Timeout = o.timeout
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithCacheExpiryDuration sets the duration after which the cache is considered
|
||||||
|
// expired. By default, the cache is 0 (disabled).
|
||||||
|
func WithCacheExpiryDuration(duration time.Duration) ExecutorOption {
|
||||||
|
return &cacheExpiryDurationOption{duration: duration}
|
||||||
|
}
|
||||||
|
|
||||||
|
type cacheExpiryDurationOption struct {
|
||||||
|
duration time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *cacheExpiryDurationOption) ApplyToExecutor(r *Executor) {
|
||||||
|
r.CacheExpiryDuration = o.duration
|
||||||
|
}
|
||||||
|
|
||||||
// WithWatch tells the [Executor] to keep running in the background and watch
|
// WithWatch tells the [Executor] to keep running in the background and watch
|
||||||
// for changes to the fingerprint of the tasks that are run. When changes are
|
// for changes to the fingerprint of the tasks that are run. When changes are
|
||||||
// detected, a new task run is triggered.
|
// detected, a new task run is triggered.
|
||||||
|
|||||||
331
executor_test.go
331
executor_test.go
@@ -3,11 +3,9 @@ package task_test
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"cmp"
|
"cmp"
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"slices"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/sebdah/goldie/v2"
|
"github.com/sebdah/goldie/v2"
|
||||||
@@ -16,7 +14,6 @@ import (
|
|||||||
"github.com/go-task/task/v3"
|
"github.com/go-task/task/v3"
|
||||||
"github.com/go-task/task/v3/experiments"
|
"github.com/go-task/task/v3/experiments"
|
||||||
"github.com/go-task/task/v3/internal/filepathext"
|
"github.com/go-task/task/v3/internal/filepathext"
|
||||||
"github.com/go-task/task/v3/taskfile"
|
|
||||||
"github.com/go-task/task/v3/taskfile/ast"
|
"github.com/go-task/task/v3/taskfile/ast"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -36,12 +33,7 @@ type (
|
|||||||
task string
|
task string
|
||||||
vars map[string]any
|
vars map[string]any
|
||||||
input string
|
input string
|
||||||
nodeDir string
|
|
||||||
nodeEntrypoint string
|
|
||||||
nodeInsecure bool
|
|
||||||
readerOpts []taskfile.ReaderOption
|
|
||||||
executorOpts []task.ExecutorOption
|
executorOpts []task.ExecutorOption
|
||||||
wantReaderError bool
|
|
||||||
wantSetupError bool
|
wantSetupError bool
|
||||||
wantRunError bool
|
wantRunError bool
|
||||||
wantStatusError bool
|
wantStatusError bool
|
||||||
@@ -54,9 +46,8 @@ type (
|
|||||||
func NewExecutorTest(t *testing.T, opts ...ExecutorTestOption) {
|
func NewExecutorTest(t *testing.T, opts ...ExecutorTestOption) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
tt := &ExecutorTest{
|
tt := &ExecutorTest{
|
||||||
task: "default",
|
task: "default",
|
||||||
vars: map[string]any{},
|
vars: map[string]any{},
|
||||||
nodeDir: ".",
|
|
||||||
TaskTest: TaskTest{
|
TaskTest: TaskTest{
|
||||||
experiments: map[*experiments.Experiment]int{},
|
experiments: map[*experiments.Experiment]int{},
|
||||||
fixtureTemplateData: map[string]any{},
|
fixtureTemplateData: map[string]any{},
|
||||||
@@ -152,53 +143,12 @@ func (tt *ExecutorTest) run(t *testing.T) {
|
|||||||
t.Helper()
|
t.Helper()
|
||||||
f := func(t *testing.T) {
|
f := func(t *testing.T) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
var buf bytes.Buffer
|
var buffer SyncBuffer
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
// Create a new root node for the given entrypoint
|
opts := append(
|
||||||
node, err := taskfile.NewRootNode(
|
|
||||||
tt.nodeEntrypoint,
|
|
||||||
tt.nodeDir,
|
|
||||||
tt.nodeInsecure,
|
|
||||||
)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// Create a golden fixture file for the output
|
|
||||||
g := goldie.New(t,
|
|
||||||
goldie.WithFixtureDir(filepath.Join(node.Dir(), "testdata")),
|
|
||||||
)
|
|
||||||
|
|
||||||
// Set up a temporary directory for the taskfile reader and task executor
|
|
||||||
tempDir, err := task.NewTempDir(node.Dir())
|
|
||||||
require.NoError(t, err)
|
|
||||||
tt.readerOpts = append(tt.readerOpts, taskfile.WithTempDir(tempDir.Remote))
|
|
||||||
|
|
||||||
// Set up the taskfile reader
|
|
||||||
reader := taskfile.NewReader(tt.readerOpts...)
|
|
||||||
graph, err := reader.Read(ctx, node)
|
|
||||||
if tt.wantReaderError {
|
|
||||||
require.Error(t, err)
|
|
||||||
tt.writeFixtureErrReader(t, g, err)
|
|
||||||
tt.writeFixtureBuffer(t, g, buf)
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
executorOpts := slices.Concat(
|
|
||||||
// Apply the node directory and temp directory to the executor options
|
|
||||||
// by default, but allow them to by overridden by the test options
|
|
||||||
[]task.ExecutorOption{
|
|
||||||
task.WithDir(node.Dir()),
|
|
||||||
task.WithTempDir(tempDir),
|
|
||||||
},
|
|
||||||
// Apply the executor options from the test
|
|
||||||
tt.executorOpts,
|
tt.executorOpts,
|
||||||
// Force the input/output streams to be set to the test buffer
|
task.WithStdout(&buffer),
|
||||||
[]task.ExecutorOption{
|
task.WithStderr(&buffer),
|
||||||
task.WithStdout(&buf),
|
|
||||||
task.WithStderr(&buf),
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// If the test has input, create a reader for it and add it to the
|
// If the test has input, create a reader for it and add it to the
|
||||||
@@ -206,15 +156,22 @@ func (tt *ExecutorTest) run(t *testing.T) {
|
|||||||
if tt.input != "" {
|
if tt.input != "" {
|
||||||
var reader bytes.Buffer
|
var reader bytes.Buffer
|
||||||
reader.WriteString(tt.input)
|
reader.WriteString(tt.input)
|
||||||
executorOpts = append(executorOpts, task.WithStdin(&reader))
|
opts = append(opts, task.WithStdin(&reader))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up the task executor
|
// Set up the task executor
|
||||||
executor, err := task.NewExecutor(graph, executorOpts...)
|
e := task.NewExecutor(opts...)
|
||||||
if tt.wantSetupError {
|
|
||||||
|
// Create a golden fixture file for the output
|
||||||
|
g := goldie.New(t,
|
||||||
|
goldie.WithFixtureDir(filepath.Join(e.Dir, "testdata")),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Call setup and check for errors
|
||||||
|
if err := e.Setup(); tt.wantSetupError {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
tt.writeFixtureErrSetup(t, g, err)
|
tt.writeFixtureErrSetup(t, g, err)
|
||||||
tt.writeFixtureBuffer(t, g, buf)
|
tt.writeFixtureBuffer(t, g, buffer.buf)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -231,10 +188,11 @@ func (tt *ExecutorTest) run(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Run the task and check for errors
|
// Run the task and check for errors
|
||||||
if err := executor.Run(ctx, call); tt.wantRunError {
|
ctx := t.Context()
|
||||||
|
if err := e.Run(ctx, call); tt.wantRunError {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
tt.writeFixtureErrRun(t, g, err)
|
tt.writeFixtureErrRun(t, g, err)
|
||||||
tt.writeFixtureBuffer(t, g, buf)
|
tt.writeFixtureBuffer(t, g, buffer.buf)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -242,12 +200,12 @@ func (tt *ExecutorTest) run(t *testing.T) {
|
|||||||
|
|
||||||
// If the status flag is set, run the status check
|
// If the status flag is set, run the status check
|
||||||
if tt.wantStatusError {
|
if tt.wantStatusError {
|
||||||
if err := executor.Status(ctx, call); err != nil {
|
if err := e.Status(ctx, call); err != nil {
|
||||||
tt.writeFixtureStatus(t, g, err.Error())
|
tt.writeFixtureStatus(t, g, err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tt.writeFixtureBuffer(t, g, buf)
|
tt.writeFixtureBuffer(t, g, buffer.buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the test (with a name if it has one)
|
// Run the test (with a name if it has one)
|
||||||
@@ -261,16 +219,19 @@ func (tt *ExecutorTest) run(t *testing.T) {
|
|||||||
func TestEmptyTask(t *testing.T) {
|
func TestEmptyTask(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithNodeDir("testdata/empty_task"),
|
WithExecutorOptions(
|
||||||
WithExecutorOptions(),
|
task.WithDir("testdata/empty_task"),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEmptyTaskfile(t *testing.T) {
|
func TestEmptyTaskfile(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithNodeDir("testdata/empty_taskfile"),
|
WithExecutorOptions(
|
||||||
WithReaderError(),
|
task.WithDir("testdata/empty_taskfile"),
|
||||||
|
),
|
||||||
|
WithSetupError(),
|
||||||
WithFixtureTemplating(),
|
WithFixtureTemplating(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -279,15 +240,15 @@ func TestEnv(t *testing.T) {
|
|||||||
t.Setenv("QUX", "from_os")
|
t.Setenv("QUX", "from_os")
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("env precedence disabled"),
|
WithName("env precedence disabled"),
|
||||||
WithNodeDir("testdata/env"),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/env"),
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("env precedence enabled"),
|
WithName("env precedence enabled"),
|
||||||
WithNodeDir("testdata/env"),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/env"),
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
),
|
),
|
||||||
WithExperiment(&experiments.EnvPrecedence, 1),
|
WithExperiment(&experiments.EnvPrecedence, 1),
|
||||||
@@ -297,8 +258,8 @@ func TestEnv(t *testing.T) {
|
|||||||
func TestVars(t *testing.T) {
|
func TestVars(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithNodeDir("testdata/vars"),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/vars"),
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -308,19 +269,25 @@ func TestRequires(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("required var missing"),
|
WithName("required var missing"),
|
||||||
WithNodeDir("testdata/requires"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/requires"),
|
||||||
|
),
|
||||||
WithTask("missing-var"),
|
WithTask("missing-var"),
|
||||||
WithRunError(),
|
WithRunError(),
|
||||||
)
|
)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("required var ok"),
|
WithName("required var ok"),
|
||||||
WithNodeDir("testdata/requires"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/requires"),
|
||||||
|
),
|
||||||
WithTask("missing-var"),
|
WithTask("missing-var"),
|
||||||
WithVar("FOO", "bar"),
|
WithVar("FOO", "bar"),
|
||||||
)
|
)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("fails validation"),
|
WithName("fails validation"),
|
||||||
WithNodeDir("testdata/requires"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/requires"),
|
||||||
|
),
|
||||||
WithTask("validation-var"),
|
WithTask("validation-var"),
|
||||||
WithVar("ENV", "dev"),
|
WithVar("ENV", "dev"),
|
||||||
WithVar("FOO", "bar"),
|
WithVar("FOO", "bar"),
|
||||||
@@ -328,37 +295,48 @@ func TestRequires(t *testing.T) {
|
|||||||
)
|
)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("passes validation"),
|
WithName("passes validation"),
|
||||||
WithNodeDir("testdata/requires"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/requires"),
|
||||||
|
),
|
||||||
WithTask("validation-var"),
|
WithTask("validation-var"),
|
||||||
WithVar("FOO", "one"),
|
WithVar("FOO", "one"),
|
||||||
WithVar("ENV", "dev"),
|
WithVar("ENV", "dev"),
|
||||||
)
|
)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("required var missing + fails validation"),
|
WithName("required var missing + fails validation"),
|
||||||
WithNodeDir("testdata/requires"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/requires"),
|
||||||
|
),
|
||||||
WithTask("validation-var"),
|
WithTask("validation-var"),
|
||||||
WithRunError(),
|
WithRunError(),
|
||||||
)
|
)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("required var missing + fails validation"),
|
WithName("required var missing + fails validation"),
|
||||||
WithNodeDir("testdata/requires"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/requires"),
|
||||||
|
),
|
||||||
WithTask("validation-var-dynamic"),
|
WithTask("validation-var-dynamic"),
|
||||||
WithVar("FOO", "one"),
|
WithVar("FOO", "one"),
|
||||||
WithVar("ENV", "dev"),
|
WithVar("ENV", "dev"),
|
||||||
)
|
)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("require before compile"),
|
WithName("require before compile"),
|
||||||
WithNodeDir("testdata/requires"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/requires"),
|
||||||
|
),
|
||||||
WithTask("require-before-compile"),
|
WithTask("require-before-compile"),
|
||||||
WithRunError(),
|
WithRunError(),
|
||||||
)
|
)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("var defined in task"),
|
WithName("var defined in task"),
|
||||||
WithNodeDir("testdata/requires"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/requires"),
|
||||||
|
),
|
||||||
WithTask("var-defined-in-task"),
|
WithTask("var-defined-in-task"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: mock fs
|
||||||
func TestSpecialVars(t *testing.T) {
|
func TestSpecialVars(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -379,13 +357,12 @@ func TestSpecialVars(t *testing.T) {
|
|||||||
"included:print-taskfile-dir",
|
"included:print-taskfile-dir",
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, executorDir := range []string{dir, subdir} {
|
for _, dir := range []string{dir, subdir} {
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
name := fmt.Sprintf("%s-%s", executorDir, test)
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName(name),
|
WithName(fmt.Sprintf("%s-%s", dir, test)),
|
||||||
WithNodeDir(executorDir),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir(dir),
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
task.WithVersionCheck(true),
|
task.WithVersionCheck(true),
|
||||||
),
|
),
|
||||||
@@ -399,8 +376,8 @@ func TestSpecialVars(t *testing.T) {
|
|||||||
func TestConcurrency(t *testing.T) {
|
func TestConcurrency(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithNodeDir("testdata/concurrency"),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/concurrency"),
|
||||||
task.WithConcurrency(1),
|
task.WithConcurrency(1),
|
||||||
),
|
),
|
||||||
WithPostProcessFn(PPSortedLines),
|
WithPostProcessFn(PPSortedLines),
|
||||||
@@ -410,8 +387,8 @@ func TestConcurrency(t *testing.T) {
|
|||||||
func TestParams(t *testing.T) {
|
func TestParams(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithNodeDir("testdata/params"),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/params"),
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
),
|
),
|
||||||
WithPostProcessFn(PPSortedLines),
|
WithPostProcessFn(PPSortedLines),
|
||||||
@@ -421,14 +398,15 @@ func TestParams(t *testing.T) {
|
|||||||
func TestDeps(t *testing.T) {
|
func TestDeps(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithNodeDir("testdata/deps"),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/deps"),
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
),
|
),
|
||||||
WithPostProcessFn(PPSortedLines),
|
WithPostProcessFn(PPSortedLines),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: mock fs
|
||||||
func TestStatus(t *testing.T) {
|
func TestStatus(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -451,8 +429,8 @@ func TestStatus(t *testing.T) {
|
|||||||
// gen-foo creates foo.txt, and will always fail it's status check.
|
// gen-foo creates foo.txt, and will always fail it's status check.
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-foo 1 silent"),
|
WithName("run gen-foo 1 silent"),
|
||||||
WithNodeDir(dir),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir(dir),
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
),
|
),
|
||||||
WithTask("gen-foo"),
|
WithTask("gen-foo"),
|
||||||
@@ -463,8 +441,8 @@ func TestStatus(t *testing.T) {
|
|||||||
// only exists after the first run.
|
// only exists after the first run.
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-bar 1 silent"),
|
WithName("run gen-bar 1 silent"),
|
||||||
WithNodeDir(dir),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir(dir),
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
),
|
),
|
||||||
WithTask("gen-bar"),
|
WithTask("gen-bar"),
|
||||||
@@ -473,8 +451,8 @@ func TestStatus(t *testing.T) {
|
|||||||
// if e.Verbose is set to true.
|
// if e.Verbose is set to true.
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-baz silent"),
|
WithName("run gen-baz silent"),
|
||||||
WithNodeDir(dir),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir(dir),
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
),
|
),
|
||||||
WithTask("gen-silent-baz"),
|
WithTask("gen-silent-baz"),
|
||||||
@@ -489,8 +467,8 @@ func TestStatus(t *testing.T) {
|
|||||||
// Run gen-bar a second time to produce a checksum file that matches bar.txt
|
// Run gen-bar a second time to produce a checksum file that matches bar.txt
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-bar 2 silent"),
|
WithName("run gen-bar 2 silent"),
|
||||||
WithNodeDir(dir),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir(dir),
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
),
|
),
|
||||||
WithTask("gen-bar"),
|
WithTask("gen-bar"),
|
||||||
@@ -498,8 +476,8 @@ func TestStatus(t *testing.T) {
|
|||||||
// Run gen-bar a third time, to make sure we've triggered the status check.
|
// Run gen-bar a third time, to make sure we've triggered the status check.
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-bar 3 silent"),
|
WithName("run gen-bar 3 silent"),
|
||||||
WithNodeDir(dir),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir(dir),
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
),
|
),
|
||||||
WithTask("gen-bar"),
|
WithTask("gen-bar"),
|
||||||
@@ -511,8 +489,8 @@ func TestStatus(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-bar 4 silent"),
|
WithName("run gen-bar 4 silent"),
|
||||||
WithNodeDir(dir),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir(dir),
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
),
|
),
|
||||||
WithTask("gen-bar"),
|
WithTask("gen-bar"),
|
||||||
@@ -520,44 +498,56 @@ func TestStatus(t *testing.T) {
|
|||||||
// all: not up-to-date
|
// all: not up-to-date
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-foo 2"),
|
WithName("run gen-foo 2"),
|
||||||
WithNodeDir(dir),
|
WithExecutorOptions(
|
||||||
|
task.WithDir(dir),
|
||||||
|
),
|
||||||
WithTask("gen-foo"),
|
WithTask("gen-foo"),
|
||||||
)
|
)
|
||||||
// status: not up-to-date
|
// status: not up-to-date
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-foo 3"),
|
WithName("run gen-foo 3"),
|
||||||
WithNodeDir(dir),
|
WithExecutorOptions(
|
||||||
|
task.WithDir(dir),
|
||||||
|
),
|
||||||
WithTask("gen-foo"),
|
WithTask("gen-foo"),
|
||||||
)
|
)
|
||||||
// sources: not up-to-date
|
// sources: not up-to-date
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-bar 5"),
|
WithName("run gen-bar 5"),
|
||||||
WithNodeDir(dir),
|
WithExecutorOptions(
|
||||||
|
task.WithDir(dir),
|
||||||
|
),
|
||||||
WithTask("gen-bar"),
|
WithTask("gen-bar"),
|
||||||
)
|
)
|
||||||
// all: up-to-date
|
// all: up-to-date
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-bar 6"),
|
WithName("run gen-bar 6"),
|
||||||
WithNodeDir(dir),
|
WithExecutorOptions(
|
||||||
|
task.WithDir(dir),
|
||||||
|
),
|
||||||
WithTask("gen-bar"),
|
WithTask("gen-bar"),
|
||||||
)
|
)
|
||||||
// sources: not up-to-date, no output produced.
|
// sources: not up-to-date, no output produced.
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-baz 2"),
|
WithName("run gen-baz 2"),
|
||||||
WithNodeDir(dir),
|
WithExecutorOptions(
|
||||||
|
task.WithDir(dir),
|
||||||
|
),
|
||||||
WithTask("gen-silent-baz"),
|
WithTask("gen-silent-baz"),
|
||||||
)
|
)
|
||||||
// up-to-date, no output produced
|
// up-to-date, no output produced
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-baz 3"),
|
WithName("run gen-baz 3"),
|
||||||
WithNodeDir(dir),
|
WithExecutorOptions(
|
||||||
|
task.WithDir(dir),
|
||||||
|
),
|
||||||
WithTask("gen-silent-baz"),
|
WithTask("gen-silent-baz"),
|
||||||
)
|
)
|
||||||
// up-to-date, output produced due to Verbose mode.
|
// up-to-date, output produced due to Verbose mode.
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("run gen-baz 4 verbose"),
|
WithName("run gen-baz 4 verbose"),
|
||||||
WithNodeDir(dir),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir(dir),
|
||||||
task.WithVerbose(true),
|
task.WithVerbose(true),
|
||||||
),
|
),
|
||||||
WithTask("gen-silent-baz"),
|
WithTask("gen-silent-baz"),
|
||||||
@@ -570,24 +560,32 @@ func TestPrecondition(t *testing.T) {
|
|||||||
const dir = "testdata/precondition"
|
const dir = "testdata/precondition"
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("a precondition has been met"),
|
WithName("a precondition has been met"),
|
||||||
WithNodeDir(dir),
|
WithExecutorOptions(
|
||||||
|
task.WithDir(dir),
|
||||||
|
),
|
||||||
WithTask("foo"),
|
WithTask("foo"),
|
||||||
)
|
)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("a precondition was not met"),
|
WithName("a precondition was not met"),
|
||||||
WithNodeDir(dir),
|
WithExecutorOptions(
|
||||||
|
task.WithDir(dir),
|
||||||
|
),
|
||||||
WithTask("impossible"),
|
WithTask("impossible"),
|
||||||
WithRunError(),
|
WithRunError(),
|
||||||
)
|
)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("precondition in dependency fails the task"),
|
WithName("precondition in dependency fails the task"),
|
||||||
WithNodeDir(dir),
|
WithExecutorOptions(
|
||||||
|
task.WithDir(dir),
|
||||||
|
),
|
||||||
WithTask("depends_on_impossible"),
|
WithTask("depends_on_impossible"),
|
||||||
WithRunError(),
|
WithRunError(),
|
||||||
)
|
)
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("precondition in cmd fails the task"),
|
WithName("precondition in cmd fails the task"),
|
||||||
WithNodeDir(dir),
|
WithExecutorOptions(
|
||||||
|
task.WithDir(dir),
|
||||||
|
),
|
||||||
WithTask("executes_failing_task_as_cmd"),
|
WithTask("executes_failing_task_as_cmd"),
|
||||||
WithRunError(),
|
WithRunError(),
|
||||||
)
|
)
|
||||||
@@ -598,21 +596,25 @@ func TestAlias(t *testing.T) {
|
|||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("alias"),
|
WithName("alias"),
|
||||||
WithNodeDir("testdata/alias"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/alias"),
|
||||||
|
),
|
||||||
WithTask("f"),
|
WithTask("f"),
|
||||||
)
|
)
|
||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("duplicate alias"),
|
WithName("duplicate alias"),
|
||||||
WithNodeDir("testdata/alias"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/alias"),
|
||||||
|
),
|
||||||
WithTask("x"),
|
WithTask("x"),
|
||||||
WithRunError(),
|
WithRunError(),
|
||||||
)
|
)
|
||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("alias summary"),
|
WithName("alias summary"),
|
||||||
WithNodeDir("testdata/alias"),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/alias"),
|
||||||
task.WithSummary(true),
|
task.WithSummary(true),
|
||||||
),
|
),
|
||||||
WithTask("f"),
|
WithTask("f"),
|
||||||
@@ -624,14 +626,16 @@ func TestLabel(t *testing.T) {
|
|||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("up to date"),
|
WithName("up to date"),
|
||||||
WithNodeDir("testdata/label_uptodate"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/label_uptodate"),
|
||||||
|
),
|
||||||
WithTask("foo"),
|
WithTask("foo"),
|
||||||
)
|
)
|
||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("summary"),
|
WithName("summary"),
|
||||||
WithNodeDir("testdata/label_summary"),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/label_summary"),
|
||||||
task.WithSummary(true),
|
task.WithSummary(true),
|
||||||
),
|
),
|
||||||
WithTask("foo"),
|
WithTask("foo"),
|
||||||
@@ -639,22 +643,37 @@ func TestLabel(t *testing.T) {
|
|||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("status"),
|
WithName("status"),
|
||||||
WithNodeDir("testdata/label_status"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/label_status"),
|
||||||
|
),
|
||||||
WithTask("foo"),
|
WithTask("foo"),
|
||||||
WithStatusError(),
|
WithStatusError(),
|
||||||
)
|
)
|
||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("var"),
|
WithName("var"),
|
||||||
WithNodeDir("testdata/label_var"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/label_var"),
|
||||||
|
),
|
||||||
WithTask("foo"),
|
WithTask("foo"),
|
||||||
)
|
)
|
||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("label in summary"),
|
WithName("label in summary"),
|
||||||
WithNodeDir("testdata/label_summary"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/label_summary"),
|
||||||
|
),
|
||||||
WithTask("foo"),
|
WithTask("foo"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
NewExecutorTest(t,
|
||||||
|
WithName("label in error"),
|
||||||
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/label_error"),
|
||||||
|
),
|
||||||
|
WithTask("foo"),
|
||||||
|
WithRunError(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPromptInSummary(t *testing.T) {
|
func TestPromptInSummary(t *testing.T) {
|
||||||
@@ -679,8 +698,8 @@ func TestPromptInSummary(t *testing.T) {
|
|||||||
|
|
||||||
opts := []ExecutorTestOption{
|
opts := []ExecutorTestOption{
|
||||||
WithName(test.name),
|
WithName(test.name),
|
||||||
WithNodeDir("testdata/prompt"),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/prompt"),
|
||||||
task.WithAssumeTerm(true),
|
task.WithAssumeTerm(true),
|
||||||
),
|
),
|
||||||
WithTask("foo"),
|
WithTask("foo"),
|
||||||
@@ -698,8 +717,8 @@ func TestPromptWithIndirectTask(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithNodeDir("testdata/prompt"),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/prompt"),
|
||||||
task.WithAssumeTerm(true),
|
task.WithAssumeTerm(true),
|
||||||
),
|
),
|
||||||
WithTask("bar"),
|
WithTask("bar"),
|
||||||
@@ -712,8 +731,8 @@ func TestPromptAssumeYes(t *testing.T) {
|
|||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("--yes flag should skip prompt"),
|
WithName("--yes flag should skip prompt"),
|
||||||
WithNodeDir("testdata/prompt"),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/prompt"),
|
||||||
task.WithAssumeTerm(true),
|
task.WithAssumeTerm(true),
|
||||||
task.WithAssumeYes(true),
|
task.WithAssumeYes(true),
|
||||||
),
|
),
|
||||||
@@ -723,8 +742,8 @@ func TestPromptAssumeYes(t *testing.T) {
|
|||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("task should raise errors.TaskCancelledError"),
|
WithName("task should raise errors.TaskCancelledError"),
|
||||||
WithNodeDir("testdata/prompt"),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/prompt"),
|
||||||
task.WithAssumeTerm(true),
|
task.WithAssumeTerm(true),
|
||||||
),
|
),
|
||||||
WithTask("foo"),
|
WithTask("foo"),
|
||||||
@@ -761,8 +780,8 @@ func TestForCmds(t *testing.T) {
|
|||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
opts := []ExecutorTestOption{
|
opts := []ExecutorTestOption{
|
||||||
WithName(test.name),
|
WithName(test.name),
|
||||||
WithNodeDir("testdata/for/cmds"),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/for/cmds"),
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
task.WithForce(true),
|
task.WithForce(true),
|
||||||
),
|
),
|
||||||
@@ -804,8 +823,8 @@ func TestForDeps(t *testing.T) {
|
|||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
opts := []ExecutorTestOption{
|
opts := []ExecutorTestOption{
|
||||||
WithName(test.name),
|
WithName(test.name),
|
||||||
WithNodeDir("testdata/for/deps"),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/for/deps"),
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
task.WithForce(true),
|
task.WithForce(true),
|
||||||
// Force output of each dep to be grouped together to prevent interleaving
|
// Force output of each dep to be grouped together to prevent interleaving
|
||||||
@@ -850,8 +869,8 @@ func TestReference(t *testing.T) {
|
|||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName(test.name),
|
WithName(test.name),
|
||||||
WithNodeDir("testdata/var_references"),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/var_references"),
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
task.WithForce(true),
|
task.WithForce(true),
|
||||||
),
|
),
|
||||||
@@ -918,8 +937,8 @@ func TestVarInheritance(t *testing.T) {
|
|||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName(test.name),
|
WithName(test.name),
|
||||||
WithNodeDir(fmt.Sprintf("testdata/var_inheritance/v3/%s", test.name)),
|
|
||||||
WithExecutorOptions(
|
WithExecutorOptions(
|
||||||
|
task.WithDir(fmt.Sprintf("testdata/var_inheritance/v3/%s", test.name)),
|
||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
task.WithForce(true),
|
task.WithForce(true),
|
||||||
),
|
),
|
||||||
@@ -933,20 +952,26 @@ func TestFuzzyModel(t *testing.T) {
|
|||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("fuzzy"),
|
WithName("fuzzy"),
|
||||||
WithNodeDir("testdata/fuzzy"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/fuzzy"),
|
||||||
|
),
|
||||||
WithTask("instal"),
|
WithTask("instal"),
|
||||||
WithRunError(),
|
WithRunError(),
|
||||||
)
|
)
|
||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("not-fuzzy"),
|
WithName("not-fuzzy"),
|
||||||
WithNodeDir("testdata/fuzzy"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/fuzzy"),
|
||||||
|
),
|
||||||
WithTask("install"),
|
WithTask("install"),
|
||||||
)
|
)
|
||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("intern"),
|
WithName("intern"),
|
||||||
WithNodeDir("testdata/fuzzy"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/fuzzy"),
|
||||||
|
),
|
||||||
WithTask("intern"),
|
WithTask("intern"),
|
||||||
WithRunError(),
|
WithRunError(),
|
||||||
)
|
)
|
||||||
@@ -957,65 +982,17 @@ func TestIncludeChecksum(t *testing.T) {
|
|||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("correct"),
|
WithName("correct"),
|
||||||
WithNodeDir("testdata/includes_checksum/correct"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/includes_checksum/correct"),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
WithName("incorrect"),
|
WithName("incorrect"),
|
||||||
WithNodeDir("testdata/includes_checksum/incorrect"),
|
WithExecutorOptions(
|
||||||
WithReaderError(),
|
task.WithDir("testdata/includes_checksum/incorrect"),
|
||||||
|
),
|
||||||
|
WithSetupError(),
|
||||||
WithFixtureTemplating(),
|
WithFixtureTemplating(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWildcard(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
call string
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "basic wildcard",
|
|
||||||
call: "wildcard-foo",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "double wildcard",
|
|
||||||
call: "foo-wildcard-bar",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "store wildcard",
|
|
||||||
call: "start-foo",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "matches exactly",
|
|
||||||
call: "matches-exactly-*",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "no matches",
|
|
||||||
call: "no-match",
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "multiple matches",
|
|
||||||
call: "wildcard-foo-bar",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
opts := []ExecutorTestOption{
|
|
||||||
WithName(test.name),
|
|
||||||
WithNodeDir("testdata/wildcards"),
|
|
||||||
WithExecutorOptions(
|
|
||||||
task.WithSilent(true),
|
|
||||||
task.WithForce(true),
|
|
||||||
),
|
|
||||||
WithTask(test.call),
|
|
||||||
}
|
|
||||||
if test.wantErr {
|
|
||||||
opts = append(opts, WithRunError())
|
|
||||||
}
|
|
||||||
NewExecutorTest(t, opts...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/joho/godotenv"
|
"github.com/joho/godotenv"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/taskrc"
|
"github.com/go-task/task/v3/taskrc"
|
||||||
|
"github.com/go-task/task/v3/taskrc/ast"
|
||||||
)
|
)
|
||||||
|
|
||||||
const envPrefix = "TASK_X_"
|
const envPrefix = "TASK_X_"
|
||||||
@@ -31,16 +32,13 @@ var (
|
|||||||
var xList []Experiment
|
var xList []Experiment
|
||||||
|
|
||||||
func Parse(dir string) {
|
func Parse(dir string) {
|
||||||
|
config, _ := taskrc.GetConfig(dir)
|
||||||
|
ParseWithConfig(dir, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseWithConfig(dir string, config *ast.TaskRC) {
|
||||||
// Read any .env files
|
// Read any .env files
|
||||||
readDotEnv(dir)
|
readDotEnv(dir)
|
||||||
|
|
||||||
// Create a node for the Task config reader
|
|
||||||
node, _ := taskrc.NewNode("", dir)
|
|
||||||
|
|
||||||
// Read the Task config file
|
|
||||||
reader := taskrc.NewReader()
|
|
||||||
config, _ := reader.Read(node)
|
|
||||||
|
|
||||||
// Initialize the experiments
|
// Initialize the experiments
|
||||||
GentleForce = New("GENTLE_FORCE", config, 1)
|
GentleForce = New("GENTLE_FORCE", config, 1)
|
||||||
RemoteTaskfiles = New("REMOTE_TASKFILES", config, 1)
|
RemoteTaskfiles = New("REMOTE_TASKFILES", config, 1)
|
||||||
|
|||||||
@@ -2,9 +2,7 @@ package task_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"slices"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/sebdah/goldie/v2"
|
"github.com/sebdah/goldie/v2"
|
||||||
@@ -12,7 +10,6 @@ import (
|
|||||||
|
|
||||||
"github.com/go-task/task/v3"
|
"github.com/go-task/task/v3"
|
||||||
"github.com/go-task/task/v3/experiments"
|
"github.com/go-task/task/v3/experiments"
|
||||||
"github.com/go-task/task/v3/taskfile"
|
|
||||||
"github.com/go-task/task/v3/taskfile/ast"
|
"github.com/go-task/task/v3/taskfile/ast"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -29,17 +26,12 @@ type (
|
|||||||
// running `task gen:fixtures`.
|
// running `task gen:fixtures`.
|
||||||
FormatterTest struct {
|
FormatterTest struct {
|
||||||
TaskTest
|
TaskTest
|
||||||
task string
|
task string
|
||||||
vars map[string]any
|
vars map[string]any
|
||||||
nodeDir string
|
executorOpts []task.ExecutorOption
|
||||||
nodeEntrypoint string
|
listOptions task.ListOptions
|
||||||
nodeInsecure bool
|
wantSetupError bool
|
||||||
readerOpts []taskfile.ReaderOption
|
wantListError bool
|
||||||
executorOpts []task.ExecutorOption
|
|
||||||
listOptions task.ListOptions
|
|
||||||
wantReaderError bool
|
|
||||||
wantSetupError bool
|
|
||||||
wantListError bool
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -49,9 +41,8 @@ type (
|
|||||||
func NewFormatterTest(t *testing.T, opts ...FormatterTestOption) {
|
func NewFormatterTest(t *testing.T, opts ...FormatterTestOption) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
tt := &FormatterTest{
|
tt := &FormatterTest{
|
||||||
task: "default",
|
task: "default",
|
||||||
vars: map[string]any{},
|
vars: map[string]any{},
|
||||||
nodeDir: ".",
|
|
||||||
TaskTest: TaskTest{
|
TaskTest: TaskTest{
|
||||||
experiments: map[*experiments.Experiment]int{},
|
experiments: map[*experiments.Experiment]int{},
|
||||||
fixtureTemplateData: map[string]any{},
|
fixtureTemplateData: map[string]any{},
|
||||||
@@ -123,57 +114,23 @@ func (tt *FormatterTest) run(t *testing.T) {
|
|||||||
f := func(t *testing.T) {
|
f := func(t *testing.T) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
// Create a new root node for the given entrypoint
|
opts := append(
|
||||||
node, err := taskfile.NewRootNode(
|
|
||||||
tt.nodeEntrypoint,
|
|
||||||
tt.nodeDir,
|
|
||||||
tt.nodeInsecure,
|
|
||||||
)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// Create a golden fixture file for the output
|
|
||||||
g := goldie.New(t,
|
|
||||||
goldie.WithFixtureDir(filepath.Join(node.Dir(), "testdata")),
|
|
||||||
)
|
|
||||||
|
|
||||||
// Set up a temporary directory for the taskfile reader and task executor
|
|
||||||
tempDir, err := task.NewTempDir(node.Dir())
|
|
||||||
require.NoError(t, err)
|
|
||||||
tt.readerOpts = append(tt.readerOpts, taskfile.WithTempDir(tempDir.Remote))
|
|
||||||
|
|
||||||
// Set up the taskfile reader
|
|
||||||
reader := taskfile.NewReader(tt.readerOpts...)
|
|
||||||
graph, err := reader.Read(ctx, node)
|
|
||||||
if tt.wantReaderError {
|
|
||||||
require.Error(t, err)
|
|
||||||
tt.writeFixtureErrReader(t, g, err)
|
|
||||||
tt.writeFixtureBuffer(t, g, buf)
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
executorOpts := slices.Concat(
|
|
||||||
// Apply the node directory and temp directory to the executor options
|
|
||||||
// by default, but allow them to by overridden by the test options
|
|
||||||
[]task.ExecutorOption{
|
|
||||||
task.WithDir(node.Dir()),
|
|
||||||
task.WithTempDir(tempDir),
|
|
||||||
},
|
|
||||||
// Apply the executor options from the test
|
|
||||||
tt.executorOpts,
|
tt.executorOpts,
|
||||||
// Force the input/output streams to be set to the test buffer
|
task.WithStdout(&buf),
|
||||||
[]task.ExecutorOption{
|
task.WithStderr(&buf),
|
||||||
task.WithStdout(&buf),
|
|
||||||
task.WithStderr(&buf),
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Set up the task executor
|
// Set up the task executor
|
||||||
executor, err := task.NewExecutor(graph, executorOpts...)
|
e := task.NewExecutor(opts...)
|
||||||
if tt.wantSetupError {
|
|
||||||
|
// Create a golden fixture file for the output
|
||||||
|
g := goldie.New(t,
|
||||||
|
goldie.WithFixtureDir(filepath.Join(e.Dir, "testdata")),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Call setup and check for errors
|
||||||
|
if err := e.Setup(); tt.wantSetupError {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
tt.writeFixtureErrSetup(t, g, err)
|
tt.writeFixtureErrSetup(t, g, err)
|
||||||
tt.writeFixtureBuffer(t, g, buf)
|
tt.writeFixtureBuffer(t, g, buf)
|
||||||
@@ -189,7 +146,7 @@ func (tt *FormatterTest) run(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Run the formatter and check for errors
|
// Run the formatter and check for errors
|
||||||
if _, err := executor.ListTasks(tt.listOptions); tt.wantListError {
|
if _, err := e.ListTasks(tt.listOptions); tt.wantListError {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
tt.writeFixtureErrList(t, g, err)
|
tt.writeFixtureErrList(t, g, err)
|
||||||
tt.writeFixtureBuffer(t, g, buf)
|
tt.writeFixtureBuffer(t, g, buf)
|
||||||
@@ -213,7 +170,9 @@ func TestNoLabelInList(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
NewFormatterTest(t,
|
NewFormatterTest(t,
|
||||||
WithNodeDir("testdata/label_list"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/label_list"),
|
||||||
|
),
|
||||||
WithListOptions(task.ListOptions{
|
WithListOptions(task.ListOptions{
|
||||||
ListOnlyTasksWithDescriptions: true,
|
ListOnlyTasksWithDescriptions: true,
|
||||||
}),
|
}),
|
||||||
@@ -225,7 +184,9 @@ func TestListAllShowsNoDesc(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
NewFormatterTest(t,
|
NewFormatterTest(t,
|
||||||
WithNodeDir("testdata/list_mixed_desc"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/list_mixed_desc"),
|
||||||
|
),
|
||||||
WithListOptions(task.ListOptions{
|
WithListOptions(task.ListOptions{
|
||||||
ListAllTasks: true,
|
ListAllTasks: true,
|
||||||
}),
|
}),
|
||||||
@@ -237,7 +198,9 @@ func TestListCanListDescOnly(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
NewFormatterTest(t,
|
NewFormatterTest(t,
|
||||||
WithNodeDir("testdata/list_mixed_desc"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/list_mixed_desc"),
|
||||||
|
),
|
||||||
WithListOptions(task.ListOptions{
|
WithListOptions(task.ListOptions{
|
||||||
ListOnlyTasksWithDescriptions: true,
|
ListOnlyTasksWithDescriptions: true,
|
||||||
}),
|
}),
|
||||||
@@ -248,7 +211,9 @@ func TestListDescInterpolation(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
NewFormatterTest(t,
|
NewFormatterTest(t,
|
||||||
WithNodeDir("testdata/list_desc_interpolation"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/list_desc_interpolation"),
|
||||||
|
),
|
||||||
WithListOptions(task.ListOptions{
|
WithListOptions(task.ListOptions{
|
||||||
ListOnlyTasksWithDescriptions: true,
|
ListOnlyTasksWithDescriptions: true,
|
||||||
}),
|
}),
|
||||||
@@ -259,7 +224,9 @@ func TestJsonListFormat(t *testing.T) {
|
|||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
NewFormatterTest(t,
|
NewFormatterTest(t,
|
||||||
WithNodeDir("testdata/json_list_format"),
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/json_list_format"),
|
||||||
|
),
|
||||||
WithListOptions(task.ListOptions{
|
WithListOptions(task.ListOptions{
|
||||||
FormatTaskListAsJSON: true,
|
FormatTaskListAsJSON: true,
|
||||||
}),
|
}),
|
||||||
|
|||||||
32
go.mod
32
go.mod
@@ -1,6 +1,6 @@
|
|||||||
module github.com/go-task/task/v3
|
module github.com/go-task/task/v3
|
||||||
|
|
||||||
go 1.23.0
|
go 1.24.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Ladicle/tabwriter v1.0.0
|
github.com/Ladicle/tabwriter v1.0.0
|
||||||
@@ -13,22 +13,22 @@ require (
|
|||||||
github.com/fatih/color v1.18.0
|
github.com/fatih/color v1.18.0
|
||||||
github.com/fsnotify/fsnotify v1.9.0
|
github.com/fsnotify/fsnotify v1.9.0
|
||||||
github.com/go-git/go-billy/v5 v5.6.2
|
github.com/go-git/go-billy/v5 v5.6.2
|
||||||
github.com/go-git/go-git/v5 v5.16.2
|
github.com/go-git/go-git/v5 v5.16.3
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0
|
github.com/go-task/slim-sprig/v3 v3.0.0
|
||||||
github.com/go-task/template v0.2.0
|
github.com/go-task/template v0.2.0
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/joho/godotenv v1.5.1
|
github.com/joho/godotenv v1.5.1
|
||||||
github.com/mitchellh/hashstructure/v2 v2.0.2
|
github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||||
github.com/otiai10/copy v1.14.1
|
|
||||||
github.com/puzpuzpuz/xsync/v3 v3.5.1
|
github.com/puzpuzpuz/xsync/v3 v3.5.1
|
||||||
github.com/sajari/fuzzy v1.0.0
|
github.com/sajari/fuzzy v1.0.0
|
||||||
github.com/sebdah/goldie/v2 v2.7.1
|
github.com/sebdah/goldie/v2 v2.8.0
|
||||||
github.com/spf13/pflag v1.0.7
|
github.com/spf13/pflag v1.0.10
|
||||||
github.com/stretchr/testify v1.10.0
|
github.com/stretchr/testify v1.11.1
|
||||||
github.com/zeebo/xxh3 v1.0.2
|
github.com/zeebo/xxh3 v1.0.2
|
||||||
golang.org/x/sync v0.16.0
|
go.yaml.in/yaml/v4 v4.0.0-rc.3
|
||||||
golang.org/x/term v0.33.0
|
golang.org/x/sync v0.18.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
golang.org/x/term v0.37.0
|
||||||
|
mvdan.cc/sh/moreinterp v0.0.0-20251109230715-65adef8e2c5b
|
||||||
mvdan.cc/sh/v3 v3.12.0
|
mvdan.cc/sh/v3 v3.12.0
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -39,23 +39,29 @@ require (
|
|||||||
github.com/cloudflare/circl v1.6.1 // indirect
|
github.com/cloudflare/circl v1.6.1 // indirect
|
||||||
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
|
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
|
||||||
github.com/dlclark/regexp2 v1.11.5 // indirect
|
github.com/dlclark/regexp2 v1.11.5 // indirect
|
||||||
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
github.com/emirpasic/gods v1.18.1 // indirect
|
github.com/emirpasic/gods v1.18.1 // indirect
|
||||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||||
|
github.com/klauspost/compress v1.18.0 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||||
|
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/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/pjbgf/sha1cd v0.3.2 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
||||||
github.com/skeema/knownhosts v1.3.1 // indirect
|
github.com/skeema/knownhosts v1.3.1 // indirect
|
||||||
github.com/stretchr/objx v0.5.2 // indirect
|
github.com/stretchr/objx v0.5.2 // indirect
|
||||||
|
github.com/u-root/u-root v0.15.1-0.20251014130006-62f7144b33da // indirect
|
||||||
|
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect
|
||||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||||
golang.org/x/crypto v0.37.0 // indirect
|
golang.org/x/crypto v0.39.0 // indirect
|
||||||
golang.org/x/net v0.39.0 // indirect
|
golang.org/x/net v0.41.0 // indirect
|
||||||
golang.org/x/sys v0.34.0 // indirect
|
golang.org/x/sys v0.38.0 // indirect
|
||||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
76
go.sum
76
go.sum
@@ -11,12 +11,10 @@ github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNx
|
|||||||
github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
|
github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
|
||||||
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
|
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
|
||||||
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
||||||
github.com/alecthomas/chroma/v2 v2.19.0 h1:Im+SLRgT8maArxv81mULDWN8oKxkzboH07CHesxElq4=
|
|
||||||
github.com/alecthomas/chroma/v2 v2.19.0/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk=
|
|
||||||
github.com/alecthomas/chroma/v2 v2.20.0 h1:sfIHpxPyR07/Oylvmcai3X/exDlE8+FA820NTz+9sGw=
|
github.com/alecthomas/chroma/v2 v2.20.0 h1:sfIHpxPyR07/Oylvmcai3X/exDlE8+FA820NTz+9sGw=
|
||||||
github.com/alecthomas/chroma/v2 v2.20.0/go.mod h1:e7tViK0xh/Nf4BYHl00ycY6rV7b8iXBksI9E359yNmA=
|
github.com/alecthomas/chroma/v2 v2.20.0/go.mod h1:e7tViK0xh/Nf4BYHl00ycY6rV7b8iXBksI9E359yNmA=
|
||||||
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
|
github.com/alecthomas/repr v0.5.1 h1:E3G4t2QbHTSNpPKBgMTln5KLkZHLOcU7r37J4pXBuIg=
|
||||||
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
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 h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||||
@@ -36,6 +34,8 @@ github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZ
|
|||||||
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||||
github.com/dominikbraun/graph v0.23.0 h1:TdZB4pPqCLFxYhdyMFb1TBdFxp8XLcJfTTBQucVPgCo=
|
github.com/dominikbraun/graph v0.23.0 h1:TdZB4pPqCLFxYhdyMFb1TBdFxp8XLcJfTTBQucVPgCo=
|
||||||
github.com/dominikbraun/graph v0.23.0/go.mod h1:yOjYyogZLY1LSG9E33JWZJiq5k83Qy2C6POAuiViluc=
|
github.com/dominikbraun/graph v0.23.0/go.mod h1:yOjYyogZLY1LSG9E33JWZJiq5k83Qy2C6POAuiViluc=
|
||||||
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
|
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
|
||||||
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
|
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
|
||||||
github.com/elliotchance/orderedmap/v3 v3.1.0 h1:j4DJ5ObEmMBt/lcwIecKcoRxIQUEnw0L804lXYDt/pg=
|
github.com/elliotchance/orderedmap/v3 v3.1.0 h1:j4DJ5ObEmMBt/lcwIecKcoRxIQUEnw0L804lXYDt/pg=
|
||||||
@@ -54,8 +54,8 @@ 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-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 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-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||||
github.com/go-git/go-git/v5 v5.16.2 h1:fT6ZIOjE5iEnkzKyxTHK1W4HGAsPhqEqiSAssSO77hM=
|
github.com/go-git/go-git/v5 v5.16.3 h1:Z8BtvxZ09bYm/yYNgPKCzgWtaRqDTgIKRgIRHBfU6Z8=
|
||||||
github.com/go-git/go-git/v5 v5.16.2/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
|
github.com/go-git/go-git/v5 v5.16.3/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
|
||||||
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
|
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-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 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||||
@@ -76,8 +76,12 @@ github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
|||||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
|
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
|
||||||
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||||
|
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||||
|
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||||
|
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
|
||||||
|
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
@@ -94,10 +98,8 @@ github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4
|
|||||||
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
|
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
|
||||||
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
||||||
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
||||||
github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8=
|
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
|
||||||
github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I=
|
github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||||
github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs=
|
|
||||||
github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
|
|
||||||
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
|
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
|
||||||
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
|
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
@@ -111,40 +113,48 @@ 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/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 h1:+FmwVvJErsd0d0hAPlj4CxqxUtQY/fOoY0DwX4ykpRY=
|
||||||
github.com/sajari/fuzzy v1.0.0/go.mod h1:OjYR6KxoWOe9+dOlXeiCJd4dIbED4Oo8wpS89o0pwOo=
|
github.com/sajari/fuzzy v1.0.0/go.mod h1:OjYR6KxoWOe9+dOlXeiCJd4dIbED4Oo8wpS89o0pwOo=
|
||||||
github.com/sebdah/goldie/v2 v2.7.1 h1:PkBHymaYdtvEkZV7TmyqKxdmn5/Vcj+8TpATWZjnG5E=
|
github.com/sebdah/goldie/v2 v2.8.0 h1:dZb9wR8q5++oplmEiJT+U/5KyotVD+HNGCAc5gNr8rc=
|
||||||
github.com/sebdah/goldie/v2 v2.7.1/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI=
|
github.com/sebdah/goldie/v2 v2.8.0/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.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 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
||||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
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/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 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
|
||||||
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
|
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
|
||||||
github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M=
|
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||||
github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
|
github.com/u-root/u-root v0.15.1-0.20251014130006-62f7144b33da h1:Vst9Tvq3G6f6pYBvxy7coi2arDsnOZ3Mkj8MkNarSK8=
|
||||||
|
github.com/u-root/u-root v0.15.1-0.20251014130006-62f7144b33da/go.mod h1:R49zft13memK20EgFAvmTbXBS0t29UvglnM0BCA1ldQ=
|
||||||
|
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 h1:pyC9PaHYZFgEKFdlp3G8RaCKgVpHZnecvArXvPXcFkM=
|
||||||
|
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA=
|
||||||
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||||
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
||||||
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
|
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
|
||||||
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
||||||
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
|
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
|
||||||
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
|
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
|
||||||
|
go.yaml.in/yaml/v4 v4.0.0-rc.3 h1:3h1fjsh1CTAPjW7q/EMe+C8shx5d8ctzZTrLcs/j8Go=
|
||||||
|
go.yaml.in/yaml/v4 v4.0.0-rc.3/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0=
|
||||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
||||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
|
||||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
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.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
||||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
|
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||||
|
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@@ -154,14 +164,18 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
||||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
|
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||||
|
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
|
golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q=
|
||||||
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
|
golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss=
|
||||||
|
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
|
||||||
|
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
@@ -173,5 +187,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.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
mvdan.cc/sh/moreinterp v0.0.0-20251109230715-65adef8e2c5b h1:vTpx76nZDTP/BAGnnhEXYjM+8nPKe9+I86qCErBvjCw=
|
||||||
|
mvdan.cc/sh/moreinterp v0.0.0-20251109230715-65adef8e2c5b/go.mod h1:bDyKbUYKqkFunWmxxuSPrkYpln9QZcUsqu7W128qYW4=
|
||||||
mvdan.cc/sh/v3 v3.12.0 h1:ejKUR7ONP5bb+UGHGEG/k9V5+pRVIyD+LsZz7o8KHrI=
|
mvdan.cc/sh/v3 v3.12.0 h1:ejKUR7ONP5bb+UGHGEG/k9V5+pRVIyD+LsZz7o8KHrI=
|
||||||
mvdan.cc/sh/v3 v3.12.0/go.mod h1:Se6Cj17eYSn+sNooLZiEUnNNmNxg0imoYlTu4CyaGyg=
|
mvdan.cc/sh/v3 v3.12.0/go.mod h1:Se6Cj17eYSn+sNooLZiEUnNNmNxg0imoYlTu4CyaGyg=
|
||||||
|
|||||||
65
help.go
65
help.go
@@ -24,15 +24,17 @@ type ListOptions struct {
|
|||||||
ListAllTasks bool
|
ListAllTasks bool
|
||||||
FormatTaskListAsJSON bool
|
FormatTaskListAsJSON bool
|
||||||
NoStatus bool
|
NoStatus bool
|
||||||
|
Nested bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewListOptions creates a new ListOptions instance
|
// NewListOptions creates a new ListOptions instance
|
||||||
func NewListOptions(list, listAll, listAsJson, noStatus bool) ListOptions {
|
func NewListOptions(list, listAll, listAsJson, noStatus, nested bool) ListOptions {
|
||||||
return ListOptions{
|
return ListOptions{
|
||||||
ListOnlyTasksWithDescriptions: list,
|
ListOnlyTasksWithDescriptions: list,
|
||||||
ListAllTasks: listAll,
|
ListAllTasks: listAll,
|
||||||
FormatTaskListAsJSON: listAsJson,
|
FormatTaskListAsJSON: listAsJson,
|
||||||
NoStatus: noStatus,
|
NoStatus: noStatus,
|
||||||
|
Nested: nested,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +65,7 @@ func (e *Executor) ListTasks(o ListOptions) (bool, error) {
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
if o.FormatTaskListAsJSON {
|
if o.FormatTaskListAsJSON {
|
||||||
output, err := e.ToEditorOutput(tasks, o.NoStatus)
|
output, err := e.ToEditorOutput(tasks, o.NoStatus, o.Nested)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
@@ -135,33 +137,17 @@ func (e *Executor) ListTaskNames(allTasks bool) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Executor) ToEditorOutput(tasks []*ast.Task, noStatus bool) (*editors.Taskfile, error) {
|
func (e *Executor) ToEditorOutput(tasks []*ast.Task, noStatus bool, nested bool) (*editors.Namespace, error) {
|
||||||
o := &editors.Taskfile{
|
|
||||||
Tasks: make([]editors.Task, len(tasks)),
|
|
||||||
Location: e.Taskfile.Location,
|
|
||||||
}
|
|
||||||
var g errgroup.Group
|
var g errgroup.Group
|
||||||
|
editorTasks := make([]editors.Task, len(tasks))
|
||||||
|
|
||||||
|
// Look over each task in parallel and turn it into an editor task
|
||||||
for i := range tasks {
|
for i := range tasks {
|
||||||
aliases := []string{}
|
|
||||||
if len(tasks[i].Aliases) > 0 {
|
|
||||||
aliases = tasks[i].Aliases
|
|
||||||
}
|
|
||||||
g.Go(func() error {
|
g.Go(func() error {
|
||||||
o.Tasks[i] = editors.Task{
|
editorTask := editors.NewTask(tasks[i])
|
||||||
Name: tasks[i].Name(),
|
|
||||||
Task: tasks[i].Task,
|
|
||||||
Desc: tasks[i].Desc,
|
|
||||||
Summary: tasks[i].Summary,
|
|
||||||
Aliases: aliases,
|
|
||||||
UpToDate: false,
|
|
||||||
Location: &editors.Location{
|
|
||||||
Line: tasks[i].Location.Line,
|
|
||||||
Column: tasks[i].Location.Column,
|
|
||||||
Taskfile: tasks[i].Location.Taskfile,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if noStatus {
|
if noStatus {
|
||||||
|
editorTasks[i] = editorTask
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,10 +166,35 @@ func (e *Executor) ToEditorOutput(tasks []*ast.Task, noStatus bool) (*editors.Ta
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
o.Tasks[i].UpToDate = upToDate
|
editorTask.UpToDate = &upToDate
|
||||||
|
editorTasks[i] = editorTask
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return o, g.Wait()
|
if err := g.Wait(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the root namespace
|
||||||
|
var tasksLen int
|
||||||
|
if !nested {
|
||||||
|
tasksLen = len(editorTasks)
|
||||||
|
}
|
||||||
|
rootNamespace := &editors.Namespace{
|
||||||
|
Tasks: make([]editors.Task, tasksLen),
|
||||||
|
Location: e.Taskfile.Location,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively add namespaces to the root namespace or if nesting is
|
||||||
|
// disabled add them all to the root namespace
|
||||||
|
for i, task := range editorTasks {
|
||||||
|
taskNamespacePath := strings.Split(task.Task, ast.NamespaceSeparator)
|
||||||
|
if nested {
|
||||||
|
rootNamespace.AddNamespace(taskNamespacePath, task)
|
||||||
|
} else {
|
||||||
|
rootNamespace.Tasks[i] = task
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rootNamespace, g.Wait()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
package editors
|
package editors
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/go-task/task/v3/taskfile/ast"
|
||||||
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// Taskfile wraps task list output for use in editor integrations (e.g. VSCode, etc)
|
// Namespace wraps task list output for use in editor integrations (e.g. VSCode, etc)
|
||||||
Taskfile struct {
|
Namespace struct {
|
||||||
Tasks []Task `json:"tasks"`
|
Tasks []Task `json:"tasks"`
|
||||||
Location string `json:"location"`
|
Namespaces map[string]*Namespace `json:"namespaces,omitempty"`
|
||||||
|
Location string `json:"location,omitempty"`
|
||||||
}
|
}
|
||||||
// Task describes a single task
|
// Task describes a single task
|
||||||
Task struct {
|
Task struct {
|
||||||
@@ -13,7 +18,7 @@ type (
|
|||||||
Desc string `json:"desc"`
|
Desc string `json:"desc"`
|
||||||
Summary string `json:"summary"`
|
Summary string `json:"summary"`
|
||||||
Aliases []string `json:"aliases"`
|
Aliases []string `json:"aliases"`
|
||||||
UpToDate bool `json:"up_to_date"`
|
UpToDate *bool `json:"up_to_date,omitempty"`
|
||||||
Location *Location `json:"location"`
|
Location *Location `json:"location"`
|
||||||
}
|
}
|
||||||
// Location describes a task's location in a taskfile
|
// Location describes a task's location in a taskfile
|
||||||
@@ -23,3 +28,59 @@ type (
|
|||||||
Taskfile string `json:"taskfile"`
|
Taskfile string `json:"taskfile"`
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func NewTask(task *ast.Task) Task {
|
||||||
|
aliases := []string{}
|
||||||
|
if len(task.Aliases) > 0 {
|
||||||
|
aliases = task.Aliases
|
||||||
|
}
|
||||||
|
return Task{
|
||||||
|
Name: task.Name(),
|
||||||
|
Task: task.Task,
|
||||||
|
Desc: task.Desc,
|
||||||
|
Summary: task.Summary,
|
||||||
|
Aliases: aliases,
|
||||||
|
Location: &Location{
|
||||||
|
Line: task.Location.Line,
|
||||||
|
Column: task.Location.Column,
|
||||||
|
Taskfile: task.Location.Taskfile,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (parent *Namespace) AddNamespace(namespacePath []string, task Task) {
|
||||||
|
if len(namespacePath) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are no child namespaces, then we have found a task and we can
|
||||||
|
// simply add it to the current namespace
|
||||||
|
if len(namespacePath) == 1 {
|
||||||
|
parent.Tasks = append(parent.Tasks, task)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the key of the current namespace in the path
|
||||||
|
namespaceKey := namespacePath[0]
|
||||||
|
|
||||||
|
// Add the namespace to the parent namespaces map using the namespace key
|
||||||
|
if parent.Namespaces == nil {
|
||||||
|
parent.Namespaces = make(map[string]*Namespace, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search for the current namespace in the parent namespaces map
|
||||||
|
// If it doesn't exist, create it
|
||||||
|
namespace, ok := parent.Namespaces[namespaceKey]
|
||||||
|
if !ok {
|
||||||
|
namespace = &Namespace{}
|
||||||
|
parent.Namespaces[namespaceKey] = namespace
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the current namespace key from the namespace path.
|
||||||
|
childNamespacePath := namespacePath[1:]
|
||||||
|
|
||||||
|
// If there are no child namespaces in the task name, then we have found the
|
||||||
|
// namespace of the task and we can add it to the current namespace.
|
||||||
|
// Otherwise, we need to go deeper
|
||||||
|
namespace.AddNamespace(childNamespacePath, task)
|
||||||
|
}
|
||||||
|
|||||||
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"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
|
"mvdan.cc/sh/moreinterp/coreutils"
|
||||||
"mvdan.cc/sh/v3/expand"
|
"mvdan.cc/sh/v3/expand"
|
||||||
"mvdan.cc/sh/v3/interp"
|
"mvdan.cc/sh/v3/interp"
|
||||||
"mvdan.cc/sh/v3/syntax"
|
"mvdan.cc/sh/v3/syntax"
|
||||||
@@ -59,7 +59,7 @@ func RunCommand(ctx context.Context, opts *RunCommandOptions) error {
|
|||||||
r, err := interp.New(
|
r, err := interp.New(
|
||||||
interp.Params(params...),
|
interp.Params(params...),
|
||||||
interp.Env(expand.ListEnviron(environ...)),
|
interp.Env(expand.ListEnviron(environ...)),
|
||||||
interp.ExecHandlers(execHandler),
|
interp.ExecHandlers(execHandlers()...),
|
||||||
interp.OpenHandler(openHandler),
|
interp.OpenHandler(openHandler),
|
||||||
interp.StdIO(opts.Stdin, opts.Stdout, opts.Stderr),
|
interp.StdIO(opts.Stdin, opts.Stdout, opts.Stderr),
|
||||||
dirOption(opts.Dir),
|
dirOption(opts.Dir),
|
||||||
@@ -143,8 +143,11 @@ func ExpandFields(s string) ([]string, error) {
|
|||||||
return expand.Fields(cfg, words...)
|
return expand.Fields(cfg, words...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func execHandler(next interp.ExecHandlerFunc) interp.ExecHandlerFunc {
|
func execHandlers() (handlers []func(next interp.ExecHandlerFunc) interp.ExecHandlerFunc) {
|
||||||
return interp.DefaultExecHandler(15 * time.Second)
|
if useGoCoreUtils {
|
||||||
|
handlers = append(handlers, coreutils.ExecHandler)
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func openHandler(ctx context.Context, path string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) {
|
func openHandler(ctx context.Context, path string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package fingerprint
|
package fingerprint
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -164,7 +163,7 @@ func TestIsTaskUpToDate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
result, err := IsTaskUpToDate(
|
result, err := IsTaskUpToDate(
|
||||||
context.Background(),
|
t.Context(),
|
||||||
tt.task,
|
tt.task,
|
||||||
WithStatusChecker(mockStatusChecker),
|
WithStatusChecker(mockStatusChecker),
|
||||||
WithSourcesChecker(mockSourcesChecker),
|
WithSourcesChecker(mockSourcesChecker),
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
@@ -13,10 +12,10 @@ import (
|
|||||||
"github.com/go-task/task/v3"
|
"github.com/go-task/task/v3"
|
||||||
"github.com/go-task/task/v3/errors"
|
"github.com/go-task/task/v3/errors"
|
||||||
"github.com/go-task/task/v3/experiments"
|
"github.com/go-task/task/v3/experiments"
|
||||||
"github.com/go-task/task/v3/internal/env"
|
|
||||||
"github.com/go-task/task/v3/internal/sort"
|
"github.com/go-task/task/v3/internal/sort"
|
||||||
"github.com/go-task/task/v3/taskfile"
|
|
||||||
"github.com/go-task/task/v3/taskfile/ast"
|
"github.com/go-task/task/v3/taskfile/ast"
|
||||||
|
"github.com/go-task/task/v3/taskrc"
|
||||||
|
taskrcast "github.com/go-task/task/v3/taskrc/ast"
|
||||||
)
|
)
|
||||||
|
|
||||||
const usage = `Usage: task [flags...] [task...]
|
const usage = `Usage: task [flags...] [task...]
|
||||||
@@ -52,6 +51,7 @@ var (
|
|||||||
TaskSort string
|
TaskSort string
|
||||||
Status bool
|
Status bool
|
||||||
NoStatus bool
|
NoStatus bool
|
||||||
|
Nested bool
|
||||||
Insecure bool
|
Insecure bool
|
||||||
Force bool
|
Force bool
|
||||||
ForceAll bool
|
ForceAll bool
|
||||||
@@ -96,7 +96,9 @@ func init() {
|
|||||||
|
|
||||||
// Parse the experiments
|
// Parse the experiments
|
||||||
dir = cmp.Or(dir, filepath.Dir(entrypoint))
|
dir = cmp.Or(dir, filepath.Dir(entrypoint))
|
||||||
experiments.Parse(dir)
|
|
||||||
|
config, _ := taskrc.GetConfig(dir)
|
||||||
|
experiments.ParseWithConfig(dir, config)
|
||||||
|
|
||||||
// Parse the rest of the flags
|
// Parse the rest of the flags
|
||||||
log.SetFlags(0)
|
log.SetFlags(0)
|
||||||
@@ -105,10 +107,7 @@ func init() {
|
|||||||
log.Print(usage)
|
log.Print(usage)
|
||||||
pflag.PrintDefaults()
|
pflag.PrintDefaults()
|
||||||
}
|
}
|
||||||
offline, err := strconv.ParseBool(cmp.Or(env.GetTaskEnv("OFFLINE"), "false"))
|
|
||||||
if err != nil {
|
|
||||||
offline = false
|
|
||||||
}
|
|
||||||
pflag.BoolVar(&Version, "version", false, "Show Task version.")
|
pflag.BoolVar(&Version, "version", false, "Show Task version.")
|
||||||
pflag.BoolVarP(&Help, "help", "h", false, "Shows Task usage.")
|
pflag.BoolVarP(&Help, "help", "h", false, "Shows Task usage.")
|
||||||
pflag.BoolVarP(&Init, "init", "i", false, "Creates a new Taskfile.yml in the current folder.")
|
pflag.BoolVarP(&Init, "init", "i", false, "Creates a new Taskfile.yml in the current folder.")
|
||||||
@@ -119,9 +118,10 @@ func init() {
|
|||||||
pflag.StringVar(&TaskSort, "sort", "", "Changes the order of the tasks when listed. [default|alphanumeric|none].")
|
pflag.StringVar(&TaskSort, "sort", "", "Changes the order of the tasks when listed. [default|alphanumeric|none].")
|
||||||
pflag.BoolVar(&Status, "status", false, "Exits with non-zero exit code if any of the given tasks is not up-to-date.")
|
pflag.BoolVar(&Status, "status", false, "Exits with non-zero exit code if any of the given tasks is not up-to-date.")
|
||||||
pflag.BoolVar(&NoStatus, "no-status", false, "Ignore status when listing tasks as JSON")
|
pflag.BoolVar(&NoStatus, "no-status", false, "Ignore status when listing tasks as JSON")
|
||||||
pflag.BoolVar(&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(&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(&Silent, "silent", "s", false, "Disables echoing.")
|
||||||
pflag.BoolVarP(&AssumeYes, "yes", "y", false, "Assume \"yes\" as answer to all prompts.")
|
pflag.BoolVarP(&AssumeYes, "yes", "y", false, "Assume \"yes\" as answer to all prompts.")
|
||||||
pflag.BoolVarP(&Parallel, "parallel", "p", false, "Executes tasks provided on command line in parallel.")
|
pflag.BoolVarP(&Parallel, "parallel", "p", false, "Executes tasks provided on command line in parallel.")
|
||||||
@@ -135,7 +135,7 @@ func init() {
|
|||||||
pflag.StringVar(&Output.Group.End, "output-group-end", "", "Message template to print after a task's grouped output.")
|
pflag.StringVar(&Output.Group.End, "output-group-end", "", "Message template to print after a task's grouped output.")
|
||||||
pflag.BoolVar(&Output.Group.ErrorOnly, "output-group-error-only", false, "Swallow output from successful tasks.")
|
pflag.BoolVar(&Output.Group.ErrorOnly, "output-group-error-only", false, "Swallow output from successful tasks.")
|
||||||
pflag.BoolVarP(&Color, "color", "c", true, "Colored output. Enabled by default. Set flag to false or use NO_COLOR=1 to disable.")
|
pflag.BoolVarP(&Color, "color", "c", true, "Colored output. Enabled by default. Set flag to false or use NO_COLOR=1 to disable.")
|
||||||
pflag.IntVarP(&Concurrency, "concurrency", "C", 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.DurationVarP(&Interval, "interval", "I", 0, "Interval to watch for changes.")
|
||||||
pflag.BoolVarP(&Global, "global", "g", false, "Runs global Taskfile, from $HOME/{T,t}askfile.{yml,yaml}.")
|
pflag.BoolVarP(&Global, "global", "g", false, "Runs global Taskfile, from $HOME/{T,t}askfile.{yml,yaml}.")
|
||||||
pflag.BoolVar(&Experiments, "experiments", false, "Lists all the available experiments and whether or not they are enabled.")
|
pflag.BoolVar(&Experiments, "experiments", false, "Lists all the available experiments and whether or not they are enabled.")
|
||||||
@@ -151,12 +151,11 @@ func init() {
|
|||||||
// Remote Taskfiles experiment will adds the "download" and "offline" flags
|
// Remote Taskfiles experiment will adds the "download" and "offline" flags
|
||||||
if experiments.RemoteTaskfiles.Enabled() {
|
if experiments.RemoteTaskfiles.Enabled() {
|
||||||
pflag.BoolVar(&Download, "download", false, "Downloads a cached version of a remote Taskfile.")
|
pflag.BoolVar(&Download, "download", false, "Downloads a cached version of a remote Taskfile.")
|
||||||
pflag.BoolVar(&Offline, "offline", offline, "Forces Task to only use local or cached 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", time.Second*10, "Timeout for downloading remote 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.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.CacheExpiry }, 0), "Expiry duration for cached remote Taskfiles.")
|
||||||
}
|
}
|
||||||
|
|
||||||
pflag.Parse()
|
pflag.Parse()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,12 +196,16 @@ func Validate() error {
|
|||||||
return errors.New("task: --no-status only applies to --json with --list or --list-all")
|
return errors.New("task: --no-status only applies to --json with --list or --list-all")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if Nested && !ListJson {
|
||||||
|
return errors.New("task: --nested only applies to --json with --list or --list-all")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithFlags is a special internal functional option that is used to pass flags
|
// WithFlags is a special internal functional option that is used to pass flags
|
||||||
// from the CLI into any constructor that accepts functional options.
|
// from the CLI into any constructor that accepts functional options.
|
||||||
func WithFlags() *flagsOption {
|
func WithFlags() task.ExecutorOption {
|
||||||
return &flagsOption{}
|
return &flagsOption{}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,8 +232,14 @@ func (o *flagsOption) ApplyToExecutor(e *task.Executor) {
|
|||||||
|
|
||||||
e.Options(
|
e.Options(
|
||||||
task.WithDir(dir),
|
task.WithDir(dir),
|
||||||
|
task.WithEntrypoint(Entrypoint),
|
||||||
task.WithForce(Force),
|
task.WithForce(Force),
|
||||||
task.WithForceAll(ForceAll),
|
task.WithForceAll(ForceAll),
|
||||||
|
task.WithInsecure(Insecure),
|
||||||
|
task.WithDownload(Download),
|
||||||
|
task.WithOffline(Offline),
|
||||||
|
task.WithTimeout(Timeout),
|
||||||
|
task.WithCacheExpiryDuration(CacheExpiryDuration),
|
||||||
task.WithWatch(Watch),
|
task.WithWatch(Watch),
|
||||||
task.WithVerbose(Verbose),
|
task.WithVerbose(Verbose),
|
||||||
task.WithSilent(Silent),
|
task.WithSilent(Silent),
|
||||||
@@ -247,11 +256,15 @@ func (o *flagsOption) ApplyToExecutor(e *task.Executor) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *flagsOption) ApplyToReader(r *taskfile.Reader) {
|
// getConfig extracts a config value directly from a pointer field with a fallback default
|
||||||
r.Options(
|
func getConfig[T any](config *taskrcast.TaskRC, fieldFunc func() *T, fallback T) T {
|
||||||
taskfile.WithInsecure(Insecure),
|
if config == nil {
|
||||||
taskfile.WithDownload(Download),
|
return fallback
|
||||||
taskfile.WithOffline(Offline),
|
}
|
||||||
taskfile.WithCacheExpiryDuration(CacheExpiryDuration),
|
|
||||||
)
|
field := fieldFunc()
|
||||||
|
if field != nil {
|
||||||
|
return *field
|
||||||
|
}
|
||||||
|
return fallback
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,51 +37,87 @@ func DefaultDir(entrypoint, dir string) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search will look for files with the given possible filenames using the given
|
// ResolveDir returns an absolute path to the directory that the task should be
|
||||||
// entrypoint and directory. If the entrypoint is set, it will check if the
|
// 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
|
// 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
|
// possible filenames. Otherwise, it walks up the file tree starting at the
|
||||||
// given directory and perform a search in each directory for the possible
|
// 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
|
// 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
|
// entrypoint and directory are both empty, it defaults the directory to the
|
||||||
// current working directory and perform a recursive search starting there. If a
|
// current working directory and performs a recursive search starting there. If
|
||||||
// match is found, the absolute path to the file will be returned with its
|
// a match is found, the absolute path to the file is returned with its
|
||||||
// directory. If no match is found, an error will be returned.
|
// directory. If no match is found, an error is returned.
|
||||||
func Search(entrypoint, dir string, possibleFilenames []string) (string, string, error) {
|
func Search(entrypoint, dir string, possibleFilenames []string) (string, error) {
|
||||||
var err error
|
var err error
|
||||||
if entrypoint != "" {
|
if entrypoint != "" {
|
||||||
entrypoint, err = SearchPath(entrypoint, possibleFilenames)
|
entrypoint, err = SearchPath(entrypoint, possibleFilenames)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", err
|
||||||
}
|
}
|
||||||
if dir == "" {
|
return entrypoint, nil
|
||||||
dir = filepath.Dir(entrypoint)
|
|
||||||
} else {
|
|
||||||
dir, err = filepath.Abs(dir)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return entrypoint, dir, nil
|
|
||||||
}
|
}
|
||||||
if dir == "" {
|
if dir == "" {
|
||||||
dir, err = os.Getwd()
|
dir, err = os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
entrypoint, err = SearchPathRecursively(dir, possibleFilenames)
|
entrypoint, err = SearchPathRecursively(dir, possibleFilenames)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", err
|
||||||
}
|
}
|
||||||
dir = filepath.Dir(entrypoint)
|
return entrypoint, nil
|
||||||
return entrypoint, dir, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search will check if a file at the given path exists or not. If it does, it
|
// SearchAll looks for files with the given possible filenames using the given
|
||||||
// will return the path to it. If it does not, it will search for any files at
|
// entrypoint and directory. If the entrypoint is set, it checks if the
|
||||||
// the given path with any of the given possible names. If any of these match a
|
// entrypoint matches a file or if it matches a directory containing one of the
|
||||||
// file, the first matching path will be returned. If no files are found, an
|
// 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.
|
// error will be returned.
|
||||||
func SearchPath(path string, possibleFilenames []string) (string, error) {
|
func SearchPath(path string, possibleFilenames []string) (string, error) {
|
||||||
// Get file info about the path
|
// Get file info about the path
|
||||||
@@ -111,36 +147,56 @@ func SearchPath(path string, possibleFilenames []string) (string, error) {
|
|||||||
return "", os.ErrNotExist
|
return "", os.ErrNotExist
|
||||||
}
|
}
|
||||||
|
|
||||||
// SearchRecursively will check if a file at the given path exists by calling
|
// SearchPathRecursively walks up the directory tree starting at the given
|
||||||
// the exists function. If a file is not found, it will walk up the directory
|
// path, calling the Search function in each directory until it finds a matching
|
||||||
// tree calling the Search function until it finds a file or reaches the root
|
// file or reaches the root directory. On supported operating systems, it will
|
||||||
// directory. On supported operating systems, it will also check if the user ID
|
// also check if the user ID of the directory changes and abort if it does.
|
||||||
// of the directory changes and abort if it does.
|
|
||||||
func SearchPathRecursively(path string, possibleFilenames []string) (string, error) {
|
func SearchPathRecursively(path string, possibleFilenames []string) (string, error) {
|
||||||
owner, err := sysinfo.Owner(path)
|
paths, err := SearchNPathRecursively(path, possibleFilenames, 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
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)
|
fpath, err := SearchPath(path, possibleFilenames)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return fpath, nil
|
paths = append(paths, fpath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the parent path/user id
|
// Get the parent path/user id
|
||||||
parentPath := filepath.Dir(path)
|
parentPath := filepath.Dir(path)
|
||||||
parentOwner, err := sysinfo.Owner(parentPath)
|
parentOwner, err := sysinfo.Owner(parentPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error if we reached the root directory and still haven't found a file
|
// Error if we reached the root directory and still haven't found a file
|
||||||
// OR if the user id of the directory changes
|
// OR if the user id of the directory changes
|
||||||
if path == parentPath || (parentOwner != owner) {
|
if path == parentPath || (parentOwner != owner) {
|
||||||
return "", os.ErrNotExist
|
return paths, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
owner = parentOwner
|
owner = parentOwner
|
||||||
path = parentPath
|
path = parentPath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return paths, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,35 +71,30 @@ func TestSearch(t *testing.T) {
|
|||||||
dir string
|
dir string
|
||||||
possibleFilenames []string
|
possibleFilenames []string
|
||||||
expectedEntrypoint string
|
expectedEntrypoint string
|
||||||
expectedDir string
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "find foo.txt using relative entrypoint",
|
name: "find foo.txt using relative entrypoint",
|
||||||
entrypoint: "./testdata/foo.txt",
|
entrypoint: "./testdata/foo.txt",
|
||||||
possibleFilenames: []string{"foo.txt"},
|
possibleFilenames: []string{"foo.txt"},
|
||||||
expectedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
expectedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||||
expectedDir: filepath.Join(wd, "testdata"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "find foo.txt using absolute entrypoint",
|
name: "find foo.txt using absolute entrypoint",
|
||||||
entrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
entrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||||
possibleFilenames: []string{"foo.txt"},
|
possibleFilenames: []string{"foo.txt"},
|
||||||
expectedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
expectedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||||
expectedDir: filepath.Join(wd, "testdata"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "find foo.txt using relative dir",
|
name: "find foo.txt using relative dir",
|
||||||
dir: "./testdata",
|
dir: "./testdata",
|
||||||
possibleFilenames: []string{"foo.txt"},
|
possibleFilenames: []string{"foo.txt"},
|
||||||
expectedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
expectedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||||
expectedDir: filepath.Join(wd, "testdata"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "find foo.txt using absolute dir",
|
name: "find foo.txt using absolute dir",
|
||||||
dir: filepath.Join(wd, "testdata"),
|
dir: filepath.Join(wd, "testdata"),
|
||||||
possibleFilenames: []string{"foo.txt"},
|
possibleFilenames: []string{"foo.txt"},
|
||||||
expectedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
expectedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||||
expectedDir: filepath.Join(wd, "testdata"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "find foo.txt using relative dir and relative entrypoint",
|
name: "find foo.txt using relative dir and relative entrypoint",
|
||||||
@@ -107,7 +102,6 @@ func TestSearch(t *testing.T) {
|
|||||||
dir: "./testdata/some/other/dir",
|
dir: "./testdata/some/other/dir",
|
||||||
possibleFilenames: []string{"foo.txt"},
|
possibleFilenames: []string{"foo.txt"},
|
||||||
expectedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
expectedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||||
expectedDir: filepath.Join(wd, "testdata", "some", "other", "dir"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "find fs.go using no entrypoint or dir",
|
name: "find fs.go using no entrypoint or dir",
|
||||||
@@ -115,7 +109,6 @@ func TestSearch(t *testing.T) {
|
|||||||
dir: "",
|
dir: "",
|
||||||
possibleFilenames: []string{"fs.go"},
|
possibleFilenames: []string{"fs.go"},
|
||||||
expectedEntrypoint: filepath.Join(wd, "fs.go"),
|
expectedEntrypoint: filepath.Join(wd, "fs.go"),
|
||||||
expectedDir: wd,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "find ../../Taskfile.yml using no entrypoint or dir by walking",
|
name: "find ../../Taskfile.yml using no entrypoint or dir by walking",
|
||||||
@@ -123,30 +116,109 @@ func TestSearch(t *testing.T) {
|
|||||||
dir: "",
|
dir: "",
|
||||||
possibleFilenames: []string{"Taskfile.yml"},
|
possibleFilenames: []string{"Taskfile.yml"},
|
||||||
expectedEntrypoint: filepath.Join(wd, "..", "..", "Taskfile.yml"),
|
expectedEntrypoint: filepath.Join(wd, "..", "..", "Taskfile.yml"),
|
||||||
expectedDir: filepath.Join(wd, "..", ".."),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "find foo.txt first if listed first in possible filenames",
|
name: "find foo.txt first if listed first in possible filenames",
|
||||||
entrypoint: "./testdata",
|
entrypoint: "./testdata",
|
||||||
possibleFilenames: []string{"foo.txt", "bar.txt"},
|
possibleFilenames: []string{"foo.txt", "bar.txt"},
|
||||||
expectedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
expectedEntrypoint: filepath.Join(wd, "testdata", "foo.txt"),
|
||||||
expectedDir: filepath.Join(wd, "testdata"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "find bar.txt first if listed first in possible filenames",
|
name: "find bar.txt first if listed first in possible filenames",
|
||||||
entrypoint: "./testdata",
|
entrypoint: "./testdata",
|
||||||
possibleFilenames: []string{"bar.txt", "foo.txt"},
|
possibleFilenames: []string{"bar.txt", "foo.txt"},
|
||||||
expectedEntrypoint: filepath.Join(wd, "testdata", "bar.txt"),
|
expectedEntrypoint: filepath.Join(wd, "testdata", "bar.txt"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
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"),
|
expectedDir: filepath.Join(wd, "testdata"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
entrypoint, dir, err := Search(tt.entrypoint, tt.dir, tt.possibleFilenames)
|
dir, err := ResolveDir(tt.entrypoint, tt.resolvedEntrypoint, tt.dir)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, tt.expectedEntrypoint, entrypoint)
|
|
||||||
require.Equal(t, tt.expectedDir, dir)
|
require.Equal(t, tt.expectedDir, dir)
|
||||||
|
require.NoError(t, err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ func (g Group) WrapWriter(stdOut, _ io.Writer, _ string, cache *templater.Cache)
|
|||||||
if g.ErrorOnly && err == nil {
|
if g.ErrorOnly && err == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return gw.close()
|
return gw.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -40,14 +39,22 @@ func (gw *groupWriter) Write(p []byte) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gw *groupWriter) close() error {
|
func (gw *groupWriter) close() error {
|
||||||
if gw.buff.Len() == 0 {
|
switch {
|
||||||
// don't print begin/end messages if there's no buffered entries
|
case gw.buff.Len() == 0:
|
||||||
return nil
|
return nil
|
||||||
}
|
case gw.begin == "" && gw.end == "":
|
||||||
if _, err := io.WriteString(gw.writer, gw.begin); err != nil {
|
_, err := io.Copy(gw.writer, &gw.buff)
|
||||||
|
return err
|
||||||
|
default:
|
||||||
|
_, err := io.Copy(gw.writer, gw.combinedBuff())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
gw.buff.WriteString(gw.end)
|
}
|
||||||
_, err := io.Copy(gw.writer, &gw.buff)
|
|
||||||
return err
|
func (gw *groupWriter) combinedBuff() io.Reader {
|
||||||
|
var b bytes.Buffer
|
||||||
|
_, _ = b.WriteString(gw.begin)
|
||||||
|
_, _ = io.Copy(&b, &gw.buff)
|
||||||
|
_, _ = b.WriteString(gw.end)
|
||||||
|
return &b
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"gopkg.in/yaml.v3"
|
"go.yaml.in/yaml/v4"
|
||||||
"mvdan.cc/sh/v3/shell"
|
"mvdan.cc/sh/v3/shell"
|
||||||
"mvdan.cc/sh/v3/syntax"
|
"mvdan.cc/sh/v3/syntax"
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
3.44.1
|
3.45.5
|
||||||
|
|||||||
32
package-lock.json
generated
32
package-lock.json
generated
@@ -1,32 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@go-task/cli",
|
|
||||||
"version": "3.44.1",
|
|
||||||
"lockfileVersion": 2,
|
|
||||||
"requires": true,
|
|
||||||
"packages": {
|
|
||||||
"": {
|
|
||||||
"name": "@go-task/cli",
|
|
||||||
"version": "3.26.0",
|
|
||||||
"hasInstallScript": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@go-task/go-npm": "^0.2.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@go-task/go-npm": {
|
|
||||||
"version": "0.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@go-task/go-npm/-/go-npm-0.2.0.tgz",
|
|
||||||
"integrity": "sha512-vQbdtBvesHm8EUFHX8QKg4rbBodmu9VsAXH1ozpbiN5jdTMOYHTCMM31EurAYmY+rNNtxJQ4JGy6t383RPlqbw==",
|
|
||||||
"bin": {
|
|
||||||
"go-npm": "bin/index.js"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@go-task/go-npm": {
|
|
||||||
"version": "0.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@go-task/go-npm/-/go-npm-0.2.0.tgz",
|
|
||||||
"integrity": "sha512-vQbdtBvesHm8EUFHX8QKg4rbBodmu9VsAXH1ozpbiN5jdTMOYHTCMM31EurAYmY+rNNtxJQ4JGy6t383RPlqbw=="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
34
package.json
34
package.json
@@ -1,34 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@go-task/cli",
|
|
||||||
"version": "3.44.1",
|
|
||||||
"description": "A task runner / simpler Make alternative written in Go",
|
|
||||||
"scripts": {
|
|
||||||
"postinstall": "go-npm install",
|
|
||||||
"preuninstall": "go-npm uninstall"
|
|
||||||
},
|
|
||||||
"goBinary": {
|
|
||||||
"name": "task",
|
|
||||||
"path": "./bin",
|
|
||||||
"url": "https://github.com/go-task/task/releases/download/v{{version}}/task_{{platform}}_{{arch}}{{archive_ext}}"
|
|
||||||
},
|
|
||||||
"files": [],
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/go-task/task.git"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"task",
|
|
||||||
"taskfile",
|
|
||||||
"build-tool",
|
|
||||||
"task-runner"
|
|
||||||
],
|
|
||||||
"author": "The Task authors",
|
|
||||||
"license": "MIT",
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/go-task/task/issues"
|
|
||||||
},
|
|
||||||
"homepage": "https://taskfile.dev",
|
|
||||||
"dependencies": {
|
|
||||||
"@go-task/go-npm": "^0.2.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,13 +4,19 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"slices"
|
"slices"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/Masterminds/semver/v3"
|
"github.com/Masterminds/semver/v3"
|
||||||
"github.com/sajari/fuzzy"
|
"github.com/sajari/fuzzy"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/errors"
|
"github.com/go-task/task/v3/errors"
|
||||||
|
"github.com/go-task/task/v3/internal/env"
|
||||||
|
"github.com/go-task/task/v3/internal/execext"
|
||||||
|
"github.com/go-task/task/v3/internal/filepathext"
|
||||||
|
"github.com/go-task/task/v3/internal/fsext"
|
||||||
"github.com/go-task/task/v3/internal/logger"
|
"github.com/go-task/task/v3/internal/logger"
|
||||||
"github.com/go-task/task/v3/internal/output"
|
"github.com/go-task/task/v3/internal/output"
|
||||||
"github.com/go-task/task/v3/internal/version"
|
"github.com/go-task/task/v3/internal/version"
|
||||||
@@ -18,8 +24,18 @@ import (
|
|||||||
"github.com/go-task/task/v3/taskfile/ast"
|
"github.com/go-task/task/v3/taskfile/ast"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (e *Executor) setup() error {
|
func (e *Executor) Setup() error {
|
||||||
e.setupLogger()
|
e.setupLogger()
|
||||||
|
node, err := e.getRootNode()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := e.setupTempDir(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := e.readTaskfile(node); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
e.setupFuzzyModel()
|
e.setupFuzzyModel()
|
||||||
e.setupStdFiles()
|
e.setupStdFiles()
|
||||||
if err := e.setupOutput(); err != nil {
|
if err := e.setupOutput(); err != nil {
|
||||||
@@ -39,6 +55,53 @@ func (e *Executor) setup() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *Executor) getRootNode() (taskfile.Node, error) {
|
||||||
|
node, err := taskfile.NewRootNode(e.Entrypoint, e.Dir, e.Insecure, e.Timeout)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil, errors.TaskfileNotFoundError{
|
||||||
|
URI: fsext.DefaultDir(e.Entrypoint, e.Dir),
|
||||||
|
Walk: true,
|
||||||
|
AskInit: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
e.Dir = node.Dir()
|
||||||
|
return node, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Executor) readTaskfile(node taskfile.Node) error {
|
||||||
|
ctx, cf := context.WithTimeout(context.Background(), e.Timeout)
|
||||||
|
defer cf()
|
||||||
|
debugFunc := func(s string) {
|
||||||
|
e.Logger.VerboseOutf(logger.Magenta, s)
|
||||||
|
}
|
||||||
|
promptFunc := func(s string) error {
|
||||||
|
return e.Logger.Prompt(logger.Yellow, s, "n", "y", "yes")
|
||||||
|
}
|
||||||
|
reader := taskfile.NewReader(
|
||||||
|
taskfile.WithInsecure(e.Insecure),
|
||||||
|
taskfile.WithDownload(e.Download),
|
||||||
|
taskfile.WithOffline(e.Offline),
|
||||||
|
taskfile.WithTempDir(e.TempDir.Remote),
|
||||||
|
taskfile.WithCacheExpiryDuration(e.CacheExpiryDuration),
|
||||||
|
taskfile.WithDebugFunc(debugFunc),
|
||||||
|
taskfile.WithPromptFunc(promptFunc),
|
||||||
|
)
|
||||||
|
graph, err := reader.Read(ctx, node)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, context.DeadlineExceeded) {
|
||||||
|
return &errors.TaskfileNetworkTimeoutError{URI: node.Location(), Timeout: e.Timeout}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if e.Taskfile, err = graph.Merge(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (e *Executor) setupFuzzyModel() {
|
func (e *Executor) setupFuzzyModel() {
|
||||||
if e.Taskfile == nil {
|
if e.Taskfile == nil {
|
||||||
return
|
return
|
||||||
@@ -60,6 +123,52 @@ func (e *Executor) setupFuzzyModel() {
|
|||||||
e.fuzzyModel = model
|
e.fuzzyModel = model
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *Executor) setupTempDir() error {
|
||||||
|
if e.TempDir != (TempDir{}) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tempDir := env.GetTaskEnv("TEMP_DIR")
|
||||||
|
if tempDir == "" {
|
||||||
|
e.TempDir = TempDir{
|
||||||
|
Remote: filepathext.SmartJoin(e.Dir, ".task"),
|
||||||
|
Fingerprint: filepathext.SmartJoin(e.Dir, ".task"),
|
||||||
|
}
|
||||||
|
} else if filepath.IsAbs(tempDir) || strings.HasPrefix(tempDir, "~") {
|
||||||
|
tempDir, err := execext.ExpandLiteral(tempDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
projectDir, _ := filepath.Abs(e.Dir)
|
||||||
|
projectName := filepath.Base(projectDir)
|
||||||
|
e.TempDir = TempDir{
|
||||||
|
Remote: tempDir,
|
||||||
|
Fingerprint: filepathext.SmartJoin(tempDir, projectName),
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
e.TempDir = TempDir{
|
||||||
|
Remote: filepathext.SmartJoin(e.Dir, tempDir),
|
||||||
|
Fingerprint: filepathext.SmartJoin(e.Dir, tempDir),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
remoteDir := env.GetTaskEnv("REMOTE_DIR")
|
||||||
|
if remoteDir != "" {
|
||||||
|
if filepath.IsAbs(remoteDir) || strings.HasPrefix(remoteDir, "~") {
|
||||||
|
remoteTempDir, err := execext.ExpandLiteral(remoteDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
e.TempDir.Remote = remoteTempDir
|
||||||
|
} else {
|
||||||
|
e.TempDir.Remote = filepathext.SmartJoin(e.Dir, ".task")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (e *Executor) setupStdFiles() {
|
func (e *Executor) setupStdFiles() {
|
||||||
if e.Stdin == nil {
|
if e.Stdin == nil {
|
||||||
e.Stdin = os.Stdin
|
e.Stdin = os.Stdin
|
||||||
@@ -105,7 +214,7 @@ func (e *Executor) setupCompiler() error {
|
|||||||
|
|
||||||
e.Compiler = &Compiler{
|
e.Compiler = &Compiler{
|
||||||
Dir: e.Dir,
|
Dir: e.Dir,
|
||||||
Entrypoint: e.Taskfile.Location,
|
Entrypoint: e.Entrypoint,
|
||||||
UserWorkingDir: e.UserWorkingDir,
|
UserWorkingDir: e.UserWorkingDir,
|
||||||
TaskfileEnv: e.Taskfile.Env,
|
TaskfileEnv: e.Taskfile.Env,
|
||||||
TaskfileVars: e.Taskfile.Vars,
|
TaskfileVars: e.Taskfile.Vars,
|
||||||
28
task.go
28
task.go
@@ -150,7 +150,7 @@ func (e *Executor) RunTask(ctx context.Context, call *Call) error {
|
|||||||
release := e.acquireConcurrencyLimit()
|
release := e.acquireConcurrencyLimit()
|
||||||
defer release()
|
defer release()
|
||||||
|
|
||||||
return e.startExecution(ctx, t, func(ctx context.Context) error {
|
if err = e.startExecution(ctx, t, func(ctx context.Context) error {
|
||||||
e.Logger.VerboseErrf(logger.Magenta, "task: %q started\n", call.Task)
|
e.Logger.VerboseErrf(logger.Magenta, "task: %q started\n", call.Task)
|
||||||
if err := e.runDeps(ctx, t); err != nil {
|
if err := e.runDeps(ctx, t); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -172,7 +172,6 @@ func (e *Executor) RunTask(ctx context.Context, call *Call) error {
|
|||||||
if t.Method != "" {
|
if t.Method != "" {
|
||||||
method = t.Method
|
method = t.Method
|
||||||
}
|
}
|
||||||
|
|
||||||
upToDate, err := fingerprint.IsTaskUpToDate(ctx, t,
|
upToDate, err := fingerprint.IsTaskUpToDate(ctx, t,
|
||||||
fingerprint.WithMethod(method),
|
fingerprint.WithMethod(method),
|
||||||
fingerprint.WithTempDir(e.TempDir.Fingerprint),
|
fingerprint.WithTempDir(e.TempDir.Fingerprint),
|
||||||
@@ -211,7 +210,7 @@ func (e *Executor) RunTask(ctx context.Context, call *Call) error {
|
|||||||
|
|
||||||
for i := range t.Cmds {
|
for i := range t.Cmds {
|
||||||
if t.Cmds[i].Defer {
|
if t.Cmds[i].Defer {
|
||||||
defer e.runDeferred(t, call, i, &deferredExitCode)
|
defer e.runDeferred(t, call, i, t.Vars, &deferredExitCode)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,16 +228,16 @@ func (e *Executor) RunTask(ctx context.Context, call *Call) error {
|
|||||||
deferredExitCode = uint8(exitCode)
|
deferredExitCode = uint8(exitCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
if call.Indirect {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &errors.TaskRunError{TaskName: t.Task, Err: err}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
e.Logger.VerboseErrf(logger.Magenta, "task: %q finished\n", call.Task)
|
e.Logger.VerboseErrf(logger.Magenta, "task: %q finished\n", call.Task)
|
||||||
return nil
|
return nil
|
||||||
})
|
}); err != nil {
|
||||||
|
return &errors.TaskRunError{TaskName: t.Name(), Err: err}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Executor) mkdir(t *ast.Task) error {
|
func (e *Executor) mkdir(t *ast.Task) error {
|
||||||
@@ -278,17 +277,11 @@ func (e *Executor) runDeps(ctx context.Context, t *ast.Task) error {
|
|||||||
return g.Wait()
|
return g.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Executor) runDeferred(t *ast.Task, call *Call, i int, deferredExitCode *uint8) {
|
func (e *Executor) runDeferred(t *ast.Task, call *Call, i int, vars *ast.Vars, deferredExitCode *uint8) {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
origTask, err := e.GetTask(call)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd := t.Cmds[i]
|
cmd := t.Cmds[i]
|
||||||
vars, _ := e.Compiler.GetVariables(origTask, call)
|
|
||||||
cache := &templater.Cache{Vars: vars}
|
cache := &templater.Cache{Vars: vars}
|
||||||
extra := map[string]any{}
|
extra := map[string]any{}
|
||||||
|
|
||||||
@@ -467,7 +460,6 @@ func (e *Executor) GetTask(call *Call) (*ast.Task, error) {
|
|||||||
DidYouMean: didYouMean,
|
DidYouMean: didYouMean,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return matchingTask, nil
|
return matchingTask, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -500,7 +492,7 @@ func (e *Executor) GetTaskList(filters ...FilterFunc) ([]*ast.Task, error) {
|
|||||||
// Compile the list of tasks
|
// Compile the list of tasks
|
||||||
for i := range tasks {
|
for i := range tasks {
|
||||||
g.Go(func() error {
|
g.Go(func() error {
|
||||||
compiledTask, err := e.FastCompiledTask(&Call{Task: tasks[i].Task})
|
compiledTask, err := e.CompiledTaskForTaskList(&Call{Task: tasks[i].Task})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
4523
task_test.go
4523
task_test.go
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
|||||||
package ast
|
package ast
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gopkg.in/yaml.v3"
|
"go.yaml.in/yaml/v4"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/errors"
|
"github.com/go-task/task/v3/errors"
|
||||||
"github.com/go-task/task/v3/internal/deepcopy"
|
"github.com/go-task/task/v3/internal/deepcopy"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package ast
|
package ast
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gopkg.in/yaml.v3"
|
"go.yaml.in/yaml/v4"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/errors"
|
"github.com/go-task/task/v3/errors"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package ast
|
package ast
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gopkg.in/yaml.v3"
|
"go.yaml.in/yaml/v4"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/errors"
|
"github.com/go-task/task/v3/errors"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package ast
|
package ast
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gopkg.in/yaml.v3"
|
"go.yaml.in/yaml/v4"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/errors"
|
"github.com/go-task/task/v3/errors"
|
||||||
"github.com/go-task/task/v3/internal/deepcopy"
|
"github.com/go-task/task/v3/internal/deepcopy"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package ast
|
package ast
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gopkg.in/yaml.v3"
|
"go.yaml.in/yaml/v4"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/errors"
|
"github.com/go-task/task/v3/errors"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/elliotchance/orderedmap/v3"
|
"github.com/elliotchance/orderedmap/v3"
|
||||||
"gopkg.in/yaml.v3"
|
"go.yaml.in/yaml/v4"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/errors"
|
"github.com/go-task/task/v3/errors"
|
||||||
"github.com/go-task/task/v3/internal/deepcopy"
|
"github.com/go-task/task/v3/internal/deepcopy"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"iter"
|
"iter"
|
||||||
|
|
||||||
"github.com/elliotchance/orderedmap/v3"
|
"github.com/elliotchance/orderedmap/v3"
|
||||||
"gopkg.in/yaml.v3"
|
"go.yaml.in/yaml/v4"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/errors"
|
"github.com/go-task/task/v3/errors"
|
||||||
"github.com/go-task/task/v3/internal/deepcopy"
|
"github.com/go-task/task/v3/internal/deepcopy"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package ast
|
package ast
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gopkg.in/yaml.v3"
|
"go.yaml.in/yaml/v4"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/errors"
|
"github.com/go-task/task/v3/errors"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
"go.yaml.in/yaml/v4"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/errors"
|
"github.com/go-task/task/v3/errors"
|
||||||
"github.com/go-task/task/v3/internal/goext"
|
"github.com/go-task/task/v3/internal/goext"
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package ast
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
"go.yaml.in/yaml/v4"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/errors"
|
"github.com/go-task/task/v3/errors"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"gopkg.in/yaml.v3"
|
"go.yaml.in/yaml/v4"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/taskfile/ast"
|
"github.com/go-task/task/v3/taskfile/ast"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package ast
|
package ast
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gopkg.in/yaml.v3"
|
"go.yaml.in/yaml/v4"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/errors"
|
"github.com/go-task/task/v3/errors"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package ast
|
package ast
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gopkg.in/yaml.v3"
|
"go.yaml.in/yaml/v4"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/errors"
|
"github.com/go-task/task/v3/errors"
|
||||||
"github.com/go-task/task/v3/internal/deepcopy"
|
"github.com/go-task/task/v3/internal/deepcopy"
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
"go.yaml.in/yaml/v4"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/errors"
|
"github.com/go-task/task/v3/errors"
|
||||||
"github.com/go-task/task/v3/internal/deepcopy"
|
"github.com/go-task/task/v3/internal/deepcopy"
|
||||||
@@ -46,17 +46,22 @@ type Task struct {
|
|||||||
Namespace string
|
Namespace string
|
||||||
IncludeVars *Vars
|
IncludeVars *Vars
|
||||||
IncludedTaskfileVars *Vars
|
IncludedTaskfileVars *Vars
|
||||||
|
|
||||||
|
FullName string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Task) Name() string {
|
func (t *Task) Name() string {
|
||||||
if t.Label != "" {
|
if t.Label != "" {
|
||||||
return t.Label
|
return t.Label
|
||||||
}
|
}
|
||||||
|
if t.FullName != "" {
|
||||||
|
return t.FullName
|
||||||
|
}
|
||||||
return t.Task
|
return t.Task
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Task) LocalName() string {
|
func (t *Task) LocalName() string {
|
||||||
name := t.Task
|
name := t.FullName
|
||||||
name = strings.TrimPrefix(name, t.Namespace)
|
name = strings.TrimPrefix(name, t.Namespace)
|
||||||
name = strings.TrimPrefix(name, ":")
|
name = strings.TrimPrefix(name, ":")
|
||||||
return name
|
return name
|
||||||
@@ -220,6 +225,7 @@ func (t *Task) DeepCopy() *Task {
|
|||||||
Location: t.Location.DeepCopy(),
|
Location: t.Location.DeepCopy(),
|
||||||
Requires: t.Requires.DeepCopy(),
|
Requires: t.Requires.DeepCopy(),
|
||||||
Namespace: t.Namespace,
|
Namespace: t.Namespace,
|
||||||
|
FullName: t.FullName,
|
||||||
}
|
}
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Masterminds/semver/v3"
|
"github.com/Masterminds/semver/v3"
|
||||||
"gopkg.in/yaml.v3"
|
"go.yaml.in/yaml/v4"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/errors"
|
"github.com/go-task/task/v3/errors"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"gopkg.in/yaml.v3"
|
"go.yaml.in/yaml/v4"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/taskfile/ast"
|
"github.com/go-task/task/v3/taskfile/ast"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/elliotchance/orderedmap/v3"
|
"github.com/elliotchance/orderedmap/v3"
|
||||||
"gopkg.in/yaml.v3"
|
"go.yaml.in/yaml/v4"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/errors"
|
"github.com/go-task/task/v3/errors"
|
||||||
"github.com/go-task/task/v3/internal/filepathext"
|
"github.com/go-task/task/v3/internal/filepathext"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package ast
|
package ast
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gopkg.in/yaml.v3"
|
"go.yaml.in/yaml/v4"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/errors"
|
"github.com/go-task/task/v3/errors"
|
||||||
)
|
)
|
||||||
@@ -18,7 +18,10 @@ type Var struct {
|
|||||||
func (v *Var) UnmarshalYAML(node *yaml.Node) error {
|
func (v *Var) UnmarshalYAML(node *yaml.Node) error {
|
||||||
switch node.Kind {
|
switch node.Kind {
|
||||||
case yaml.MappingNode:
|
case yaml.MappingNode:
|
||||||
key := node.Content[0].Value
|
key := "<none>"
|
||||||
|
if len(node.Content) > 0 {
|
||||||
|
key = node.Content[0].Value
|
||||||
|
}
|
||||||
switch key {
|
switch key {
|
||||||
case "sh", "ref", "map":
|
case "sh", "ref", "map":
|
||||||
var m struct {
|
var m struct {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/elliotchance/orderedmap/v3"
|
"github.com/elliotchance/orderedmap/v3"
|
||||||
"gopkg.in/yaml.v3"
|
"go.yaml.in/yaml/v4"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/errors"
|
"github.com/go-task/task/v3/errors"
|
||||||
"github.com/go-task/task/v3/internal/deepcopy"
|
"github.com/go-task/task/v3/internal/deepcopy"
|
||||||
@@ -113,7 +113,7 @@ func (vars *Vars) ToCacheMap() (m map[string]any) {
|
|||||||
m[k] = v.Value
|
m[k] = v.Value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge loops over other and merges it values with the variables in vars. If
|
// Merge loops over other and merges it values with the variables in vars. If
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package taskfile
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
giturls "github.com/chainguard-dev/git-urls"
|
giturls "github.com/chainguard-dev/git-urls"
|
||||||
|
|
||||||
@@ -32,6 +33,7 @@ func NewRootNode(
|
|||||||
entrypoint string,
|
entrypoint string,
|
||||||
dir string,
|
dir string,
|
||||||
insecure bool,
|
insecure bool,
|
||||||
|
timeout time.Duration,
|
||||||
) (Node, error) {
|
) (Node, error) {
|
||||||
dir = fsext.DefaultDir(entrypoint, dir)
|
dir = fsext.DefaultDir(entrypoint, dir)
|
||||||
// If the entrypoint is "-", we read from stdin
|
// If the entrypoint is "-", we read from stdin
|
||||||
@@ -70,6 +72,16 @@ func NewNode(
|
|||||||
return node, err
|
return node, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isRemoteEntrypoint(entrypoint string) bool {
|
||||||
|
scheme, _ := getScheme(entrypoint)
|
||||||
|
switch scheme {
|
||||||
|
case "git", "http", "https":
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func getScheme(uri string) (string, error) {
|
func getScheme(uri string) (string, error) {
|
||||||
u, err := giturls.Parse(uri)
|
u, err := giturls.Parse(uri)
|
||||||
if u == nil {
|
if u == nil {
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
|
||||||
|
|
||||||
|
"github.com/go-task/task/v3/errors"
|
||||||
"github.com/go-task/task/v3/internal/execext"
|
"github.com/go-task/task/v3/internal/execext"
|
||||||
"github.com/go-task/task/v3/internal/filepathext"
|
"github.com/go-task/task/v3/internal/filepathext"
|
||||||
"github.com/go-task/task/v3/internal/fsext"
|
"github.com/go-task/task/v3/internal/fsext"
|
||||||
@@ -18,15 +18,24 @@ type FileNode struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewFileNode(entrypoint, dir string, opts ...NodeOption) (*FileNode, error) {
|
func NewFileNode(entrypoint, dir string, opts ...NodeOption) (*FileNode, error) {
|
||||||
var err error
|
// Find the entrypoint file
|
||||||
base := NewBaseNode(dir, opts...)
|
resolvedEntrypoint, err := fsext.Search(entrypoint, dir, defaultTaskfiles)
|
||||||
entrypoint, base.dir, err = fsext.Search(entrypoint, base.dir, defaultTaskfiles)
|
if err != nil {
|
||||||
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
|
return nil, errors.TaskfileNotFoundError{URI: entrypoint, Walk: false}
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve the directory
|
||||||
|
resolvedDir, err := fsext.ResolveDir(entrypoint, resolvedEntrypoint, dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &FileNode{
|
return &FileNode{
|
||||||
baseNode: base,
|
baseNode: NewBaseNode(resolvedDir, opts...),
|
||||||
entrypoint: entrypoint,
|
entrypoint: resolvedEntrypoint,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,10 +54,7 @@ func (node *FileNode) Read() ([]byte, error) {
|
|||||||
|
|
||||||
func (node *FileNode) ResolveEntrypoint(entrypoint string) (string, error) {
|
func (node *FileNode) ResolveEntrypoint(entrypoint string) (string, error) {
|
||||||
// If the file is remote, we don't need to resolve the path
|
// If the file is remote, we don't need to resolve the path
|
||||||
if strings.Contains(entrypoint, "://") {
|
if isRemoteEntrypoint(entrypoint) {
|
||||||
return entrypoint, nil
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(entrypoint, "git") {
|
|
||||||
return entrypoint, nil
|
return entrypoint, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -98,6 +98,11 @@ func (node *GitNode) ReadContext(_ context.Context) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (node *GitNode) ResolveEntrypoint(entrypoint string) (string, error) {
|
func (node *GitNode) ResolveEntrypoint(entrypoint string) (string, error) {
|
||||||
|
// If the file is remote, we don't need to resolve the path
|
||||||
|
if isRemoteEntrypoint(entrypoint) {
|
||||||
|
return entrypoint, nil
|
||||||
|
}
|
||||||
|
|
||||||
dir, _ := filepath.Split(node.path)
|
dir, _ := filepath.Split(node.path)
|
||||||
resolvedEntrypoint := fmt.Sprintf("%s//%s", node.url, filepath.Join(dir, entrypoint))
|
resolvedEntrypoint := fmt.Sprintf("%s//%s", node.url, filepath.Join(dir, entrypoint))
|
||||||
if node.ref != "" {
|
if node.ref != "" {
|
||||||
|
|||||||
@@ -21,6 +21,17 @@ func TestGitNode_ssh(t *testing.T) {
|
|||||||
assert.Equal(t, "ssh://git@github.com/foo/bar.git//common.yml?ref=main", entrypoint)
|
assert.Equal(t, "ssh://git@github.com/foo/bar.git//common.yml?ref=main", entrypoint)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGitNode_sshWithAltRepo(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
node, err := NewGitNode("git@github.com:foo/bar.git//Taskfile.yml?ref=main", "", false)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
entrypoint, err := node.ResolveEntrypoint("git@github.com:foo/other.git//Taskfile.yml?ref=dev")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "git@github.com:foo/other.git//Taskfile.yml?ref=dev", entrypoint)
|
||||||
|
}
|
||||||
|
|
||||||
func TestGitNode_sshWithDir(t *testing.T) {
|
func TestGitNode_sshWithDir(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ func (node *HTTPNode) ReadContext(ctx context.Context) ([]byte, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
req, err := http.NewRequest("GET", url.String(), nil)
|
req, err := http.NewRequestWithContext(ctx, "GET", url.String(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.TaskfileFetchFailedError{URI: node.Location()}
|
return nil, errors.TaskfileFetchFailedError{URI: node.Location()}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/go-task/task/v3/internal/execext"
|
"github.com/go-task/task/v3/internal/execext"
|
||||||
"github.com/go-task/task/v3/internal/filepathext"
|
"github.com/go-task/task/v3/internal/filepathext"
|
||||||
@@ -43,7 +42,7 @@ func (node *StdinNode) Read() ([]byte, error) {
|
|||||||
|
|
||||||
func (node *StdinNode) ResolveEntrypoint(entrypoint string) (string, error) {
|
func (node *StdinNode) ResolveEntrypoint(entrypoint string) (string, error) {
|
||||||
// If the file is remote, we don't need to resolve the path
|
// If the file is remote, we don't need to resolve the path
|
||||||
if strings.Contains(entrypoint, "://") {
|
if isRemoteEntrypoint(entrypoint) {
|
||||||
return entrypoint, nil
|
return entrypoint, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/dominikbraun/graph"
|
"github.com/dominikbraun/graph"
|
||||||
|
"go.yaml.in/yaml/v4"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
"gopkg.in/yaml.v3"
|
|
||||||
|
|
||||||
"github.com/go-task/task/v3/errors"
|
"github.com/go-task/task/v3/errors"
|
||||||
"github.com/go-task/task/v3/internal/env"
|
"github.com/go-task/task/v3/internal/env"
|
||||||
|
|||||||
@@ -1,8 +1,48 @@
|
|||||||
package ast
|
package ast
|
||||||
|
|
||||||
import "github.com/Masterminds/semver/v3"
|
import (
|
||||||
|
"cmp"
|
||||||
|
"maps"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Masterminds/semver/v3"
|
||||||
|
)
|
||||||
|
|
||||||
type TaskRC struct {
|
type TaskRC struct {
|
||||||
Version *semver.Version `yaml:"version"`
|
Version *semver.Version `yaml:"version"`
|
||||||
|
Verbose *bool `yaml:"verbose"`
|
||||||
|
Concurrency *int `yaml:"concurrency"`
|
||||||
|
Remote Remote `yaml:"remote"`
|
||||||
Experiments map[string]int `yaml:"experiments"`
|
Experiments map[string]int `yaml:"experiments"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Remote struct {
|
||||||
|
Insecure *bool `yaml:"insecure"`
|
||||||
|
Offline *bool `yaml:"offline"`
|
||||||
|
Timeout *time.Duration `yaml:"timeout"`
|
||||||
|
CacheExpiry *time.Duration `yaml:"cache-expiry"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge combines the current TaskRC with another TaskRC, prioritizing non-nil fields from the other TaskRC.
|
||||||
|
func (t *TaskRC) Merge(other *TaskRC) {
|
||||||
|
if other == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Version = cmp.Or(other.Version, t.Version)
|
||||||
|
|
||||||
|
if t.Experiments == nil && other.Experiments != nil {
|
||||||
|
t.Experiments = other.Experiments
|
||||||
|
} else if t.Experiments != nil && other.Experiments != nil {
|
||||||
|
maps.Copy(t.Experiments, other.Experiments)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge Remote fields
|
||||||
|
t.Remote.Insecure = cmp.Or(other.Remote.Insecure, t.Remote.Insecure)
|
||||||
|
t.Remote.Offline = cmp.Or(other.Remote.Offline, t.Remote.Offline)
|
||||||
|
t.Remote.Timeout = cmp.Or(other.Remote.Timeout, t.Remote.Timeout)
|
||||||
|
t.Remote.CacheExpiry = cmp.Or(other.Remote.CacheExpiry, t.Remote.CacheExpiry)
|
||||||
|
|
||||||
|
t.Verbose = cmp.Or(other.Verbose, t.Verbose)
|
||||||
|
t.Concurrency = cmp.Or(other.Concurrency, t.Concurrency)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
package taskrc
|
package taskrc
|
||||||
|
|
||||||
import "github.com/go-task/task/v3/internal/fsext"
|
import (
|
||||||
|
"github.com/go-task/task/v3/internal/fsext"
|
||||||
|
)
|
||||||
|
|
||||||
type Node struct {
|
type Node struct {
|
||||||
entrypoint string
|
entrypoint string
|
||||||
dir string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNode(
|
func NewNode(
|
||||||
entrypoint string,
|
entrypoint string,
|
||||||
dir string,
|
dir string,
|
||||||
|
possibleFileNames []string,
|
||||||
) (*Node, error) {
|
) (*Node, error) {
|
||||||
dir = fsext.DefaultDir(entrypoint, dir)
|
dir = fsext.DefaultDir(entrypoint, dir)
|
||||||
var err error
|
resolvedEntrypoint, err := fsext.SearchPath(dir, possibleFileNames)
|
||||||
entrypoint, dir, err = fsext.Search(entrypoint, dir, defaultTaskRCs)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &Node{
|
return &Node{
|
||||||
entrypoint: entrypoint,
|
entrypoint: resolvedEntrypoint,
|
||||||
dir: dir,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package taskrc
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
"go.yaml.in/yaml/v4"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/taskrc/ast"
|
"github.com/go-task/task/v3/taskrc/ast"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,92 @@
|
|||||||
package taskrc
|
package taskrc
|
||||||
|
|
||||||
var defaultTaskRCs = []string{
|
import (
|
||||||
".taskrc.yml",
|
"os"
|
||||||
".taskrc.yaml",
|
"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
|
||||||
|
absDir, err := filepath.Abs(dir)
|
||||||
|
if err != nil {
|
||||||
|
return config, err
|
||||||
|
}
|
||||||
|
entrypoints, err := fsext.SearchAll("", absDir, defaultTaskRCs)
|
||||||
|
if err != nil {
|
||||||
|
return config, 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)
|
||||||
|
}
|
||||||
78
temp_dir.go
78
temp_dir.go
@@ -1,78 +0,0 @@
|
|||||||
package task
|
|
||||||
|
|
||||||
import (
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/go-task/task/v3/internal/env"
|
|
||||||
"github.com/go-task/task/v3/internal/execext"
|
|
||||||
"github.com/go-task/task/v3/internal/filepathext"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TempDir struct {
|
|
||||||
Remote string
|
|
||||||
Fingerprint string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewTempDir(dir string) (*TempDir, error) {
|
|
||||||
tempDir, err := setupTempDirFingerprint(dir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = setupTempDirRemote(dir, tempDir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return tempDir, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupTempDirFingerprint(dir string) (*TempDir, error) {
|
|
||||||
tempDir := env.GetTaskEnv("TEMP_DIR")
|
|
||||||
|
|
||||||
if tempDir == "" {
|
|
||||||
return &TempDir{
|
|
||||||
Remote: filepathext.SmartJoin(dir, ".task"),
|
|
||||||
Fingerprint: filepathext.SmartJoin(dir, ".task"),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if filepath.IsAbs(tempDir) || strings.HasPrefix(tempDir, "~") {
|
|
||||||
tempDir, err := execext.ExpandLiteral(tempDir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
projectDir, _ := filepath.Abs(dir)
|
|
||||||
projectName := filepath.Base(projectDir)
|
|
||||||
return &TempDir{
|
|
||||||
Remote: tempDir,
|
|
||||||
Fingerprint: filepathext.SmartJoin(tempDir, projectName),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &TempDir{
|
|
||||||
Remote: filepathext.SmartJoin(dir, tempDir),
|
|
||||||
Fingerprint: filepathext.SmartJoin(dir, tempDir),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupTempDirRemote(dir string, tempDir *TempDir) error {
|
|
||||||
remoteDir := env.GetTaskEnv("REMOTE_DIR")
|
|
||||||
|
|
||||||
if remoteDir == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if filepath.IsAbs(remoteDir) || strings.HasPrefix(remoteDir, "~") {
|
|
||||||
remoteTempDir, err := execext.ExpandLiteral(remoteDir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
tempDir.Remote = remoteTempDir
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
tempDir.Remote = filepathext.SmartJoin(dir, ".task")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
8
testdata/checksum/Taskfile.yml
vendored
8
testdata/checksum/Taskfile.yml
vendored
@@ -12,6 +12,14 @@ tasks:
|
|||||||
generates:
|
generates:
|
||||||
- ./generated.txt
|
- ./generated.txt
|
||||||
method: checksum
|
method: checksum
|
||||||
|
build-*:
|
||||||
|
cmds:
|
||||||
|
- cp ./source.txt ./generated-{{index .MATCH 0}}.txt
|
||||||
|
sources:
|
||||||
|
- ./source.txt
|
||||||
|
generates:
|
||||||
|
- ./generated-{{index .MATCH 0}}.txt
|
||||||
|
method: checksum
|
||||||
|
|
||||||
build-with-status:
|
build-with-status:
|
||||||
cmds:
|
cmds:
|
||||||
|
|||||||
1
testdata/checksum/generated-wildcard.txt
vendored
Normal file
1
testdata/checksum/generated-wildcard.txt
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Hello, World!
|
||||||
7
testdata/label_error/Taskfile.yml
vendored
Normal file
7
testdata/label_error/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
foo:
|
||||||
|
label: "foobar"
|
||||||
|
cmds:
|
||||||
|
- "false"
|
||||||
1
testdata/label_error/testdata/TestLabel-label_in_error-err-run.golden
vendored
Normal file
1
testdata/label_error/testdata/TestLabel-label_in_error-err-run.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
task: Failed to run task "foobar": exit status 1
|
||||||
1
testdata/label_error/testdata/TestLabel-label_in_error.golden
vendored
Normal file
1
testdata/label_error/testdata/TestLabel-label_in_error.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
task: [foobar] false
|
||||||
@@ -1 +1 @@
|
|||||||
task: precondition not met
|
task: Failed to run task "impossible": task: precondition not met
|
||||||
@@ -1 +1 @@
|
|||||||
task: Failed to run task "executes_failing_task_as_cmd": task: precondition not met
|
task: Failed to run task "executes_failing_task_as_cmd": task: Failed to run task "impossible": task: precondition not met
|
||||||
@@ -1 +1 @@
|
|||||||
task: precondition not met
|
task: Failed to run task "depends_on_impossible": task: Failed to run task "impossible": task: precondition not met
|
||||||
@@ -1 +1 @@
|
|||||||
task: Task "foo" cancelled by user
|
task: Failed to run task "foo": task: Task "foo" cancelled by user
|
||||||
@@ -1 +1 @@
|
|||||||
task: Task "foo" cancelled by user
|
task: Failed to run task "foo": task: Task "foo" cancelled by user
|
||||||
@@ -1 +1 @@
|
|||||||
task: Task "foo" cancelled by user
|
task: Failed to run task "foo": task: Task "foo" cancelled by user
|
||||||
@@ -1 +1 @@
|
|||||||
task: Task "foo" cancelled by user
|
task: Failed to run task "foo": task: Task "foo" cancelled by user
|
||||||
11
testdata/run/Taskfile.yml
vendored
11
testdata/run/Taskfile.yml
vendored
@@ -22,3 +22,14 @@ tasks:
|
|||||||
run: once
|
run: once
|
||||||
cmds:
|
cmds:
|
||||||
- echo starting {{.CONTENT}} >> hash.txt
|
- echo starting {{.CONTENT}} >> hash.txt
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
cmds:
|
||||||
|
- rm -rf wildcard.txt
|
||||||
|
- task: deploy:infra
|
||||||
|
- task: deploy:js
|
||||||
|
- task: deploy:go
|
||||||
|
|
||||||
|
deploy:*:
|
||||||
|
run: once
|
||||||
|
cmd: echo "Deploy {{index .MATCH 0}}" >> wildcard.txt
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user