mirror of
https://github.com/go-task/task.git
synced 2026-06-15 20:01:40 +00:00
Compare commits
112 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
179bde1f37 | ||
|
|
e4de687aee | ||
|
|
06538860a8 | ||
|
|
8a37bf5c1f | ||
|
|
ca99266aea | ||
|
|
8dfafe507f | ||
|
|
678fdec7d2 | ||
|
|
3626b271a7 | ||
|
|
fc378cfb92 | ||
|
|
9488a2a744 | ||
|
|
6ece2445ae | ||
|
|
9b95e758f4 | ||
|
|
28fee2c356 | ||
|
|
763e77467b | ||
|
|
f2385e625d | ||
|
|
e929cccd73 | ||
|
|
cb183349b7 | ||
|
|
2ebbb99f58 | ||
|
|
6660afc8d2 | ||
|
|
b710259bfa | ||
|
|
4ec6c453bd | ||
|
|
28408ef3f4 | ||
|
|
a2d34ffc4c | ||
|
|
1a190a118f | ||
|
|
18efa3982f | ||
|
|
655e83454e | ||
|
|
3ad4604c36 | ||
|
|
5a27d04655 | ||
|
|
ea933bcc55 | ||
|
|
e0d6b71971 | ||
|
|
d7ee855e49 | ||
|
|
511f35a456 | ||
|
|
5889ff6b65 | ||
|
|
85a98b5f90 | ||
|
|
89b6140166 | ||
|
|
8cd51af3b0 | ||
|
|
a40ddd4949 | ||
|
|
b1814277c2 | ||
|
|
500ab8b941 | ||
|
|
745633dc0e | ||
|
|
9b99866224 | ||
|
|
54e4905432 | ||
|
|
c95805e0e0 | ||
|
|
4560589652 | ||
|
|
084d6444b4 | ||
|
|
3fb7919577 | ||
|
|
69b345efc9 | ||
|
|
4af5278d73 | ||
|
|
12fbdd3ec7 | ||
|
|
72a349b0e9 | ||
|
|
896d65b21f | ||
|
|
2161f33b5c | ||
|
|
b93638b97a | ||
|
|
47b78ca879 | ||
|
|
f0b15d397b | ||
|
|
eb285fa3d2 | ||
|
|
02b13a687a | ||
|
|
a085d62727 | ||
|
|
4ab1958df1 | ||
|
|
54ca217b92 | ||
|
|
a6c0c1daba | ||
|
|
9cc1c7b40b | ||
|
|
7901cce831 | ||
|
|
c7b4f26900 | ||
|
|
3ed403b839 | ||
|
|
386dcbc1a0 | ||
|
|
799bc85498 | ||
|
|
0d9e8dd71b | ||
|
|
a927ffb31e | ||
|
|
42ad618205 | ||
|
|
2b713f564f | ||
|
|
cb8e94aa33 | ||
|
|
6bc339d714 | ||
|
|
5712c463f5 | ||
|
|
78cc6e5fd3 | ||
|
|
38e07ea812 | ||
|
|
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 |
11
.github/renovate.json
vendored
11
.github/renovate.json
vendored
@@ -8,6 +8,17 @@
|
|||||||
],
|
],
|
||||||
"mode": "full",
|
"mode": "full",
|
||||||
"addLabels":["area: dependencies"],
|
"addLabels":["area: dependencies"],
|
||||||
|
"customManagers": [
|
||||||
|
{
|
||||||
|
"customType": "regex",
|
||||||
|
"fileMatch": ["^\\.github/workflows/.*\\.ya?ml$"],
|
||||||
|
"matchStrings": [
|
||||||
|
"uses:\\s*golangci/golangci-lint-action@\\S+\\s+with:\\s+version:\\s*(?<currentValue>v[\\d.]+)"
|
||||||
|
],
|
||||||
|
"datasourceTemplate": "github-releases",
|
||||||
|
"depNameTemplate": "golangci/golangci-lint"
|
||||||
|
}
|
||||||
|
],
|
||||||
"packageRules": [
|
"packageRules": [
|
||||||
{
|
{
|
||||||
"matchManagers": ["github-actions"],
|
"matchManagers": ["github-actions"],
|
||||||
|
|||||||
@@ -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: |
|
||||||
|
|||||||
14
.github/workflows/lint.yml
vendored
14
.github/workflows/lint.yml
vendored
@@ -16,25 +16,25 @@ jobs:
|
|||||||
go-version: [1.24.x, 1.25.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@v5
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v8
|
uses: golangci/golangci-lint-action@v9
|
||||||
with:
|
with:
|
||||||
version: v2.1.0
|
version: v2.7.2
|
||||||
|
|
||||||
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.13
|
python-version: 3.14
|
||||||
|
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- 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'
|
||||||
|
|||||||
4
.github/workflows/release-nightly.yml
vendored
4
.github/workflows/release-nightly.yml
vendored
@@ -9,12 +9,12 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
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.25.x
|
go-version: 1.25.x
|
||||||
|
|
||||||
|
|||||||
19
.github/workflows/release.yml
vendored
19
.github/workflows/release.yml
vendored
@@ -5,23 +5,32 @@ on:
|
|||||||
tags:
|
tags:
|
||||||
- 'v*'
|
- 'v*'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
id-token: write # Required for OIDC
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
goreleaser:
|
goreleaser:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
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.25.x
|
go-version: 1.25.x
|
||||||
|
|
||||||
- name: npm-login
|
- uses: actions/setup-node@v6
|
||||||
run: |
|
with:
|
||||||
npm config set '//registry.npmjs.org/:_authToken'=${{ secrets.NPM_TOKEN }}
|
node-version: '24'
|
||||||
|
registry-url: 'https://registry.npmjs.org'
|
||||||
|
|
||||||
|
- name: Update npm
|
||||||
|
run: npm install -g npm@latest
|
||||||
|
|
||||||
- name: Install Task
|
- name: Install Task
|
||||||
uses: go-task/setup-task@v1
|
uses: go-task/setup-task@v1
|
||||||
|
|
||||||
|
|||||||
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
@@ -18,13 +18,13 @@ jobs:
|
|||||||
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@v5
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Download Go modules
|
- name: Download Go modules
|
||||||
run: go mod download
|
run: go mod download
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ nfpms:
|
|||||||
- deb
|
- deb
|
||||||
- rpm
|
- rpm
|
||||||
- apk
|
- apk
|
||||||
file_name_template: '{{.ProjectName}}_{{.Os}}_{{.Arch}}'
|
file_name_template: '{{.ProjectName}}_{{.Version}}_{{.Os}}_{{.Arch}}'
|
||||||
contents:
|
contents:
|
||||||
- src: completion/bash/task.bash
|
- src: completion/bash/task.bash
|
||||||
dst: /etc/bash_completion.d/task
|
dst: /etc/bash_completion.d/task
|
||||||
@@ -127,7 +127,7 @@ winget:
|
|||||||
repository:
|
repository:
|
||||||
owner: go-task
|
owner: go-task
|
||||||
name: winget-pkgs
|
name: winget-pkgs
|
||||||
branch: 'chore/task-{{.Version}}'
|
branch: 'task-{{.Version}}'
|
||||||
pull_request:
|
pull_request:
|
||||||
enabled: true
|
enabled: true
|
||||||
draft: false
|
draft: false
|
||||||
@@ -136,6 +136,8 @@ winget:
|
|||||||
owner: microsoft
|
owner: microsoft
|
||||||
name: winget-pkgs
|
name: winget-pkgs
|
||||||
branch: master
|
branch: master
|
||||||
|
body: |
|
||||||
|
/cc @andreynering @pd93 @vmaerten
|
||||||
|
|
||||||
|
|
||||||
npms:
|
npms:
|
||||||
|
|||||||
110
CHANGELOG.md
110
CHANGELOG.md
@@ -1,9 +1,113 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## Unreleased
|
## v3.46.4 - 2025-12-24
|
||||||
|
|
||||||
- Fixed bug that made a generic message, instead of an useful one, appear when
|
- Fixed regressions in completion script for Fish (#2591, #2604, #2592 by
|
||||||
a Taskfile could not be found (#2431 by @andreynering).
|
@WinkelCode).
|
||||||
|
|
||||||
|
## v3.46.3 - 2025-12-19
|
||||||
|
|
||||||
|
- Fixed regression in completion script for zsh (#2593, #2594 by @vmaerten).
|
||||||
|
|
||||||
|
## v3.46.2 - 2025-12-18
|
||||||
|
|
||||||
|
- Fixed a regression on previous release that affected variables passed via
|
||||||
|
command line (#2588, #2589 by @vmaerten).
|
||||||
|
|
||||||
|
## v3.46.1 - 2025-12-18
|
||||||
|
|
||||||
|
### ✨ Features
|
||||||
|
|
||||||
|
- A small behavior change was made to dependencies. Task will now wait for all
|
||||||
|
dependencies to finish running before continuing, even if any of them fail. To
|
||||||
|
opt for the previous behavior, set `failfast: true` either on your
|
||||||
|
`.taskrc.yml` or per task, or use the `--failfast` flag, which will also work
|
||||||
|
for `--parallel` (#1246, #2525 by @andreynering).
|
||||||
|
- The `--summary` flag now displays `vars:` (both global and task-level),
|
||||||
|
`env:`, and `requires:` sections. Dynamic variables show their shell command
|
||||||
|
(e.g., `sh: echo "hello"`) instead of the evaluated value (#2486 ,#2524 by
|
||||||
|
@vmaerten).
|
||||||
|
- Improved performance of fuzzy task name matching by implementing lazy
|
||||||
|
initialization. Added `--disable-fuzzy` flag and `disable-fuzzy` taskrc option
|
||||||
|
to allow disabling fuzzy matching entirely (#2521, #2523 by @vmaerten).
|
||||||
|
- Added LLM-optimized documentation via VitePress plugin, generating `llms.txt`
|
||||||
|
and `llms-full.txt` for AI-powered development tools (#2513 by @vmaerten).
|
||||||
|
- Added `--trusted-hosts` CLI flag and `remote.trusted-hosts` config option to
|
||||||
|
skip confirmation prompts for specified hosts when using Remote Taskfiles
|
||||||
|
(#2491, #2473 by @maciejlech).
|
||||||
|
- When running in GitHub Actions, Task now automatically emits error annotations
|
||||||
|
on failure, improving visibility in workflow summaries (#2568 by @vmaerten).
|
||||||
|
- The `--yes` flag is now accessible in templates via the new `CLI_ASSUME_YES`
|
||||||
|
variable (#2577, #2479 by @semihbkgr).
|
||||||
|
- Improved shell completion scripts (Zsh, Fish, PowerShell) by adding missing
|
||||||
|
flags and dynamic experimental feature detection (#2532 by @vmaerten).
|
||||||
|
- Remote Taskfiles now accept `application/octet-stream` Content-Type (#2536,
|
||||||
|
#1944 by @vmaerten).
|
||||||
|
- Shell completion now works when Task is installed or aliased under a different
|
||||||
|
binary name via TASK_EXE environment variable (#2495, #2468 by @vmaerten).
|
||||||
|
- Some small fixes and improvements were made to `task --init` and to the
|
||||||
|
default Taskfile it generates (#2433 by @andreynering).
|
||||||
|
- Added `--remote-cache-dir` flag and `remote.cache-dir` taskrc option to
|
||||||
|
customize the cache directory for Remote Taskfiles (#2572 by @vmaerten).
|
||||||
|
- Zsh completion now supports zstyle verbose option to show or hide task
|
||||||
|
descriptions (#2571 by @vmaerten).
|
||||||
|
- Task now automatically enables colored output in CI environments (GitHub
|
||||||
|
Actions, GitLab CI, etc.) without requiring FORCE_COLOR=1 (#2569 by
|
||||||
|
@vmaerten).
|
||||||
|
- Added color taskrc option to explicitly enable or disable colored output
|
||||||
|
globally (#2569 by @vmaerten).
|
||||||
|
- Improved Git Remote Taskfiles by switching to go-getter: SSH authentication
|
||||||
|
now works out of the box and `applyOf` is properly supported (#2512 by
|
||||||
|
@vmaerten).
|
||||||
|
|
||||||
|
### 🐛 Fixes
|
||||||
|
|
||||||
|
- Fix RPM upload to Cloudsmith by including the version in the filename to
|
||||||
|
ensure unique filenames (#2507 by @vmaerten).
|
||||||
|
- Fix `run: when_changed` to work properly for Taskfiles included multiple times
|
||||||
|
(#2508, #2511 by @trulede).
|
||||||
|
- Fixed Zsh and Fish completions to stop suggesting task names after `--`
|
||||||
|
separator, allowing proper CLI_ARGS completion (#1843, #1844 by
|
||||||
|
@boiledfroginthewell).
|
||||||
|
- Watch mode (`--watch`) now always runs the task, regardless of `run: once` or
|
||||||
|
`run: when_changed` settings (#2566, #1388 by @trulede).
|
||||||
|
- Fixed global variables (CLI_ARGS, CLI_FORCE, etc.) not being accessible in
|
||||||
|
root-level vars section (#2403, #2397 by @trulede, @vmaerten).
|
||||||
|
- Fixed a bug where `ignore_error` was ignored when using `task:` to call
|
||||||
|
another task (#2552, #363 by @trulede).
|
||||||
|
- Fixed Zsh completion not suggesting global tasks when using `-g`/`--global`
|
||||||
|
flag (#1574, #2574 by @vmaerten).
|
||||||
|
- Fixed Fish completion failing to parse task descriptions containing colons
|
||||||
|
(e.g., URLs or namespaced functions) (#2101, #2573 by @vmaerten).
|
||||||
|
- Fixed false positive "property 'for' is not allowed" warnings in IntelliJ when
|
||||||
|
using `for` loops in Taskfiles (#2576 by @vmaerten).
|
||||||
|
|
||||||
|
## 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
|
## v3.45.4 - 2025-09-17
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<a href="https://taskfile.dev/installation/">Installation</a> | <a href="https://taskfile.dev/usage/">Documentation</a> | <a href="https://twitter.com/taskfiledev">Twitter</a> | <a href="https://bsky.app/profile/taskfile.dev">Bluesky</a> | <a href="https://fosstodon.org/@task">Mastodon</a> | <a href="https://discord.gg/6TY36E39UK">Discord</a>
|
<a href="https://taskfile.dev/docs/installation">Installation</a> | <a href="https://taskfile.dev/docs/getting-started">Getting Started</a> | <a href="https://taskfile.dev/docs/guide">Docs</a> | <a href="https://twitter.com/taskfiledev">Twitter</a> | <a href="https://bsky.app/profile/taskfile.dev">Bluesky</a> | <a href="https://fosstodon.org/@task">Mastodon</a> | <a href="https://discord.gg/6TY36E39UK">Discord</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h1>Gold Sponsors</h1>
|
<h1>Gold Sponsors</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="https://devowl.io/wp-content/uploads/meta/favicon.webp" 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>
|
||||||
|
|||||||
@@ -121,11 +121,15 @@ func changelog(version *semver.Version) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wrap the changelog content with v-pre directive for VitePress to prevent
|
||||||
|
// Vue from interpreting template syntax like {{.TASK_VERSION}}
|
||||||
|
changelogWithVPre := strings.Replace(changelog, "# Changelog\n\n", "# Changelog\n\n::: v-pre\n\n", 1) + "\n:::"
|
||||||
|
|
||||||
// Add the frontmatter to the changelog
|
// Add the frontmatter to the changelog
|
||||||
changelog = fmt.Sprintf("---\n%s\n---\n\n%s", frontmatter, changelog)
|
changelogWithFrontmatter := fmt.Sprintf("---\n%s\n---\n\n%s", frontmatter, changelogWithVPre)
|
||||||
|
|
||||||
// Write the changelog to the target file
|
// Write the changelog to the target file
|
||||||
return os.WriteFile(changelogTarget, []byte(changelog), 0o644)
|
return os.WriteFile(changelogTarget, []byte(changelogWithFrontmatter), 0o644)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setVersionFile(fileName string, version *semver.Version) error {
|
func setVersionFile(fileName string, version *semver.Version) error {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
|
||||||
@@ -28,19 +29,34 @@ func main() {
|
|||||||
Color: flags.Color,
|
Color: flags.Color,
|
||||||
}
|
}
|
||||||
if err, ok := err.(*errors.TaskRunError); ok && flags.ExitCode {
|
if err, ok := err.(*errors.TaskRunError); ok && flags.ExitCode {
|
||||||
|
emitCIErrorAnnotation(err)
|
||||||
l.Errf(logger.Red, "%v\n", err)
|
l.Errf(logger.Red, "%v\n", err)
|
||||||
os.Exit(err.TaskExitCode())
|
os.Exit(err.TaskExitCode())
|
||||||
}
|
}
|
||||||
if err, ok := err.(errors.TaskError); ok {
|
if err, ok := err.(errors.TaskError); ok {
|
||||||
|
emitCIErrorAnnotation(err)
|
||||||
l.Errf(logger.Red, "%v\n", err)
|
l.Errf(logger.Red, "%v\n", err)
|
||||||
os.Exit(err.Code())
|
os.Exit(err.Code())
|
||||||
}
|
}
|
||||||
|
emitCIErrorAnnotation(err)
|
||||||
l.Errf(logger.Red, "%v\n", err)
|
l.Errf(logger.Red, "%v\n", err)
|
||||||
os.Exit(errors.CodeUnknown)
|
os.Exit(errors.CodeUnknown)
|
||||||
}
|
}
|
||||||
os.Exit(errors.CodeOk)
|
os.Exit(errors.CodeOk)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// emitCIErrorAnnotation emits an error annotation for supported CI providers.
|
||||||
|
func emitCIErrorAnnotation(err error) {
|
||||||
|
if isGA, _ := strconv.ParseBool(os.Getenv("GITHUB_ACTIONS")); !isGA {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if e, ok := err.(*errors.TaskRunError); ok {
|
||||||
|
fmt.Fprintf(os.Stdout, "::error title=Task '%s' failed::%v\n", e.TaskName, e.Err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Fprintf(os.Stdout, "::error title=Task failed::%v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
func run() error {
|
func run() error {
|
||||||
log := &logger.Logger{
|
log := &logger.Logger{
|
||||||
Stdout: os.Stdout,
|
Stdout: os.Stdout,
|
||||||
@@ -156,18 +172,23 @@ func run() error {
|
|||||||
calls = append(calls, &task.Call{Task: "default"})
|
calls = append(calls, &task.Call{Task: "default"})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Merge CLI variables first (e.g. FOO=bar) so they take priority over Taskfile defaults
|
||||||
|
e.Taskfile.Vars.Merge(globals, nil)
|
||||||
|
|
||||||
|
// Then ReverseMerge special variables so they're available for templating
|
||||||
cliArgsPostDashQuoted, err := args.ToQuotedString(cliArgsPostDash)
|
cliArgsPostDashQuoted, err := args.ToQuotedString(cliArgsPostDash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
globals.Set("CLI_ARGS", ast.Var{Value: cliArgsPostDashQuoted})
|
specialVars := ast.NewVars()
|
||||||
globals.Set("CLI_ARGS_LIST", ast.Var{Value: cliArgsPostDash})
|
specialVars.Set("CLI_ARGS", ast.Var{Value: cliArgsPostDashQuoted})
|
||||||
globals.Set("CLI_FORCE", ast.Var{Value: flags.Force || flags.ForceAll})
|
specialVars.Set("CLI_ARGS_LIST", ast.Var{Value: cliArgsPostDash})
|
||||||
globals.Set("CLI_SILENT", ast.Var{Value: flags.Silent})
|
specialVars.Set("CLI_FORCE", ast.Var{Value: flags.Force || flags.ForceAll})
|
||||||
globals.Set("CLI_VERBOSE", ast.Var{Value: flags.Verbose})
|
specialVars.Set("CLI_SILENT", ast.Var{Value: flags.Silent})
|
||||||
globals.Set("CLI_OFFLINE", ast.Var{Value: flags.Offline})
|
specialVars.Set("CLI_VERBOSE", ast.Var{Value: flags.Verbose})
|
||||||
e.Taskfile.Vars.Merge(globals, nil)
|
specialVars.Set("CLI_OFFLINE", ast.Var{Value: flags.Offline})
|
||||||
|
specialVars.Set("CLI_ASSUME_YES", ast.Var{Value: flags.AssumeYes})
|
||||||
|
e.Taskfile.Vars.ReverseMerge(specialVars, nil)
|
||||||
if !flags.Watch {
|
if !flags.Watch {
|
||||||
e.InterceptInterruptSignals()
|
e.InterceptInterruptSignals()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,13 +61,14 @@ func (c *Compiler) getVariables(t *ast.Task, call *Call, evaluateShVars bool) (*
|
|||||||
newVar := templater.ReplaceVar(v, cache)
|
newVar := templater.ReplaceVar(v, cache)
|
||||||
// If the variable should not be evaluated, but is nil, set it to an empty string
|
// If the variable should not be evaluated, but is nil, set it to an empty string
|
||||||
// This stops empty interface errors when using the templater to replace values later
|
// This stops empty interface errors when using the templater to replace values later
|
||||||
|
// Preserve the Sh field so it can be displayed in summary
|
||||||
if !evaluateShVars && newVar.Value == nil {
|
if !evaluateShVars && newVar.Value == nil {
|
||||||
result.Set(k, ast.Var{Value: ""})
|
result.Set(k, ast.Var{Value: "", Sh: newVar.Sh})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// If the variable should not be evaluated and it is set, we can set it and return
|
// If the variable should not be evaluated and it is set, we can set it and return
|
||||||
if !evaluateShVars {
|
if !evaluateShVars {
|
||||||
result.Set(k, ast.Var{Value: newVar.Value})
|
result.Set(k, ast.Var{Value: newVar.Value, Sh: newVar.Sh})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// Now we can check for errors since we've handled all the cases when we don't want to evaluate
|
// Now we can check for errors since we've handled all the cases when we don't want to evaluate
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# vim: set tabstop=2 shiftwidth=2 expandtab:
|
# vim: set tabstop=2 shiftwidth=2 expandtab:
|
||||||
|
|
||||||
_GO_TASK_COMPLETION_LIST_OPTION='--list-all'
|
_GO_TASK_COMPLETION_LIST_OPTION='--list-all'
|
||||||
|
TASK_CMD="${TASK_EXE:-task}"
|
||||||
|
|
||||||
function _task()
|
function _task()
|
||||||
{
|
{
|
||||||
@@ -21,7 +22,7 @@ function _task()
|
|||||||
|
|
||||||
# Handle special arguments of options.
|
# Handle special arguments of options.
|
||||||
case "$prev" in
|
case "$prev" in
|
||||||
-d|--dir)
|
-d|--dir|--remote-cache-dir)
|
||||||
_filedir -d
|
_filedir -d
|
||||||
return $?
|
return $?
|
||||||
;;
|
;;
|
||||||
@@ -52,4 +53,4 @@ function _task()
|
|||||||
__ltrim_colon_completions "$cur"
|
__ltrim_colon_completions "$cur"
|
||||||
}
|
}
|
||||||
|
|
||||||
complete -F _task task
|
complete -F _task "$TASK_CMD"
|
||||||
|
|||||||
@@ -1,4 +1,31 @@
|
|||||||
set -l GO_TASK_PROGNAME task
|
set -l GO_TASK_PROGNAME (if set -q GO_TASK_PROGNAME; echo $GO_TASK_PROGNAME; else if set -q TASK_EXE; echo $TASK_EXE; else; echo task; end)
|
||||||
|
|
||||||
|
# Cache variables for experiments (global)
|
||||||
|
set -g __task_experiments_cache ""
|
||||||
|
set -g __task_experiments_cache_time 0
|
||||||
|
|
||||||
|
# Helper function to get experiments with 1-second cache
|
||||||
|
function __task_get_experiments
|
||||||
|
set -l now (date +%s)
|
||||||
|
set -l ttl 1 # Cache for 1 second only
|
||||||
|
|
||||||
|
# Return cached value if still valid
|
||||||
|
if test (math "$now - $__task_experiments_cache_time") -lt $ttl
|
||||||
|
printf '%s\n' $__task_experiments_cache
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
# Refresh cache
|
||||||
|
set -g __task_experiments_cache (task --experiments 2>/dev/null)
|
||||||
|
set -g __task_experiments_cache_time $now
|
||||||
|
printf '%s\n' $__task_experiments_cache
|
||||||
|
end
|
||||||
|
|
||||||
|
# Helper function to check if an experiment is enabled
|
||||||
|
function __task_is_experiment_enabled
|
||||||
|
set -l experiment $argv[1]
|
||||||
|
__task_get_experiments | string match -qr "^\* $experiment:.*on"
|
||||||
|
end
|
||||||
|
|
||||||
function __task_get_tasks --description "Prints all available tasks with their description" --inherit-variable GO_TASK_PROGNAME
|
function __task_get_tasks --description "Prints all available tasks with their description" --inherit-variable GO_TASK_PROGNAME
|
||||||
# Check if the global task is requested
|
# Check if the global task is requested
|
||||||
@@ -27,28 +54,63 @@ function __task_get_tasks --description "Prints all available tasks with their d
|
|||||||
end
|
end
|
||||||
|
|
||||||
# Grab names and descriptions (if any) of the tasks
|
# Grab names and descriptions (if any) of the tasks
|
||||||
set -l output (echo $rawOutput | sed -e '1d; s/\* \(.*\):\s*\(.*\)\s*(\(aliases.*\))/\1\t\2\t\3/' -e 's/\* \(.*\):\s*\(.*\)/\1\t\2/'| string split0)
|
set -l output (echo $rawOutput | sed -e '1d; s/\* \(.*\):[[:space:]]\{2,\}\(.*\)[[:space:]]\{2,\}(\(aliases.*\))/\1\t\2\t\3/' -e 's/\* \(.*\):[[:space:]]\{2,\}\(.*\)/\1\t\2/'| string split0)
|
||||||
if test $output
|
if test $output
|
||||||
echo $output
|
echo $output
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
complete -c $GO_TASK_PROGNAME -d 'Runs the specified task(s). Falls back to the "default" task if no task name was specified, or lists all tasks if an unknown task name was
|
complete -c $GO_TASK_PROGNAME \
|
||||||
specified.' -xa "(__task_get_tasks)"
|
-d 'Runs the specified task(s). Falls back to the "default" task if no task name was specified, or lists all tasks if an unknown task name was specified.' \
|
||||||
|
-xa "(__task_get_tasks)" \
|
||||||
|
-n "not __fish_seen_subcommand_from --"
|
||||||
|
|
||||||
complete -c $GO_TASK_PROGNAME -s c -l color -d 'colored output (default true)'
|
# Standard flags
|
||||||
complete -c $GO_TASK_PROGNAME -s d -l dir -d 'sets directory of execution'
|
complete -c $GO_TASK_PROGNAME -s a -l list-all -d 'list all tasks'
|
||||||
complete -c $GO_TASK_PROGNAME -l dry -d 'compiles and prints tasks in the order that they would be run, without executing them'
|
complete -c $GO_TASK_PROGNAME -s c -l color -d 'colored output (default true)'
|
||||||
complete -c $GO_TASK_PROGNAME -s f -l force -d 'forces execution even when the task is up-to-date'
|
complete -c $GO_TASK_PROGNAME -s C -l concurrency -d 'limit number of concurrent tasks'
|
||||||
complete -c $GO_TASK_PROGNAME -s h -l help -d 'shows Task usage'
|
complete -c $GO_TASK_PROGNAME -l completion -d 'generate shell completion script' -xa "bash zsh fish powershell"
|
||||||
complete -c $GO_TASK_PROGNAME -s i -l init -d 'creates a new Taskfile.yml in the current folder'
|
complete -c $GO_TASK_PROGNAME -s d -l dir -d 'set directory of execution'
|
||||||
complete -c $GO_TASK_PROGNAME -s l -l list -d 'lists tasks with description of current Taskfile'
|
complete -c $GO_TASK_PROGNAME -l disable-fuzzy -d 'disable fuzzy matching for task names'
|
||||||
complete -c $GO_TASK_PROGNAME -s o -l output -d 'sets output style: [interleaved|group|prefixed]' -xa "interleaved group prefixed"
|
complete -c $GO_TASK_PROGNAME -s n -l dry -d 'compile and print tasks without executing'
|
||||||
complete -c $GO_TASK_PROGNAME -s p -l parallel -d 'executes tasks provided on command line in parallel'
|
complete -c $GO_TASK_PROGNAME -s x -l exit-code -d 'pass-through exit code of task command'
|
||||||
complete -c $GO_TASK_PROGNAME -s s -l silent -d 'disables echoing'
|
complete -c $GO_TASK_PROGNAME -l experiments -d 'list available experiments'
|
||||||
complete -c $GO_TASK_PROGNAME -l status -d 'exits with non-zero exit code if any of the given tasks is not up-to-date'
|
complete -c $GO_TASK_PROGNAME -s F -l failfast -d 'when running tasks in parallel, stop all tasks if one fails'
|
||||||
complete -c $GO_TASK_PROGNAME -l summary -d 'show summary about a task'
|
complete -c $GO_TASK_PROGNAME -s f -l force -d 'force execution even when up-to-date'
|
||||||
complete -c $GO_TASK_PROGNAME -s t -l taskfile -d 'choose which Taskfile to run. Defaults to "Taskfile.yml"'
|
complete -c $GO_TASK_PROGNAME -s g -l global -d 'run global Taskfile from home directory'
|
||||||
complete -c $GO_TASK_PROGNAME -s v -l verbose -d 'enables verbose mode'
|
complete -c $GO_TASK_PROGNAME -s h -l help -d 'show help'
|
||||||
complete -c $GO_TASK_PROGNAME -l version -d 'show Task version'
|
complete -c $GO_TASK_PROGNAME -s i -l init -d 'create new Taskfile'
|
||||||
complete -c $GO_TASK_PROGNAME -s w -l watch -d 'enables watch of the given task'
|
complete -c $GO_TASK_PROGNAME -l insecure -d 'allow insecure Taskfile downloads'
|
||||||
|
complete -c $GO_TASK_PROGNAME -s I -l interval -d 'interval to watch for changes'
|
||||||
|
complete -c $GO_TASK_PROGNAME -s j -l json -d 'format task list as JSON'
|
||||||
|
complete -c $GO_TASK_PROGNAME -s l -l list -d 'list tasks with descriptions'
|
||||||
|
complete -c $GO_TASK_PROGNAME -l nested -d 'nest namespaces when listing as JSON'
|
||||||
|
complete -c $GO_TASK_PROGNAME -l no-status -d 'ignore status when listing as JSON'
|
||||||
|
complete -c $GO_TASK_PROGNAME -s o -l output -d 'set output style' -xa "interleaved group prefixed"
|
||||||
|
complete -c $GO_TASK_PROGNAME -l output-group-begin -d 'message template before grouped output'
|
||||||
|
complete -c $GO_TASK_PROGNAME -l output-group-end -d 'message template after grouped output'
|
||||||
|
complete -c $GO_TASK_PROGNAME -l output-group-error-only -d 'hide output from successful tasks'
|
||||||
|
complete -c $GO_TASK_PROGNAME -s p -l parallel -d 'execute tasks in parallel'
|
||||||
|
complete -c $GO_TASK_PROGNAME -s s -l silent -d 'disable echoing'
|
||||||
|
complete -c $GO_TASK_PROGNAME -l sort -d 'set task sorting order' -xa "default alphanumeric none"
|
||||||
|
complete -c $GO_TASK_PROGNAME -l status -d 'exit non-zero if tasks not up-to-date'
|
||||||
|
complete -c $GO_TASK_PROGNAME -l summary -d 'show task summary'
|
||||||
|
complete -c $GO_TASK_PROGNAME -s t -l taskfile -d 'choose Taskfile to run'
|
||||||
|
complete -c $GO_TASK_PROGNAME -s v -l verbose -d 'verbose output'
|
||||||
|
complete -c $GO_TASK_PROGNAME -l version -d 'show version'
|
||||||
|
complete -c $GO_TASK_PROGNAME -s w -l watch -d 'watch mode, re-run on changes'
|
||||||
|
complete -c $GO_TASK_PROGNAME -s y -l yes -d 'assume yes to all prompts'
|
||||||
|
|
||||||
|
# Experimental flags (dynamically checked at completion time via -n condition)
|
||||||
|
# GentleForce experiment
|
||||||
|
complete -c $GO_TASK_PROGNAME -n "__task_is_experiment_enabled GENTLE_FORCE" -l force-all -d 'force execution of task and all dependencies'
|
||||||
|
|
||||||
|
# RemoteTaskfiles experiment - Options
|
||||||
|
complete -c $GO_TASK_PROGNAME -n "__task_is_experiment_enabled REMOTE_TASKFILES" -l offline -d 'use only local or cached Taskfiles'
|
||||||
|
complete -c $GO_TASK_PROGNAME -n "__task_is_experiment_enabled REMOTE_TASKFILES" -l timeout -d 'timeout for remote Taskfile downloads'
|
||||||
|
complete -c $GO_TASK_PROGNAME -n "__task_is_experiment_enabled REMOTE_TASKFILES" -l expiry -d 'cache expiry duration'
|
||||||
|
complete -c $GO_TASK_PROGNAME -n "__task_is_experiment_enabled REMOTE_TASKFILES" -l remote-cache-dir -d 'directory to cache remote Taskfiles' -xa "(__fish_complete_directories)"
|
||||||
|
|
||||||
|
# RemoteTaskfiles experiment - Operations
|
||||||
|
complete -c $GO_TASK_PROGNAME -n "__task_is_experiment_enabled REMOTE_TASKFILES" -l download -d 'download remote Taskfile'
|
||||||
|
complete -c $GO_TASK_PROGNAME -n "__task_is_experiment_enabled REMOTE_TASKFILES" -l clear-cache -d 'clear remote Taskfile cache'
|
||||||
|
|||||||
@@ -5,22 +5,82 @@ Register-ArgumentCompleter -CommandName task -ScriptBlock {
|
|||||||
|
|
||||||
if ($commandName.StartsWith('-')) {
|
if ($commandName.StartsWith('-')) {
|
||||||
$completions = @(
|
$completions = @(
|
||||||
[CompletionResult]::new('--list-all ', '--list-all ', [CompletionResultType]::ParameterName, 'list all tasks'),
|
# Standard flags (alphabetical order)
|
||||||
[CompletionResult]::new('--color ', '--color', [CompletionResultType]::ParameterName, '--color'),
|
[CompletionResult]::new('-a', '-a', [CompletionResultType]::ParameterName, 'list all tasks'),
|
||||||
[CompletionResult]::new('--concurrency=', '--concurrency=', [CompletionResultType]::ParameterName, 'concurrency'),
|
[CompletionResult]::new('--list-all', '--list-all', [CompletionResultType]::ParameterName, 'list all tasks'),
|
||||||
[CompletionResult]::new('--interval=', '--interval=', [CompletionResultType]::ParameterName, 'interval'),
|
[CompletionResult]::new('-c', '-c', [CompletionResultType]::ParameterName, 'colored output'),
|
||||||
[CompletionResult]::new('--output=interleaved ', '--output=interleaved', [CompletionResultType]::ParameterName, '--output='),
|
[CompletionResult]::new('--color', '--color', [CompletionResultType]::ParameterName, 'colored output'),
|
||||||
[CompletionResult]::new('--output=group ', '--output=group', [CompletionResultType]::ParameterName, '--output='),
|
[CompletionResult]::new('-C', '-C', [CompletionResultType]::ParameterName, 'limit concurrent tasks'),
|
||||||
[CompletionResult]::new('--output=prefixed ', '--output=prefixed', [CompletionResultType]::ParameterName, '--output='),
|
[CompletionResult]::new('--concurrency', '--concurrency', [CompletionResultType]::ParameterName, 'limit concurrent tasks'),
|
||||||
[CompletionResult]::new('--dry ', '--dry', [CompletionResultType]::ParameterName, '--dry'),
|
[CompletionResult]::new('--completion', '--completion', [CompletionResultType]::ParameterName, 'generate shell completion'),
|
||||||
[CompletionResult]::new('--force ', '--force', [CompletionResultType]::ParameterName, '--force'),
|
[CompletionResult]::new('-d', '-d', [CompletionResultType]::ParameterName, 'set directory'),
|
||||||
[CompletionResult]::new('--parallel ', '--parallel', [CompletionResultType]::ParameterName, '--parallel'),
|
[CompletionResult]::new('--dir', '--dir', [CompletionResultType]::ParameterName, 'set directory'),
|
||||||
[CompletionResult]::new('--silent ', '--silent', [CompletionResultType]::ParameterName, '--silent'),
|
[CompletionResult]::new('--disable-fuzzy', '--disable-fuzzy', [CompletionResultType]::ParameterName, 'disable fuzzy matching'),
|
||||||
[CompletionResult]::new('--status ', '--status', [CompletionResultType]::ParameterName, '--status'),
|
[CompletionResult]::new('-n', '-n', [CompletionResultType]::ParameterName, 'dry run'),
|
||||||
[CompletionResult]::new('--verbose ', '--verbose', [CompletionResultType]::ParameterName, '--verbose'),
|
[CompletionResult]::new('--dry', '--dry', [CompletionResultType]::ParameterName, 'dry run'),
|
||||||
[CompletionResult]::new('--watch ', '--watch', [CompletionResultType]::ParameterName, '--watch')
|
[CompletionResult]::new('-x', '-x', [CompletionResultType]::ParameterName, 'pass-through exit code'),
|
||||||
|
[CompletionResult]::new('--exit-code', '--exit-code', [CompletionResultType]::ParameterName, 'pass-through exit code'),
|
||||||
|
[CompletionResult]::new('--experiments', '--experiments', [CompletionResultType]::ParameterName, 'list experiments'),
|
||||||
|
[CompletionResult]::new('-F', '-F', [CompletionResultType]::ParameterName, 'fail fast on pallalel tasks'),
|
||||||
|
[CompletionResult]::new('--failfast', '--failfast', [CompletionResultType]::ParameterName, 'force execution'),
|
||||||
|
[CompletionResult]::new('-f', '-f', [CompletionResultType]::ParameterName, 'force execution'),
|
||||||
|
[CompletionResult]::new('--force', '--force', [CompletionResultType]::ParameterName, 'force execution'),
|
||||||
|
[CompletionResult]::new('-g', '-g', [CompletionResultType]::ParameterName, 'run global Taskfile'),
|
||||||
|
[CompletionResult]::new('--global', '--global', [CompletionResultType]::ParameterName, 'run global Taskfile'),
|
||||||
|
[CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'show help'),
|
||||||
|
[CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'show help'),
|
||||||
|
[CompletionResult]::new('-i', '-i', [CompletionResultType]::ParameterName, 'create new Taskfile'),
|
||||||
|
[CompletionResult]::new('--init', '--init', [CompletionResultType]::ParameterName, 'create new Taskfile'),
|
||||||
|
[CompletionResult]::new('--insecure', '--insecure', [CompletionResultType]::ParameterName, 'allow insecure downloads'),
|
||||||
|
[CompletionResult]::new('-I', '-I', [CompletionResultType]::ParameterName, 'watch interval'),
|
||||||
|
[CompletionResult]::new('--interval', '--interval', [CompletionResultType]::ParameterName, 'watch interval'),
|
||||||
|
[CompletionResult]::new('-j', '-j', [CompletionResultType]::ParameterName, 'format as JSON'),
|
||||||
|
[CompletionResult]::new('--json', '--json', [CompletionResultType]::ParameterName, 'format as JSON'),
|
||||||
|
[CompletionResult]::new('-l', '-l', [CompletionResultType]::ParameterName, 'list tasks'),
|
||||||
|
[CompletionResult]::new('--list', '--list', [CompletionResultType]::ParameterName, 'list tasks'),
|
||||||
|
[CompletionResult]::new('--nested', '--nested', [CompletionResultType]::ParameterName, 'nest namespaces in JSON'),
|
||||||
|
[CompletionResult]::new('--no-status', '--no-status', [CompletionResultType]::ParameterName, 'ignore status in JSON'),
|
||||||
|
[CompletionResult]::new('-o', '-o', [CompletionResultType]::ParameterName, 'set output style'),
|
||||||
|
[CompletionResult]::new('--output', '--output', [CompletionResultType]::ParameterName, 'set output style'),
|
||||||
|
[CompletionResult]::new('--output-group-begin', '--output-group-begin', [CompletionResultType]::ParameterName, 'template before group'),
|
||||||
|
[CompletionResult]::new('--output-group-end', '--output-group-end', [CompletionResultType]::ParameterName, 'template after group'),
|
||||||
|
[CompletionResult]::new('--output-group-error-only', '--output-group-error-only', [CompletionResultType]::ParameterName, 'hide successful output'),
|
||||||
|
[CompletionResult]::new('-p', '-p', [CompletionResultType]::ParameterName, 'execute in parallel'),
|
||||||
|
[CompletionResult]::new('--parallel', '--parallel', [CompletionResultType]::ParameterName, 'execute in parallel'),
|
||||||
|
[CompletionResult]::new('-s', '-s', [CompletionResultType]::ParameterName, 'silent mode'),
|
||||||
|
[CompletionResult]::new('--silent', '--silent', [CompletionResultType]::ParameterName, 'silent mode'),
|
||||||
|
[CompletionResult]::new('--sort', '--sort', [CompletionResultType]::ParameterName, 'task sorting order'),
|
||||||
|
[CompletionResult]::new('--status', '--status', [CompletionResultType]::ParameterName, 'check task status'),
|
||||||
|
[CompletionResult]::new('--summary', '--summary', [CompletionResultType]::ParameterName, 'show task summary'),
|
||||||
|
[CompletionResult]::new('-t', '-t', [CompletionResultType]::ParameterName, 'choose Taskfile'),
|
||||||
|
[CompletionResult]::new('--taskfile', '--taskfile', [CompletionResultType]::ParameterName, 'choose Taskfile'),
|
||||||
|
[CompletionResult]::new('-v', '-v', [CompletionResultType]::ParameterName, 'verbose output'),
|
||||||
|
[CompletionResult]::new('--verbose', '--verbose', [CompletionResultType]::ParameterName, 'verbose output'),
|
||||||
|
[CompletionResult]::new('--version', '--version', [CompletionResultType]::ParameterName, 'show version'),
|
||||||
|
[CompletionResult]::new('-w', '-w', [CompletionResultType]::ParameterName, 'watch mode'),
|
||||||
|
[CompletionResult]::new('--watch', '--watch', [CompletionResultType]::ParameterName, 'watch mode'),
|
||||||
|
[CompletionResult]::new('-y', '-y', [CompletionResultType]::ParameterName, 'assume yes'),
|
||||||
|
[CompletionResult]::new('--yes', '--yes', [CompletionResultType]::ParameterName, 'assume yes')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Experimental flags (dynamically added based on enabled experiments)
|
||||||
|
$experiments = & task --experiments 2>$null | Out-String
|
||||||
|
|
||||||
|
if ($experiments -match '\* GENTLE_FORCE:.*on') {
|
||||||
|
$completions += [CompletionResult]::new('--force-all', '--force-all', [CompletionResultType]::ParameterName, 'force all dependencies')
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($experiments -match '\* REMOTE_TASKFILES:.*on') {
|
||||||
|
# Options
|
||||||
|
$completions += [CompletionResult]::new('--offline', '--offline', [CompletionResultType]::ParameterName, 'use cached Taskfiles')
|
||||||
|
$completions += [CompletionResult]::new('--timeout', '--timeout', [CompletionResultType]::ParameterName, 'download timeout')
|
||||||
|
$completions += [CompletionResult]::new('--expiry', '--expiry', [CompletionResultType]::ParameterName, 'cache expiry')
|
||||||
|
$completions += [CompletionResult]::new('--remote-cache-dir', '--remote-cache-dir', [CompletionResultType]::ParameterName, 'cache directory')
|
||||||
|
# Operations
|
||||||
|
$completions += [CompletionResult]::new('--download', '--download', [CompletionResultType]::ParameterName, 'download remote Taskfile')
|
||||||
|
$completions += [CompletionResult]::new('--clear-cache', '--clear-cache', [CompletionResultType]::ParameterName, 'clear cache')
|
||||||
|
}
|
||||||
|
|
||||||
return $completions.Where{ $_.CompletionText.StartsWith($commandName) }
|
return $completions.Where{ $_.CompletionText.StartsWith($commandName) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,42 @@
|
|||||||
#compdef task
|
#compdef task
|
||||||
compdef _task task
|
|
||||||
typeset -A opt_args
|
typeset -A opt_args
|
||||||
|
TASK_CMD="${TASK_EXE:-task}"
|
||||||
|
compdef _task "$TASK_CMD"
|
||||||
|
|
||||||
_GO_TASK_COMPLETION_LIST_OPTION="${GO_TASK_COMPLETION_LIST_OPTION:---list-all}"
|
_GO_TASK_COMPLETION_LIST_OPTION="${GO_TASK_COMPLETION_LIST_OPTION:---list-all}"
|
||||||
|
|
||||||
|
# Check if an experiment is enabled
|
||||||
|
function __task_is_experiment_enabled() {
|
||||||
|
local experiment=$1
|
||||||
|
task --experiments 2>/dev/null | grep -q "^\* ${experiment}:.*on"
|
||||||
|
}
|
||||||
|
|
||||||
# Listing commands from Taskfile.yml
|
# Listing commands from Taskfile.yml
|
||||||
function __task_list() {
|
function __task_list() {
|
||||||
local -a scripts cmd
|
local -a scripts cmd
|
||||||
local -i enabled=0
|
local -i enabled=0
|
||||||
local taskfile item task desc
|
local taskfile item task desc
|
||||||
|
|
||||||
cmd=(task)
|
cmd=($TASK_CMD)
|
||||||
taskfile=${(Qv)opt_args[(i)-t|--taskfile]}
|
taskfile=${(Qv)opt_args[(i)-t|--taskfile]}
|
||||||
taskfile=${taskfile//\~/$HOME}
|
taskfile=${taskfile//\~/$HOME}
|
||||||
|
|
||||||
|
for arg in "${words[@]:0:$CURRENT}"; do
|
||||||
|
if [[ "$arg" = "--" ]]; then
|
||||||
|
# Use default completion for words after `--` as they are CLI_ARGS.
|
||||||
|
_default
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
if [[ -n "$taskfile" && -f "$taskfile" ]]; then
|
if [[ -n "$taskfile" && -f "$taskfile" ]]; then
|
||||||
cmd+=(--taskfile "$taskfile")
|
cmd+=(--taskfile "$taskfile")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Check if global flag is set
|
||||||
|
if (( ${+opt_args[-g]} || ${+opt_args[--global]} )); then
|
||||||
|
cmd+=(--global)
|
||||||
|
fi
|
||||||
|
|
||||||
if output=$("${cmd[@]}" $_GO_TASK_COMPLETION_LIST_OPTION 2>/dev/null); then
|
if output=$("${cmd[@]}" $_GO_TASK_COMPLETION_LIST_OPTION 2>/dev/null); then
|
||||||
enabled=1
|
enabled=1
|
||||||
@@ -27,38 +45,103 @@ function __task_list() {
|
|||||||
(( enabled )) || return 0
|
(( enabled )) || return 0
|
||||||
|
|
||||||
scripts=()
|
scripts=()
|
||||||
|
|
||||||
|
# Read zstyle verbose option (default = true via -T)
|
||||||
|
local show_desc
|
||||||
|
zstyle -T ":completion:${curcontext}:" verbose && show_desc=true || show_desc=false
|
||||||
|
|
||||||
for item in "${(@)${(f)output}[2,-1]#\* }"; do
|
for item in "${(@)${(f)output}[2,-1]#\* }"; do
|
||||||
task="${item%%:[[:space:]]*}"
|
task="${item%%:[[:space:]]*}"
|
||||||
desc="${item##[^[:space:]]##[[:space:]]##}"
|
|
||||||
scripts+=( "${task//:/\\:}:$desc" )
|
if [[ "$show_desc" == "true" ]]; then
|
||||||
|
local desc="${item##[^[:space:]]##[[:space:]]##}"
|
||||||
|
scripts+=( "${task//:/\\:}:$desc" )
|
||||||
|
else
|
||||||
|
scripts+=( "$task" )
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
_describe 'Task to run' scripts
|
|
||||||
|
if [[ "$show_desc" == "true" ]]; then
|
||||||
|
_describe 'Task to run' scripts
|
||||||
|
else
|
||||||
|
compadd -Q -a scripts
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
_task() {
|
_task() {
|
||||||
_arguments \
|
local -a standard_args operation_args
|
||||||
'(-C --concurrency)'{-C,--concurrency}'[limit number of concurrent tasks]: ' \
|
|
||||||
'(-p --parallel)'{-p,--parallel}'[run command-line tasks in parallel]' \
|
standard_args=(
|
||||||
'(-f --force)'{-f,--force}'[run even if task is up-to-date]' \
|
'(-C --concurrency)'{-C,--concurrency}'[limit number of concurrent tasks]: '
|
||||||
'(-c --color)'{-c,--color}'[colored output]' \
|
'(-p --parallel)'{-p,--parallel}'[run command-line tasks in parallel]'
|
||||||
'(-d --dir)'{-d,--dir}'[dir to run in]:execution dir:_dirs' \
|
'(-F --failfast)'{-F,--failfast}'[when running tasks in parallel, stop all tasks if one fails]'
|
||||||
'(--dry)--dry[dry-run mode, compile and print tasks only]' \
|
'(-f --force)'{-f,--force}'[run even if task is up-to-date]'
|
||||||
'(-o --output)'{-o,--output}'[set output style]:style:(interleaved group prefixed)' \
|
'(-c --color)'{-c,--color}'[colored output]'
|
||||||
'(--output-group-begin)--output-group-begin[message template before grouped output]:template text: ' \
|
'(--completion)--completion[generate shell completion script]:shell:(bash zsh fish powershell)'
|
||||||
'(--output-group-end)--output-group-end[message template after grouped output]:template text: ' \
|
'(-d --dir)'{-d,--dir}'[dir to run in]:execution dir:_dirs'
|
||||||
'(-s --silent)'{-s,--silent}'[disable echoing]' \
|
'(--disable-fuzzy)--disable-fuzzy[disable fuzzy matching for task names]'
|
||||||
'(--status)--status[exit non-zero if supplied tasks not up-to-date]' \
|
'(-n --dry)'{-n,--dry}'[compiles and prints tasks without executing]'
|
||||||
'(--summary)--summary[show summary\: field from tasks instead of running them]' \
|
'(--dry)--dry[dry-run mode, compile and print tasks only]'
|
||||||
'(-t --taskfile)'{-t,--taskfile}'[specify a different taskfile]:taskfile:_files' \
|
'(-x --exit-code)'{-x,--exit-code}'[pass-through exit code of task command]'
|
||||||
'(-v --verbose)'{-v,--verbose}'[verbose mode]' \
|
'(--experiments)--experiments[list available experiments]'
|
||||||
'(-w --watch)'{-w,--watch}'[watch-mode for given tasks, re-run when inputs change]' \
|
'(-g --global)'{-g,--global}'[run global Taskfile from home directory]'
|
||||||
+ '(operation)' \
|
'(--insecure)--insecure[allow insecure Taskfile downloads]'
|
||||||
{-l,--list}'[list describable tasks]' \
|
'(-I --interval)'{-I,--interval}'[interval to watch for changes]:duration: '
|
||||||
{-a,--list-all}'[list all tasks]' \
|
'(-j --json)'{-j,--json}'[format task list as JSON]'
|
||||||
{-i,--init}'[create new Taskfile.yml]' \
|
'(--nested)--nested[nest namespaces when listing as JSON]'
|
||||||
'(-*)'{-h,--help}'[show help]' \
|
'(--no-status)--no-status[ignore status when listing as JSON]'
|
||||||
'(-*)--version[show version and exit]' \
|
'(-o --output)'{-o,--output}'[set output style]:style:(interleaved group prefixed)'
|
||||||
'*: :__task_list'
|
'(--output-group-begin)--output-group-begin[message template before grouped output]:template text: '
|
||||||
|
'(--output-group-end)--output-group-end[message template after grouped output]:template text: '
|
||||||
|
'(--output-group-error-only)--output-group-error-only[hide output from successful tasks]'
|
||||||
|
'(-s --silent)'{-s,--silent}'[disable echoing]'
|
||||||
|
'(--sort)--sort[set task sorting order]:order:(default alphanumeric none)'
|
||||||
|
'(--status)--status[exit non-zero if supplied tasks not up-to-date]'
|
||||||
|
'(--summary)--summary[show summary\: field from tasks instead of running them]'
|
||||||
|
'(-t --taskfile)'{-t,--taskfile}'[specify a different taskfile]:taskfile:_files'
|
||||||
|
'(-v --verbose)'{-v,--verbose}'[verbose mode]'
|
||||||
|
'(-w --watch)'{-w,--watch}'[watch-mode for given tasks, re-run when inputs change]'
|
||||||
|
'(-y --yes)'{-y,--yes}'[assume yes to all prompts]'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Experimental flags (dynamically added based on enabled experiments)
|
||||||
|
# Options (modify behavior)
|
||||||
|
if __task_is_experiment_enabled "GENTLE_FORCE"; then
|
||||||
|
standard_args+=('(--force-all)--force-all[force execution of task and all dependencies]')
|
||||||
|
fi
|
||||||
|
|
||||||
|
if __task_is_experiment_enabled "REMOTE_TASKFILES"; then
|
||||||
|
standard_args+=(
|
||||||
|
'(--offline --download)--offline[use only local or cached Taskfiles]'
|
||||||
|
'(--timeout)--timeout[timeout for remote Taskfile downloads]:duration: '
|
||||||
|
'(--expiry)--expiry[cache expiry duration]:duration: '
|
||||||
|
'(--remote-cache-dir)--remote-cache-dir[directory to cache remote Taskfiles]:cache dir:_dirs'
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
|
||||||
|
operation_args=(
|
||||||
|
# Task names completion (can be specified multiple times)
|
||||||
|
'(operation)*: :__task_list'
|
||||||
|
# Operational args completion (mutually exclusive)
|
||||||
|
+ '(operation)'
|
||||||
|
'(*)'{-l,--list}'[list describable tasks]'
|
||||||
|
'(*)'{-a,--list-all}'[list all tasks]'
|
||||||
|
'(*)'{-i,--init}'[create new Taskfile.yml]'
|
||||||
|
'(- *)'{-h,--help}'[show help]'
|
||||||
|
'(- *)--version[show version and exit]'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Experimental operations (dynamically added based on enabled experiments)
|
||||||
|
if __task_is_experiment_enabled "REMOTE_TASKFILES"; then
|
||||||
|
standard_args+=(
|
||||||
|
'(--offline --clear-cache)--download[download remote Taskfile]'
|
||||||
|
)
|
||||||
|
operation_args+=(
|
||||||
|
'(* --download)--clear-cache[clear remote Taskfile cache]'
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
|
||||||
|
_arguments -S $standard_args $operation_args
|
||||||
}
|
}
|
||||||
|
|
||||||
# don't run the completion function when being source-ed or eval-ed
|
# don't run the completion function when being source-ed or eval-ed
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
64
executor.go
64
executor.go
@@ -7,7 +7,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/puzpuzpuz/xsync/v3"
|
"github.com/puzpuzpuz/xsync/v4"
|
||||||
"github.com/sajari/fuzzy"
|
"github.com/sajari/fuzzy"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/internal/logger"
|
"github.com/go-task/task/v3/internal/logger"
|
||||||
@@ -34,11 +34,14 @@ type (
|
|||||||
Insecure bool
|
Insecure bool
|
||||||
Download bool
|
Download bool
|
||||||
Offline bool
|
Offline bool
|
||||||
|
TrustedHosts []string
|
||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
CacheExpiryDuration time.Duration
|
CacheExpiryDuration time.Duration
|
||||||
|
RemoteCacheDir string
|
||||||
Watch bool
|
Watch bool
|
||||||
Verbose bool
|
Verbose bool
|
||||||
Silent bool
|
Silent bool
|
||||||
|
DisableFuzzy bool
|
||||||
AssumeYes bool
|
AssumeYes bool
|
||||||
AssumeTerm bool // Used for testing
|
AssumeTerm bool // Used for testing
|
||||||
Dry bool
|
Dry bool
|
||||||
@@ -47,6 +50,7 @@ type (
|
|||||||
Color bool
|
Color bool
|
||||||
Concurrency int
|
Concurrency int
|
||||||
Interval time.Duration
|
Interval time.Duration
|
||||||
|
Failfast bool
|
||||||
|
|
||||||
// I/O
|
// I/O
|
||||||
Stdin io.Reader
|
Stdin io.Reader
|
||||||
@@ -63,14 +67,15 @@ type (
|
|||||||
UserWorkingDir string
|
UserWorkingDir string
|
||||||
EnableVersionCheck bool
|
EnableVersionCheck bool
|
||||||
|
|
||||||
fuzzyModel *fuzzy.Model
|
fuzzyModel *fuzzy.Model
|
||||||
|
fuzzyModelOnce sync.Once
|
||||||
|
|
||||||
concurrencySemaphore chan struct{}
|
concurrencySemaphore chan struct{}
|
||||||
taskCallCount map[string]*int32
|
taskCallCount map[string]*int32
|
||||||
mkdirMutexMap map[string]*sync.Mutex
|
mkdirMutexMap map[string]*sync.Mutex
|
||||||
executionHashes map[string]context.Context
|
executionHashes map[string]context.Context
|
||||||
executionHashesMutex sync.Mutex
|
executionHashesMutex sync.Mutex
|
||||||
watchedDirs *xsync.MapOf[string, bool]
|
watchedDirs *xsync.Map[string, bool]
|
||||||
}
|
}
|
||||||
TempDir struct {
|
TempDir struct {
|
||||||
Remote string
|
Remote string
|
||||||
@@ -225,6 +230,20 @@ func (o *offlineOption) ApplyToExecutor(e *Executor) {
|
|||||||
e.Offline = o.offline
|
e.Offline = o.offline
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithTrustedHosts configures the [Executor] with a list of trusted hosts for remote
|
||||||
|
// Taskfiles. Hosts in this list will not prompt for user confirmation.
|
||||||
|
func WithTrustedHosts(trustedHosts []string) ExecutorOption {
|
||||||
|
return &trustedHostsOption{trustedHosts}
|
||||||
|
}
|
||||||
|
|
||||||
|
type trustedHostsOption struct {
|
||||||
|
trustedHosts []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *trustedHostsOption) ApplyToExecutor(e *Executor) {
|
||||||
|
e.TrustedHosts = o.trustedHosts
|
||||||
|
}
|
||||||
|
|
||||||
// WithTimeout sets the [Executor]'s timeout for fetching remote taskfiles. By
|
// WithTimeout sets the [Executor]'s timeout for fetching remote taskfiles. By
|
||||||
// default, the timeout is set to 10 seconds.
|
// default, the timeout is set to 10 seconds.
|
||||||
func WithTimeout(timeout time.Duration) ExecutorOption {
|
func WithTimeout(timeout time.Duration) ExecutorOption {
|
||||||
@@ -253,6 +272,19 @@ func (o *cacheExpiryDurationOption) ApplyToExecutor(r *Executor) {
|
|||||||
r.CacheExpiryDuration = o.duration
|
r.CacheExpiryDuration = o.duration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithRemoteCacheDir sets the directory where remote taskfiles are cached.
|
||||||
|
func WithRemoteCacheDir(dir string) ExecutorOption {
|
||||||
|
return &remoteCacheDirOption{dir: dir}
|
||||||
|
}
|
||||||
|
|
||||||
|
type remoteCacheDirOption struct {
|
||||||
|
dir string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *remoteCacheDirOption) ApplyToExecutor(e *Executor) {
|
||||||
|
e.RemoteCacheDir = o.dir
|
||||||
|
}
|
||||||
|
|
||||||
// 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.
|
||||||
@@ -296,6 +328,19 @@ func (o *silentOption) ApplyToExecutor(e *Executor) {
|
|||||||
e.Silent = o.silent
|
e.Silent = o.silent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithDisableFuzzy tells the [Executor] to disable fuzzy matching for task names.
|
||||||
|
func WithDisableFuzzy(disableFuzzy bool) ExecutorOption {
|
||||||
|
return &disableFuzzyOption{disableFuzzy}
|
||||||
|
}
|
||||||
|
|
||||||
|
type disableFuzzyOption struct {
|
||||||
|
disableFuzzy bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *disableFuzzyOption) ApplyToExecutor(e *Executor) {
|
||||||
|
e.DisableFuzzy = o.disableFuzzy
|
||||||
|
}
|
||||||
|
|
||||||
// WithAssumeYes tells the [Executor] to assume "yes" for all prompts.
|
// WithAssumeYes tells the [Executor] to assume "yes" for all prompts.
|
||||||
func WithAssumeYes(assumeYes bool) ExecutorOption {
|
func WithAssumeYes(assumeYes bool) ExecutorOption {
|
||||||
return &assumeYesOption{assumeYes}
|
return &assumeYesOption{assumeYes}
|
||||||
@@ -502,3 +547,16 @@ type versionCheckOption struct {
|
|||||||
func (o *versionCheckOption) ApplyToExecutor(e *Executor) {
|
func (o *versionCheckOption) ApplyToExecutor(e *Executor) {
|
||||||
e.EnableVersionCheck = o.enableVersionCheck
|
e.EnableVersionCheck = o.enableVersionCheck
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithFailfast tells the [Executor] whether or not to check the version of
|
||||||
|
func WithFailfast(failfast bool) ExecutorOption {
|
||||||
|
return &failfastOption{failfast}
|
||||||
|
}
|
||||||
|
|
||||||
|
type failfastOption struct {
|
||||||
|
failfast bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *failfastOption) ApplyToExecutor(e *Executor) {
|
||||||
|
e.Failfast = o.failfast
|
||||||
|
}
|
||||||
|
|||||||
109
executor_test.go
109
executor_test.go
@@ -143,12 +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
|
||||||
|
|
||||||
opts := append(
|
opts := append(
|
||||||
tt.executorOpts,
|
tt.executorOpts,
|
||||||
task.WithStdout(&buf),
|
task.WithStdout(&buffer),
|
||||||
task.WithStderr(&buf),
|
task.WithStderr(&buffer),
|
||||||
)
|
)
|
||||||
|
|
||||||
// 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
|
||||||
@@ -171,7 +171,7 @@ func (tt *ExecutorTest) run(t *testing.T) {
|
|||||||
if err := e.Setup(); tt.wantSetupError {
|
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)
|
||||||
@@ -192,7 +192,7 @@ func (tt *ExecutorTest) run(t *testing.T) {
|
|||||||
if err := e.Run(ctx, call); tt.wantRunError {
|
if err := e.Run(ctx, call); tt.wantRunError {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
tt.writeFixtureErrRun(t, g, err)
|
tt.writeFixtureErrRun(t, g, err)
|
||||||
tt.writeFixtureBuffer(t, g, buf)
|
tt.writeFixtureBuffer(t, g, buffer.buf)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -205,7 +205,7 @@ func (tt *ExecutorTest) run(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
@@ -263,6 +263,23 @@ func TestVars(t *testing.T) {
|
|||||||
task.WithSilent(true),
|
task.WithSilent(true),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
NewExecutorTest(t,
|
||||||
|
WithName("cli-var-priority-default"),
|
||||||
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/vars"),
|
||||||
|
task.WithSilent(true),
|
||||||
|
),
|
||||||
|
WithTask("cli-var-priority"),
|
||||||
|
)
|
||||||
|
NewExecutorTest(t,
|
||||||
|
WithName("cli-var-priority-override"),
|
||||||
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/vars"),
|
||||||
|
task.WithSilent(true),
|
||||||
|
),
|
||||||
|
WithTask("cli-var-priority"),
|
||||||
|
WithVar("CLI_VAR", "from_cli"),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRequires(t *testing.T) {
|
func TestRequires(t *testing.T) {
|
||||||
@@ -621,6 +638,30 @@ func TestAlias(t *testing.T) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSummaryWithVarsAndRequires(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Test basic case from prompt.md - vars and requires
|
||||||
|
NewExecutorTest(t,
|
||||||
|
WithName("vars-and-requires"),
|
||||||
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/summary-vars-requires"),
|
||||||
|
task.WithSummary(true),
|
||||||
|
),
|
||||||
|
WithTask("mytask"),
|
||||||
|
)
|
||||||
|
|
||||||
|
// Test with shell variables
|
||||||
|
NewExecutorTest(t,
|
||||||
|
WithName("shell-vars"),
|
||||||
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/summary-vars-requires"),
|
||||||
|
task.WithSummary(true),
|
||||||
|
),
|
||||||
|
WithTask("with-sh-var"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
func TestLabel(t *testing.T) {
|
func TestLabel(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -665,6 +706,15 @@ func TestLabel(t *testing.T) {
|
|||||||
),
|
),
|
||||||
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) {
|
||||||
@@ -987,3 +1037,50 @@ func TestIncludeChecksum(t *testing.T) {
|
|||||||
WithFixtureTemplating(),
|
WithFixtureTemplating(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFailfast(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
t.Run("Default", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
NewExecutorTest(t,
|
||||||
|
WithName("default"),
|
||||||
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/failfast/default"),
|
||||||
|
task.WithSilent(true),
|
||||||
|
),
|
||||||
|
WithPostProcessFn(PPSortedLines),
|
||||||
|
WithRunError(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Option", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
NewExecutorTest(t,
|
||||||
|
WithName("default"),
|
||||||
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/failfast/default"),
|
||||||
|
task.WithSilent(true),
|
||||||
|
task.WithFailfast(true),
|
||||||
|
),
|
||||||
|
WithPostProcessFn(PPSortedLines),
|
||||||
|
WithRunError(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Task", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
NewExecutorTest(t,
|
||||||
|
WithName("task"),
|
||||||
|
WithExecutorOptions(
|
||||||
|
task.WithDir("testdata/failfast/task"),
|
||||||
|
task.WithSilent(true),
|
||||||
|
),
|
||||||
|
WithPostProcessFn(PPSortedLines),
|
||||||
|
WithRunError(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -33,14 +33,12 @@ var xList []Experiment
|
|||||||
|
|
||||||
func Parse(dir string) {
|
func Parse(dir string) {
|
||||||
config, _ := taskrc.GetConfig(dir)
|
config, _ := taskrc.GetConfig(dir)
|
||||||
|
|
||||||
ParseWithConfig(dir, config)
|
ParseWithConfig(dir, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseWithConfig(dir string, config *ast.TaskRC) {
|
func ParseWithConfig(dir string, config *ast.TaskRC) {
|
||||||
// Read any .env files
|
// Read any .env files
|
||||||
readDotEnv(dir)
|
readDotEnv(dir)
|
||||||
|
|
||||||
// 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)
|
||||||
|
|||||||
83
go.mod
83
go.mod
@@ -5,62 +5,89 @@ go 1.24.0
|
|||||||
require (
|
require (
|
||||||
github.com/Ladicle/tabwriter v1.0.0
|
github.com/Ladicle/tabwriter v1.0.0
|
||||||
github.com/Masterminds/semver/v3 v3.4.0
|
github.com/Masterminds/semver/v3 v3.4.0
|
||||||
github.com/alecthomas/chroma/v2 v2.20.0
|
github.com/alecthomas/chroma/v2 v2.21.1
|
||||||
github.com/chainguard-dev/git-urls v1.0.2
|
github.com/chainguard-dev/git-urls v1.0.2
|
||||||
github.com/davecgh/go-spew v1.1.1
|
github.com/davecgh/go-spew v1.1.1
|
||||||
github.com/dominikbraun/graph v0.23.0
|
github.com/dominikbraun/graph v0.23.0
|
||||||
github.com/elliotchance/orderedmap/v3 v3.1.0
|
github.com/elliotchance/orderedmap/v3 v3.1.0
|
||||||
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-git/v5 v5.16.2
|
|
||||||
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/hashicorp/go-getter v1.8.3
|
||||||
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/puzpuzpuz/xsync/v3 v3.5.1
|
github.com/puzpuzpuz/xsync/v4 v4.2.0
|
||||||
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.10
|
github.com/spf13/pflag v1.0.10
|
||||||
github.com/stretchr/testify v1.11.1
|
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.17.0
|
go.yaml.in/yaml/v4 v4.0.0-rc.3
|
||||||
golang.org/x/term v0.35.0
|
golang.org/x/sync v0.19.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
golang.org/x/term v0.38.0
|
||||||
mvdan.cc/sh/moreinterp v0.0.0-20250921194925-171492585d48
|
mvdan.cc/sh/moreinterp v0.0.0-20251109230715-65adef8e2c5b
|
||||||
mvdan.cc/sh/v3 v3.12.0
|
mvdan.cc/sh/v3 v3.12.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
dario.cat/mergo v1.0.0 // indirect
|
cloud.google.com/go v0.110.0 // indirect
|
||||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
cloud.google.com/go/compute/metadata v0.3.0 // indirect
|
||||||
github.com/ProtonMail/go-crypto v1.1.6 // indirect
|
cloud.google.com/go/iam v0.13.0 // indirect
|
||||||
github.com/cloudflare/circl v1.6.1 // indirect
|
cloud.google.com/go/storage v1.29.0 // indirect
|
||||||
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
|
github.com/aws/aws-sdk-go-v2 v1.36.3 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/config v1.29.15 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/credentials v1.17.68 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.2 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.80.1 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 // indirect
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sts v1.33.20 // indirect
|
||||||
|
github.com/aws/smithy-go v1.22.3 // indirect
|
||||||
|
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // 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/dustin/go-humanize v1.0.1 // indirect
|
||||||
github.com/emirpasic/gods v1.18.1 // indirect
|
|
||||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
|
||||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
github.com/golang/protobuf v1.5.4 // indirect
|
||||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
github.com/google/go-cmp v0.7.0 // indirect
|
||||||
github.com/klauspost/compress v1.17.4 // indirect
|
github.com/googleapis/enterprise-certificate-proxy v0.2.3 // indirect
|
||||||
|
github.com/googleapis/gax-go/v2 v2.7.1 // indirect
|
||||||
|
github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.65 // indirect
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||||
|
github.com/hashicorp/go-version v1.6.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/klauspost/pgzip v1.2.6 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||||
github.com/pierrec/lz4/v4 v4.1.22 // indirect
|
github.com/pierrec/lz4/v4 v4.1.22 // indirect
|
||||||
github.com/pjbgf/sha1cd v0.3.2 // indirect
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/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/stretchr/objx v0.5.2 // indirect
|
github.com/stretchr/objx v0.5.2 // indirect
|
||||||
github.com/u-root/u-root v0.14.1-0.20250807200646-5e7721023dc7 // indirect
|
github.com/u-root/u-root v0.15.1-0.20251014130006-62f7144b33da // indirect
|
||||||
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect
|
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect
|
||||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
github.com/ulikunitz/xz v0.5.15 // indirect
|
||||||
golang.org/x/crypto v0.37.0 // indirect
|
go.opencensus.io v0.24.0 // indirect
|
||||||
golang.org/x/net v0.39.0 // indirect
|
golang.org/x/net v0.47.0 // indirect
|
||||||
golang.org/x/sys v0.36.0 // indirect
|
golang.org/x/oauth2 v0.27.0 // indirect
|
||||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
golang.org/x/sys v0.39.0 // indirect
|
||||||
|
golang.org/x/text v0.31.0 // indirect
|
||||||
|
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||||
|
google.golang.org/api v0.114.0 // indirect
|
||||||
|
google.golang.org/appengine v1.6.7 // indirect
|
||||||
|
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
|
||||||
|
google.golang.org/grpc v1.56.3 // indirect
|
||||||
|
google.golang.org/protobuf v1.33.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
298
go.sum
298
go.sum
@@ -1,32 +1,72 @@
|
|||||||
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys=
|
||||||
|
cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY=
|
||||||
|
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
|
||||||
|
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
||||||
|
cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k=
|
||||||
|
cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0=
|
||||||
|
cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM=
|
||||||
|
cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo=
|
||||||
|
cloud.google.com/go/storage v1.29.0 h1:6weCgzRvMg7lzuUurI4697AqIRPU1SvzHhynwpW31jI=
|
||||||
|
cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/Ladicle/tabwriter v1.0.0 h1:DZQqPvMumBDwVNElso13afjYLNp0Z7pHqHnu0r4t9Dg=
|
github.com/Ladicle/tabwriter v1.0.0 h1:DZQqPvMumBDwVNElso13afjYLNp0Z7pHqHnu0r4t9Dg=
|
||||||
github.com/Ladicle/tabwriter v1.0.0/go.mod h1:c4MdCjxQyTbGuQO/gvqJ+IA/89UEwrsD6hUCW98dyp4=
|
github.com/Ladicle/tabwriter v1.0.0/go.mod h1:c4MdCjxQyTbGuQO/gvqJ+IA/89UEwrsD6hUCW98dyp4=
|
||||||
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
||||||
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
|
||||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
|
||||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
|
||||||
github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw=
|
|
||||||
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.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/chroma/v2 v2.21.1 h1:FaSDrp6N+3pphkNKU6HPCiYLgm8dbe5UXIXcoBhZSWA=
|
||||||
|
github.com/alecthomas/chroma/v2 v2.21.1/go.mod h1:NqVhfBR0lte5Ouh3DcthuUCTUpDC9cxBOfyMbMQPs3o=
|
||||||
github.com/alecthomas/repr v0.5.1 h1:E3G4t2QbHTSNpPKBgMTln5KLkZHLOcU7r37J4pXBuIg=
|
github.com/alecthomas/repr v0.5.1 h1:E3G4t2QbHTSNpPKBgMTln5KLkZHLOcU7r37J4pXBuIg=
|
||||||
github.com/alecthomas/repr v0.5.1/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/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM=
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg=
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 h1:zAybnyUQXIZ5mok5Jqwlf58/TFE7uvd3IAsa1aF9cXs=
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10/go.mod h1:qqvMj6gHLR/EXWZw4ZbqlPbQUyenf4h82UQUlKc+l14=
|
||||||
|
github.com/aws/aws-sdk-go-v2/config v1.29.15 h1:I5XjesVMpDZXZEZonVfjI12VNMrYa38LtLnw4NtY5Ss=
|
||||||
|
github.com/aws/aws-sdk-go-v2/config v1.29.15/go.mod h1:tNIp4JIPonlsgaO5hxO372a6gjhN63aSWl2GVl5QoBQ=
|
||||||
|
github.com/aws/aws-sdk-go-v2/credentials v1.17.68 h1:cFb9yjI02/sWHBSYXAtkamjzCuRymvmeFmt0TC0MbYY=
|
||||||
|
github.com/aws/aws-sdk-go-v2/credentials v1.17.68/go.mod h1:H6E+jBzyqUu8u0vGaU6POkK3P0NylYEeRZ6ynBpMqIk=
|
||||||
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw=
|
||||||
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34 h1:ZNTqv4nIdE/DiBfUUfXcLZ/Spcuz+RjeziUtNJackkM=
|
||||||
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34/go.mod h1:zf7Vcd1ViW7cPqYWEHLHJkS50X0JS2IKz9Cgaj6ugrs=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.2 h1:BCG7DCXEXpNCcpwCxg1oi9pkJWH2+eZzTn9MY56MbVw=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.2/go.mod h1:iu6FSzgt+M2/x3Dk8zhycdIcHjEFb36IS8HVUVFoMg0=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15 h1:moLQUoVq91LiqT1nbvzDukyqAlCv89ZmwaHw/ZFlFZg=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15/go.mod h1:ZH34PJUc8ApjBIfgQCFvkWcUDBtl/WTD+uiYHjd8igA=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.80.1 h1:xYEAf/6QHiTZDccKnPMbsMwlau13GsDsTgdue3wmHGw=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.80.1/go.mod h1:qbn305Je/IofWBJ4bJz/Q7pDEtnnoInw/dGt71v6rHE=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 h1:1Gw+9ajCV1jogloEv1RRnvfRFia2cL6c9cuKV2Ps+G8=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sso v1.25.3/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 h1:hXmVKytPfTy5axZ+fYbR5d0cFmC3JvwLm5kM83luako=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sts v1.33.20 h1:oIaQ1e17CSKaWmUTu62MtraRWVIosn/iONMuZt0gbqc=
|
||||||
|
github.com/aws/aws-sdk-go-v2/service/sts v1.33.20/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4=
|
||||||
|
github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k=
|
||||||
|
github.com/aws/smithy-go v1.22.3/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
|
||||||
|
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
|
||||||
|
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
|
||||||
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/chainguard-dev/git-urls v1.0.2 h1:pSpT7ifrpc5X55n4aTTm7FFUE+ZQHKiqpiwNkJrVcKQ=
|
github.com/chainguard-dev/git-urls v1.0.2 h1:pSpT7ifrpc5X55n4aTTm7FFUE+ZQHKiqpiwNkJrVcKQ=
|
||||||
github.com/chainguard-dev/git-urls v1.0.2/go.mod h1:rbGgj10OS7UgZlbzdUQIQpT0k/D4+An04HJY7Ol+Y/o=
|
github.com/chainguard-dev/git-urls v1.0.2/go.mod h1:rbGgj10OS7UgZlbzdUQIQpT0k/D4+An04HJY7Ol+Y/o=
|
||||||
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
|
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
|
||||||
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
|
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
|
||||||
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
|
|
||||||
github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@@ -36,48 +76,70 @@ github.com/dominikbraun/graph v0.23.0 h1:TdZB4pPqCLFxYhdyMFb1TBdFxp8XLcJfTTBQucV
|
|||||||
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 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
|
|
||||||
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
|
|
||||||
github.com/elliotchance/orderedmap/v3 v3.1.0 h1:j4DJ5ObEmMBt/lcwIecKcoRxIQUEnw0L804lXYDt/pg=
|
github.com/elliotchance/orderedmap/v3 v3.1.0 h1:j4DJ5ObEmMBt/lcwIecKcoRxIQUEnw0L804lXYDt/pg=
|
||||||
github.com/elliotchance/orderedmap/v3 v3.1.0/go.mod h1:G+Hc2RwaZvJMcS4JpGCOyViCnGeKf0bTYCGTO4uhjSo=
|
github.com/elliotchance/orderedmap/v3 v3.1.0/go.mod h1:G+Hc2RwaZvJMcS4JpGCOyViCnGeKf0bTYCGTO4uhjSo=
|
||||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||||
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
|
||||||
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
|
|
||||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
|
||||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
|
||||||
github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM=
|
|
||||||
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
|
|
||||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
|
||||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
|
||||||
github.com/go-git/go-git/v5 v5.16.2 h1:fT6ZIOjE5iEnkzKyxTHK1W4HGAsPhqEqiSAssSO77hM=
|
|
||||||
github.com/go-git/go-git/v5 v5.16.2/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
|
|
||||||
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
|
github.com/go-quicktest/qt v1.101.0 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=
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||||
github.com/go-task/template v0.2.0 h1:xW7ek0o65FUSTbKcSNeg2Vyf/I7wYXFgLUznptvviBE=
|
github.com/go-task/template v0.2.0 h1:xW7ek0o65FUSTbKcSNeg2Vyf/I7wYXFgLUznptvviBE=
|
||||||
github.com/go-task/template v0.2.0/go.mod h1:dbdoUb6qKnHQi1y6o+IdIrs0J4o/SEhSTA6bbzZmdtc=
|
github.com/go-task/template v0.2.0/go.mod h1:dbdoUb6qKnHQi1y6o+IdIrs0J4o/SEhSTA6bbzZmdtc=
|
||||||
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
|
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
|
||||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
|
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
|
||||||
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||||
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
|
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||||
|
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
|
github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw=
|
||||||
|
github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
|
||||||
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k=
|
||||||
|
github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.7.1 h1:gF4c0zjUP2H/s/hEGyLA3I0fA2ZWjzYiONAD6cvPr8A=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI=
|
||||||
|
github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.65 h1:81+kWbE1yErFBMjME0I5k3x3kojjKsWtPYHEAutoPow=
|
||||||
|
github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.65/go.mod h1:WtMzv9T++tfWVea+qB2MXoaqxw33S8bpJslzUike2mQ=
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||||
|
github.com/hashicorp/go-getter v1.8.3 h1:gIS+oTNv3kyYAvlUVgMR46MiG0bM0KuSON/KZEvRoRg=
|
||||||
|
github.com/hashicorp/go-getter v1.8.3/go.mod h1:CUTt9x2bCtJ/sV8ihgrITL3IUE+0BE1j/e4n5P/GIM4=
|
||||||
|
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
|
||||||
|
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||||
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
||||||
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
|
||||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
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/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||||
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||||
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
|
|
||||||
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||||
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
|
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
|
||||||
@@ -89,111 +151,145 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
|
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
|
||||||
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
|
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
|
||||||
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
|
||||||
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
|
||||||
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
|
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
|
||||||
github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||||
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
|
|
||||||
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
|
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg=
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
|
github.com/puzpuzpuz/xsync/v4 v4.2.0 h1:dlxm77dZj2c3rxq0/XNvvUKISAmovoXF4a4qM6Wvkr0=
|
||||||
|
github.com/puzpuzpuz/xsync/v4 v4.2.0/go.mod h1:VJDmTCJMBt8igNxnkQd86r+8KUeN1quSfNKu5bLYFQo=
|
||||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||||
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/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
|
|
||||||
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
|
|
||||||
github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M=
|
|
||||||
github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
|
||||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||||
github.com/spf13/pflag v1.0.10/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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/stretchr/testify v1.11.0 h1:ib4sjIrwZKxE5u/Japgo/7SJV3PvgjGiRNAvTVGqQl8=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.11.0/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
github.com/u-root/u-root v0.14.1-0.20250807200646-5e7721023dc7 h1:ax+jBy7xFhh+Ka0IGLmH5mft+YDuqvzEjSgWuAP0nsM=
|
github.com/u-root/u-root v0.15.1-0.20251014130006-62f7144b33da h1:Vst9Tvq3G6f6pYBvxy7coi2arDsnOZ3Mkj8MkNarSK8=
|
||||||
github.com/u-root/u-root v0.14.1-0.20250807200646-5e7721023dc7/go.mod h1:/0Qr7qJeDwWxoKku2xKQ4Szc+SwBE3g9VE8jNiamsmc=
|
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 h1:pyC9PaHYZFgEKFdlp3G8RaCKgVpHZnecvArXvPXcFkM=
|
||||||
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA=
|
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/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY=
|
||||||
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||||
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=
|
||||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
go.yaml.in/yaml/v4 v4.0.0-rc.3 h1:3h1fjsh1CTAPjW7q/EMe+C8shx5d8ctzZTrLcs/j8Go=
|
||||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
|
go.yaml.in/yaml/v4 v4.0.0-rc.3/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0=
|
||||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
|
||||||
|
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
|
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
|
||||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ=
|
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||||
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
|
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
|
||||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
|
||||||
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=
|
||||||
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
|
||||||
|
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||||
|
google.golang.org/api v0.114.0 h1:1xQPji6cO2E2vLiI+C/XiFAnsn1WV3mjaEwGLhi3grE=
|
||||||
|
google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg=
|
||||||
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||||
|
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
|
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
|
||||||
|
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
|
||||||
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
|
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||||
|
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||||
|
google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc=
|
||||||
|
google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
|
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||||
|
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||||
|
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
|
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||||
|
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||||
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 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||||
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=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
|
||||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
|
||||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
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.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
mvdan.cc/sh/moreinterp v0.0.0-20250807215248-5a1a658912aa h1:sRmA9AmA5+9CbK6a7N52q9W9jAeoBy1EJ7cncm+SLxw=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
mvdan.cc/sh/moreinterp v0.0.0-20250807215248-5a1a658912aa/go.mod h1:Of9PCedbLDYT8b3EyiYG64rNnx5nOp27OLCVdDrjJyo=
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
mvdan.cc/sh/moreinterp v0.0.0-20250915182820-b717ad599e17 h1:2FU24GcRtL5Idt1KOtmzxU3RiXwirUQQUTV0voIHI2g=
|
mvdan.cc/sh/moreinterp v0.0.0-20251109230715-65adef8e2c5b h1:vTpx76nZDTP/BAGnnhEXYjM+8nPKe9+I86qCErBvjCw=
|
||||||
mvdan.cc/sh/moreinterp v0.0.0-20250915182820-b717ad599e17/go.mod h1:Of9PCedbLDYT8b3EyiYG64rNnx5nOp27OLCVdDrjJyo=
|
mvdan.cc/sh/moreinterp v0.0.0-20251109230715-65adef8e2c5b/go.mod h1:bDyKbUYKqkFunWmxxuSPrkYpln9QZcUsqu7W128qYW4=
|
||||||
mvdan.cc/sh/moreinterp v0.0.0-20250921194925-171492585d48 h1:j0QO6IrQsn7rpqs9HxwwP7ae3uZDbzRZfu0gi8IDFiE=
|
|
||||||
mvdan.cc/sh/moreinterp v0.0.0-20250921194925-171492585d48/go.mod h1:Of9PCedbLDYT8b3EyiYG64rNnx5nOp27OLCVdDrjJyo=
|
|
||||||
mvdan.cc/sh/v3 v3.12.0 h1:ejKUR7ONP5bb+UGHGEG/k9V5+pRVIyD+LsZz7o8KHrI=
|
mvdan.cc/sh/v3 v3.12.0 h1:ejKUR7ONP5bb+UGHGEG/k9V5+pRVIyD+LsZz7o8KHrI=
|
||||||
mvdan.cc/sh/v3 v3.12.0/go.mod h1:Se6Cj17eYSn+sNooLZiEUnNNmNxg0imoYlTu4CyaGyg=
|
mvdan.cc/sh/v3 v3.12.0/go.mod h1:Se6Cj17eYSn+sNooLZiEUnNNmNxg0imoYlTu4CyaGyg=
|
||||||
|
|||||||
25
init.go
25
init.go
@@ -6,9 +6,10 @@ import (
|
|||||||
|
|
||||||
"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"
|
||||||
|
"github.com/go-task/task/v3/taskfile"
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultTaskFilename = "Taskfile.yml"
|
const defaultFilename = "Taskfile.yml"
|
||||||
|
|
||||||
//go:embed taskfile/templates/default.yml
|
//go:embed taskfile/templates/default.yml
|
||||||
var DefaultTaskfile string
|
var DefaultTaskfile string
|
||||||
@@ -20,22 +21,30 @@ var DefaultTaskfile string
|
|||||||
//
|
//
|
||||||
// The final file path is always returned and may be different from the input path.
|
// The final file path is always returned and may be different from the input path.
|
||||||
func InitTaskfile(path string) (string, error) {
|
func InitTaskfile(path string) (string, error) {
|
||||||
fi, err := os.Stat(path)
|
info, err := os.Stat(path)
|
||||||
if err == nil && !fi.IsDir() {
|
if err == nil && !info.IsDir() {
|
||||||
return path, errors.TaskfileAlreadyExistsError{}
|
return path, errors.TaskfileAlreadyExistsError{}
|
||||||
}
|
}
|
||||||
|
|
||||||
if fi != nil && fi.IsDir() {
|
if info != nil && info.IsDir() {
|
||||||
path = filepathext.SmartJoin(path, defaultTaskFilename)
|
// path was a directory, check if there is a Taskfile already
|
||||||
// path was a directory, so check if Taskfile.yml exists in it
|
if hasDefaultTaskfile(path) {
|
||||||
if _, err := os.Stat(path); err == nil {
|
|
||||||
return path, errors.TaskfileAlreadyExistsError{}
|
return path, errors.TaskfileAlreadyExistsError{}
|
||||||
}
|
}
|
||||||
|
path = filepathext.SmartJoin(path, defaultFilename)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.WriteFile(path, []byte(DefaultTaskfile), 0o644); err != nil {
|
if err := os.WriteFile(path, []byte(DefaultTaskfile), 0o644); err != nil {
|
||||||
return path, err
|
return path, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return path, nil
|
return path, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func hasDefaultTaskfile(dir string) bool {
|
||||||
|
for _, name := range taskfile.DefaultTaskfiles {
|
||||||
|
if _, err := os.Stat(filepathext.SmartJoin(dir, name)); err == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ func execHandlers() (handlers []func(next interp.ExecHandlerFunc) interp.ExecHan
|
|||||||
if useGoCoreUtils {
|
if useGoCoreUtils {
|
||||||
handlers = append(handlers, coreutils.ExecHandler)
|
handlers = append(handlers, coreutils.ExecHandler)
|
||||||
}
|
}
|
||||||
return
|
return handlers
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
|
|||||||
@@ -5,13 +5,16 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/fatih/color"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
|
||||||
"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/ast"
|
"github.com/go-task/task/v3/taskfile/ast"
|
||||||
"github.com/go-task/task/v3/taskrc"
|
"github.com/go-task/task/v3/taskrc"
|
||||||
@@ -58,6 +61,7 @@ var (
|
|||||||
Watch bool
|
Watch bool
|
||||||
Verbose bool
|
Verbose bool
|
||||||
Silent bool
|
Silent bool
|
||||||
|
DisableFuzzy bool
|
||||||
AssumeYes bool
|
AssumeYes bool
|
||||||
Dry bool
|
Dry bool
|
||||||
Summary bool
|
Summary bool
|
||||||
@@ -69,13 +73,16 @@ var (
|
|||||||
Output ast.Output
|
Output ast.Output
|
||||||
Color bool
|
Color bool
|
||||||
Interval time.Duration
|
Interval time.Duration
|
||||||
|
Failfast bool
|
||||||
Global bool
|
Global bool
|
||||||
Experiments bool
|
Experiments bool
|
||||||
Download bool
|
Download bool
|
||||||
Offline bool
|
Offline bool
|
||||||
|
TrustedHosts []string
|
||||||
ClearCache bool
|
ClearCache bool
|
||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
CacheExpiryDuration time.Duration
|
CacheExpiryDuration time.Duration
|
||||||
|
RemoteCacheDir string
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -123,6 +130,7 @@ func init() {
|
|||||||
pflag.BoolVarP(&Watch, "watch", "w", false, "Enables watch of the given task.")
|
pflag.BoolVarP(&Watch, "watch", "w", false, "Enables watch of the given task.")
|
||||||
pflag.BoolVarP(&Verbose, "verbose", "v", getConfig(config, func() *bool { return config.Verbose }, false), "Enables verbose mode.")
|
pflag.BoolVarP(&Verbose, "verbose", "v", 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.BoolVar(&DisableFuzzy, "disable-fuzzy", getConfig(config, func() *bool { return config.DisableFuzzy }, false), "Disables fuzzy matching for task names.")
|
||||||
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.")
|
||||||
pflag.BoolVarP(&Dry, "dry", "n", false, "Compiles and prints tasks in the order that they would be run, without executing them.")
|
pflag.BoolVarP(&Dry, "dry", "n", false, "Compiles and prints tasks in the order that they would be run, without executing them.")
|
||||||
@@ -134,9 +142,10 @@ func init() {
|
|||||||
pflag.StringVar(&Output.Group.Begin, "output-group-begin", "", "Message template to print before a task's grouped output.")
|
pflag.StringVar(&Output.Group.Begin, "output-group-begin", "", "Message template to print before a task's grouped output.")
|
||||||
pflag.StringVar(&Output.Group.End, "output-group-end", "", "Message template to print after a task's grouped output.")
|
pflag.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", getConfig(config, func() *bool { return config.Color }, true), "Colored output. Enabled by default. Set flag to false or use NO_COLOR=1 to disable.")
|
||||||
pflag.IntVarP(&Concurrency, "concurrency", "C", getConfig(config, func() *int { return config.Concurrency }, 0), "Limit number of tasks to run concurrently.")
|
pflag.IntVarP(&Concurrency, "concurrency", "C", 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(&Failfast, "failfast", "F", getConfig(config, func() *bool { return &config.Failfast }, false), "When running tasks in parallel, stop all tasks if one fails.")
|
||||||
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.")
|
||||||
|
|
||||||
@@ -152,11 +161,36 @@ func init() {
|
|||||||
if experiments.RemoteTaskfiles.Enabled() {
|
if experiments.RemoteTaskfiles.Enabled() {
|
||||||
pflag.BoolVar(&Download, "download", false, "Downloads a cached version of a remote Taskfile.")
|
pflag.BoolVar(&Download, "download", false, "Downloads a cached version of a remote Taskfile.")
|
||||||
pflag.BoolVar(&Offline, "offline", getConfig(config, func() *bool { return config.Remote.Offline }, false), "Forces Task to only use local or cached Taskfiles.")
|
pflag.BoolVar(&Offline, "offline", getConfig(config, func() *bool { return config.Remote.Offline }, false), "Forces Task to only use local or cached Taskfiles.")
|
||||||
|
pflag.StringSliceVar(&TrustedHosts, "trusted-hosts", getConfig(config, func() *[]string { return &config.Remote.TrustedHosts }, nil), "List of trusted hosts for remote Taskfiles (comma-separated).")
|
||||||
pflag.DurationVar(&Timeout, "timeout", getConfig(config, func() *time.Duration { return config.Remote.Timeout }, time.Second*10), "Timeout for downloading remote Taskfiles.")
|
pflag.DurationVar(&Timeout, "timeout", 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", getConfig(config, func() *time.Duration { return config.Remote.CacheExpiry }, 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.StringVar(&RemoteCacheDir, "remote-cache-dir", getConfig(config, func() *string { return config.Remote.CacheDir }, env.GetTaskEnv("REMOTE_DIR")), "Directory to cache remote Taskfiles.")
|
||||||
}
|
}
|
||||||
pflag.Parse()
|
pflag.Parse()
|
||||||
|
|
||||||
|
// Auto-detect color based on environment when not explicitly configured
|
||||||
|
// Priority: CLI flag > taskrc config > NO_COLOR > FORCE_COLOR/CI > default
|
||||||
|
colorExplicitlySet := pflag.Lookup("color").Changed || (config != nil && config.Color != nil)
|
||||||
|
if !colorExplicitlySet {
|
||||||
|
if os.Getenv("NO_COLOR") != "" {
|
||||||
|
Color = false
|
||||||
|
color.NoColor = true
|
||||||
|
} else if os.Getenv("FORCE_COLOR") != "" || isCI() {
|
||||||
|
Color = true
|
||||||
|
color.NoColor = false // Force colors even without TTY
|
||||||
|
}
|
||||||
|
// Otherwise, let fatih/color auto-detect TTY
|
||||||
|
} else {
|
||||||
|
// Explicit config: sync with fatih/color
|
||||||
|
color.NoColor = !Color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// isCI returns true if running in a CI environment
|
||||||
|
func isCI() bool {
|
||||||
|
ci, _ := strconv.ParseBool(os.Getenv("CI"))
|
||||||
|
return ci
|
||||||
}
|
}
|
||||||
|
|
||||||
func Validate() error {
|
func Validate() error {
|
||||||
@@ -238,11 +272,14 @@ func (o *flagsOption) ApplyToExecutor(e *task.Executor) {
|
|||||||
task.WithInsecure(Insecure),
|
task.WithInsecure(Insecure),
|
||||||
task.WithDownload(Download),
|
task.WithDownload(Download),
|
||||||
task.WithOffline(Offline),
|
task.WithOffline(Offline),
|
||||||
|
task.WithTrustedHosts(TrustedHosts),
|
||||||
task.WithTimeout(Timeout),
|
task.WithTimeout(Timeout),
|
||||||
task.WithCacheExpiryDuration(CacheExpiryDuration),
|
task.WithCacheExpiryDuration(CacheExpiryDuration),
|
||||||
|
task.WithRemoteCacheDir(RemoteCacheDir),
|
||||||
task.WithWatch(Watch),
|
task.WithWatch(Watch),
|
||||||
task.WithVerbose(Verbose),
|
task.WithVerbose(Verbose),
|
||||||
task.WithSilent(Silent),
|
task.WithSilent(Silent),
|
||||||
|
task.WithDisableFuzzy(DisableFuzzy),
|
||||||
task.WithAssumeYes(AssumeYes),
|
task.WithAssumeYes(AssumeYes),
|
||||||
task.WithDry(Dry || Status),
|
task.WithDry(Dry || Status),
|
||||||
task.WithSummary(Summary),
|
task.WithSummary(Summary),
|
||||||
@@ -253,6 +290,7 @@ func (o *flagsOption) ApplyToExecutor(e *task.Executor) {
|
|||||||
task.WithOutputStyle(Output),
|
task.WithOutputStyle(Output),
|
||||||
task.WithTaskSorter(sorter),
|
task.WithTaskSorter(sorter),
|
||||||
task.WithVersionCheck(true),
|
task.WithVersionCheck(true),
|
||||||
|
task.WithFailfast(Failfast),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,5 +20,5 @@ func Name(t *ast.Task) (string, error) {
|
|||||||
|
|
||||||
func Hash(t *ast.Task) (string, error) {
|
func Hash(t *ast.Task) (string, error) {
|
||||||
h, err := hashstructure.Hash(t, hashstructure.FormatV2, nil)
|
h, err := hashstructure.Hash(t, hashstructure.FormatV2, nil)
|
||||||
return fmt.Sprintf("%s:%d", t.Task, h), err
|
return fmt.Sprintf("%s:%s:%d", t.Location.Taskfile, t.LocalName(), h), err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package logger
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -96,10 +95,6 @@ func BrightRed() PrintFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func envColor(name string, defaultColor color.Attribute) []color.Attribute {
|
func envColor(name string, defaultColor color.Attribute) []color.Attribute {
|
||||||
if os.Getenv("FORCE_COLOR") != "" {
|
|
||||||
color.NoColor = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch the environment variable
|
// Fetch the environment variable
|
||||||
override := env.GetTaskEnv(name)
|
override := env.GetTaskEnv(name)
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package summary
|
package summary
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/internal/logger"
|
"github.com/go-task/task/v3/internal/logger"
|
||||||
@@ -29,6 +31,9 @@ func PrintSpaceBetweenSummaries(l *logger.Logger, i int) {
|
|||||||
func PrintTask(l *logger.Logger, t *ast.Task) {
|
func PrintTask(l *logger.Logger, t *ast.Task) {
|
||||||
printTaskName(l, t)
|
printTaskName(l, t)
|
||||||
printTaskDescribingText(t, l)
|
printTaskDescribingText(t, l)
|
||||||
|
printTaskVars(l, t)
|
||||||
|
printTaskEnv(l, t)
|
||||||
|
printTaskRequires(l, t)
|
||||||
printTaskDependencies(l, t)
|
printTaskDependencies(l, t)
|
||||||
printTaskAliases(l, t)
|
printTaskAliases(l, t)
|
||||||
printTaskCommands(l, t)
|
printTaskCommands(l, t)
|
||||||
@@ -118,3 +123,168 @@ func printTaskCommands(l *logger.Logger, t *ast.Task) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func printTaskVars(l *logger.Logger, t *ast.Task) {
|
||||||
|
if t.Vars == nil || t.Vars.Len() == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
osEnvVars := getEnvVarNames()
|
||||||
|
|
||||||
|
taskfileEnvVars := make(map[string]bool)
|
||||||
|
if t.Env != nil {
|
||||||
|
for key := range t.Env.All() {
|
||||||
|
taskfileEnvVars[key] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hasNonEnvVars := false
|
||||||
|
for key := range t.Vars.All() {
|
||||||
|
if !isEnvVar(key, osEnvVars) && !taskfileEnvVars[key] {
|
||||||
|
hasNonEnvVars = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasNonEnvVars {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Outf(logger.Default, "\n")
|
||||||
|
l.Outf(logger.Default, "vars:\n")
|
||||||
|
|
||||||
|
for key, value := range t.Vars.All() {
|
||||||
|
// Only display variables that are not from OS environment or Taskfile env
|
||||||
|
if !isEnvVar(key, osEnvVars) && !taskfileEnvVars[key] {
|
||||||
|
formattedValue := formatVarValue(value)
|
||||||
|
l.Outf(logger.Yellow, " %s: %s\n", key, formattedValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func printTaskEnv(l *logger.Logger, t *ast.Task) {
|
||||||
|
if t.Env == nil || t.Env.Len() == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
envVars := getEnvVarNames()
|
||||||
|
|
||||||
|
hasNonEnvVars := false
|
||||||
|
for key := range t.Env.All() {
|
||||||
|
if !isEnvVar(key, envVars) {
|
||||||
|
hasNonEnvVars = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasNonEnvVars {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Outf(logger.Default, "\n")
|
||||||
|
l.Outf(logger.Default, "env:\n")
|
||||||
|
|
||||||
|
for key, value := range t.Env.All() {
|
||||||
|
// Only display variables that are not from OS environment
|
||||||
|
if !isEnvVar(key, envVars) {
|
||||||
|
formattedValue := formatVarValue(value)
|
||||||
|
l.Outf(logger.Yellow, " %s: %s\n", key, formattedValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// formatVarValue formats a variable value based on its type.
|
||||||
|
// Handles static values, shell commands (sh:), references (ref:), and maps.
|
||||||
|
func formatVarValue(v ast.Var) string {
|
||||||
|
// Shell command - check this first before Value
|
||||||
|
// because dynamic vars may have both Sh and an empty Value
|
||||||
|
if v.Sh != nil {
|
||||||
|
return fmt.Sprintf("sh: %s", *v.Sh)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reference
|
||||||
|
if v.Ref != "" {
|
||||||
|
return fmt.Sprintf("ref: %s", v.Ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Static value
|
||||||
|
if v.Value != nil {
|
||||||
|
// Check if it's a map or complex type
|
||||||
|
if m, ok := v.Value.(map[string]any); ok {
|
||||||
|
return formatMap(m, 4)
|
||||||
|
}
|
||||||
|
// Simple string value
|
||||||
|
return fmt.Sprintf(`"%v"`, v.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return `""`
|
||||||
|
}
|
||||||
|
|
||||||
|
// formatMap formats a map value with proper indentation for YAML.
|
||||||
|
func formatMap(m map[string]any, indent int) string {
|
||||||
|
if len(m) == 0 {
|
||||||
|
return "{}"
|
||||||
|
}
|
||||||
|
|
||||||
|
var result strings.Builder
|
||||||
|
result.WriteString("\n")
|
||||||
|
spaces := strings.Repeat(" ", indent)
|
||||||
|
|
||||||
|
for k, v := range m {
|
||||||
|
result.WriteString(fmt.Sprintf("%s%s: %v\n", spaces, k, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func printTaskRequires(l *logger.Logger, t *ast.Task) {
|
||||||
|
if t.Requires == nil || len(t.Requires.Vars) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Outf(logger.Default, "\n")
|
||||||
|
l.Outf(logger.Default, "requires:\n")
|
||||||
|
l.Outf(logger.Default, " vars:\n")
|
||||||
|
|
||||||
|
for _, v := range t.Requires.Vars {
|
||||||
|
// If the variable has enum constraints, format accordingly
|
||||||
|
if len(v.Enum) > 0 {
|
||||||
|
l.Outf(logger.Yellow, " - %s:\n", v.Name)
|
||||||
|
l.Outf(logger.Yellow, " enum:\n")
|
||||||
|
for _, enumValue := range v.Enum {
|
||||||
|
l.Outf(logger.Yellow, " - %s\n", enumValue)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Simple required variable
|
||||||
|
l.Outf(logger.Yellow, " - %s\n", v.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getEnvVarNames() map[string]bool {
|
||||||
|
envMap := make(map[string]bool)
|
||||||
|
for _, e := range os.Environ() {
|
||||||
|
parts := strings.SplitN(e, "=", 2)
|
||||||
|
if len(parts) > 0 {
|
||||||
|
envMap[parts[0]] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return envMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// isEnvVar checks if a variable is from OS environment or auto-generated by Task.
|
||||||
|
func isEnvVar(key string, envVars map[string]bool) bool {
|
||||||
|
// Filter out auto-generated Task variables
|
||||||
|
if strings.HasPrefix(key, "TASK_") ||
|
||||||
|
strings.HasPrefix(key, "CLI_") ||
|
||||||
|
strings.HasPrefix(key, "ROOT_") ||
|
||||||
|
key == "TASK" ||
|
||||||
|
key == "TASKFILE" ||
|
||||||
|
key == "TASKFILE_DIR" ||
|
||||||
|
key == "USER_WORKING_DIR" ||
|
||||||
|
key == "ALIAS" ||
|
||||||
|
key == "MATCH" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return envVars[key]
|
||||||
|
}
|
||||||
|
|||||||
@@ -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.45.4
|
3.46.4
|
||||||
|
|||||||
14
setup.go
14
setup.go
@@ -36,7 +36,6 @@ func (e *Executor) Setup() error {
|
|||||||
if err := e.readTaskfile(node); err != nil {
|
if err := e.readTaskfile(node); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
e.setupFuzzyModel()
|
|
||||||
e.setupStdFiles()
|
e.setupStdFiles()
|
||||||
if err := e.setupOutput(); err != nil {
|
if err := e.setupOutput(); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -84,6 +83,7 @@ func (e *Executor) readTaskfile(node taskfile.Node) error {
|
|||||||
taskfile.WithInsecure(e.Insecure),
|
taskfile.WithInsecure(e.Insecure),
|
||||||
taskfile.WithDownload(e.Download),
|
taskfile.WithDownload(e.Download),
|
||||||
taskfile.WithOffline(e.Offline),
|
taskfile.WithOffline(e.Offline),
|
||||||
|
taskfile.WithTrustedHosts(e.TrustedHosts),
|
||||||
taskfile.WithTempDir(e.TempDir.Remote),
|
taskfile.WithTempDir(e.TempDir.Remote),
|
||||||
taskfile.WithCacheExpiryDuration(e.CacheExpiryDuration),
|
taskfile.WithCacheExpiryDuration(e.CacheExpiryDuration),
|
||||||
taskfile.WithDebugFunc(debugFunc),
|
taskfile.WithDebugFunc(debugFunc),
|
||||||
@@ -153,16 +153,16 @@ func (e *Executor) setupTempDir() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteDir := env.GetTaskEnv("REMOTE_DIR")
|
// RemoteCacheDir from taskrc/env can override the remote cache directory
|
||||||
if remoteDir != "" {
|
if e.RemoteCacheDir != "" {
|
||||||
if filepath.IsAbs(remoteDir) || strings.HasPrefix(remoteDir, "~") {
|
if filepath.IsAbs(e.RemoteCacheDir) || strings.HasPrefix(e.RemoteCacheDir, "~") {
|
||||||
remoteTempDir, err := execext.ExpandLiteral(remoteDir)
|
remoteCacheDir, err := execext.ExpandLiteral(e.RemoteCacheDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
e.TempDir.Remote = remoteTempDir
|
e.TempDir.Remote = remoteCacheDir
|
||||||
} else {
|
} else {
|
||||||
e.TempDir.Remote = filepathext.SmartJoin(e.Dir, ".task")
|
e.TempDir.Remote = filepathext.SmartJoin(e.Dir, e.RemoteCacheDir)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
57
task.go
57
task.go
@@ -78,9 +78,11 @@ func (e *Executor) Run(ctx context.Context, calls ...*Call) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
g, ctx := errgroup.WithContext(ctx)
|
g := &errgroup.Group{}
|
||||||
|
if e.Failfast {
|
||||||
|
g, ctx = errgroup.WithContext(ctx)
|
||||||
|
}
|
||||||
for _, c := range regularCalls {
|
for _, c := range regularCalls {
|
||||||
c := c
|
|
||||||
if e.Parallel {
|
if e.Parallel {
|
||||||
g.Go(func() error { return e.RunTask(ctx, c) })
|
g.Go(func() error { return e.RunTask(ctx, c) })
|
||||||
} else {
|
} else {
|
||||||
@@ -113,7 +115,7 @@ func (e *Executor) splitRegularAndWatchCalls(calls ...*Call) (regularCalls []*Ca
|
|||||||
regularCalls = append(regularCalls, c)
|
regularCalls = append(regularCalls, c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return regularCalls, watchCalls, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunTask runs a task by its name
|
// RunTask runs a task by its name
|
||||||
@@ -150,7 +152,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
|
||||||
@@ -210,7 +212,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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,16 +230,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 {
|
||||||
@@ -258,13 +260,15 @@ func (e *Executor) mkdir(t *ast.Task) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *Executor) runDeps(ctx context.Context, t *ast.Task) error {
|
func (e *Executor) runDeps(ctx context.Context, t *ast.Task) error {
|
||||||
g, ctx := errgroup.WithContext(ctx)
|
g := &errgroup.Group{}
|
||||||
|
if e.Failfast || t.Failfast {
|
||||||
|
g, ctx = errgroup.WithContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
reacquire := e.releaseConcurrencyLimit()
|
reacquire := e.releaseConcurrencyLimit()
|
||||||
defer reacquire()
|
defer reacquire()
|
||||||
|
|
||||||
for _, d := range t.Deps {
|
for _, d := range t.Deps {
|
||||||
d := d
|
|
||||||
g.Go(func() error {
|
g.Go(func() error {
|
||||||
err := e.RunTask(ctx, &Call{Task: d.Task, Vars: d.Vars, Silent: d.Silent, Indirect: true})
|
err := e.RunTask(ctx, &Call{Task: d.Task, Vars: d.Vars, Silent: d.Silent, Indirect: true})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -277,17 +281,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{}
|
||||||
|
|
||||||
@@ -313,10 +311,12 @@ func (e *Executor) runCommand(ctx context.Context, t *ast.Task, call *Call, i in
|
|||||||
defer reacquire()
|
defer reacquire()
|
||||||
|
|
||||||
err := e.RunTask(ctx, &Call{Task: cmd.Task, Vars: cmd.Vars, Silent: cmd.Silent, Indirect: true})
|
err := e.RunTask(ctx, &Call{Task: cmd.Task, Vars: cmd.Vars, Silent: cmd.Silent, Indirect: true})
|
||||||
if err != nil {
|
var exitCode interp.ExitStatus
|
||||||
return err
|
if errors.As(err, &exitCode) && cmd.IgnoreError {
|
||||||
|
e.Logger.VerboseErrf(logger.Yellow, "task: [%s] task error ignored: %v\n", t.Name(), err)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
return nil
|
return err
|
||||||
case cmd.Cmd != "":
|
case cmd.Cmd != "":
|
||||||
if !shouldRunOnCurrentPlatform(cmd.Platforms) {
|
if !shouldRunOnCurrentPlatform(cmd.Platforms) {
|
||||||
e.Logger.VerboseOutf(logger.Yellow, "task: [%s] %s not for current platform - ignored\n", t.Name(), cmd.Cmd)
|
e.Logger.VerboseOutf(logger.Yellow, "task: [%s] %s not for current platform - ignored\n", t.Name(), cmd.Cmd)
|
||||||
@@ -372,7 +372,7 @@ func (e *Executor) startExecution(ctx context.Context, t *ast.Task, execute func
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if h == "" {
|
if h == "" || t.Watch {
|
||||||
return execute(ctx)
|
return execute(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -458,8 +458,11 @@ func (e *Executor) GetTask(call *Call) (*ast.Task, error) {
|
|||||||
// If we found no tasks
|
// If we found no tasks
|
||||||
if len(aliasedTasks) == 0 {
|
if len(aliasedTasks) == 0 {
|
||||||
didYouMean := ""
|
didYouMean := ""
|
||||||
if e.fuzzyModel != nil {
|
if !e.DisableFuzzy {
|
||||||
didYouMean = e.fuzzyModel.SpellCheck(call.Task)
|
e.fuzzyModelOnce.Do(e.setupFuzzyModel)
|
||||||
|
if e.fuzzyModel != nil {
|
||||||
|
didYouMean = e.fuzzyModel.SpellCheck(call.Task)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil, &errors.TaskNotFoundError{
|
return nil, &errors.TaskNotFoundError{
|
||||||
TaskName: call.Task,
|
TaskName: call.Task,
|
||||||
@@ -498,7 +501,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
|
||||||
}
|
}
|
||||||
|
|||||||
54
task_test.go
54
task_test.go
@@ -9,6 +9,7 @@ import (
|
|||||||
rand "math/rand/v2"
|
rand "math/rand/v2"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
@@ -569,7 +570,9 @@ func TestCyclicDep(t *testing.T) {
|
|||||||
task.WithStderr(io.Discard),
|
task.WithStderr(io.Discard),
|
||||||
)
|
)
|
||||||
require.NoError(t, e.Setup())
|
require.NoError(t, e.Setup())
|
||||||
assert.IsType(t, &errors.TaskCalledTooManyTimesError{}, e.Run(t.Context(), &task.Call{Task: "task-1"}))
|
err := e.Run(t.Context(), &task.Call{Task: "task-1"})
|
||||||
|
var taskCalledTooManyTimesError *errors.TaskCalledTooManyTimesError
|
||||||
|
assert.ErrorAs(t, err, &taskCalledTooManyTimesError)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTaskVersion(t *testing.T) {
|
func TestTaskVersion(t *testing.T) {
|
||||||
@@ -784,6 +787,11 @@ func TestIncludesRemote(t *testing.T) {
|
|||||||
|
|
||||||
var buff SyncBuffer
|
var buff SyncBuffer
|
||||||
|
|
||||||
|
// Extract host from server URL for trust testing
|
||||||
|
parsedURL, err := url.Parse(srv.URL)
|
||||||
|
require.NoError(t, err)
|
||||||
|
trustedHost := parsedURL.Host
|
||||||
|
|
||||||
executors := []struct {
|
executors := []struct {
|
||||||
name string
|
name string
|
||||||
executor *task.Executor
|
executor *task.Executor
|
||||||
@@ -823,6 +831,23 @@ func TestIncludesRemote(t *testing.T) {
|
|||||||
task.WithOffline(true),
|
task.WithOffline(true),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "with trusted hosts, no prompts",
|
||||||
|
executor: task.NewExecutor(
|
||||||
|
task.WithDir(dir),
|
||||||
|
task.WithStdout(&buff),
|
||||||
|
task.WithStderr(&buff),
|
||||||
|
task.WithTimeout(time.Minute),
|
||||||
|
task.WithInsecure(true),
|
||||||
|
task.WithStdout(&buff),
|
||||||
|
task.WithStderr(&buff),
|
||||||
|
task.WithVerbose(true),
|
||||||
|
|
||||||
|
// With trusted hosts
|
||||||
|
task.WithTrustedHosts([]string{trustedHost}),
|
||||||
|
task.WithDownload(true),
|
||||||
|
),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, e := range executors {
|
for _, e := range executors {
|
||||||
@@ -1052,7 +1077,7 @@ func TestIncludesOptionalImplicitFalse(t *testing.T) {
|
|||||||
const dir = "testdata/includes_optional_implicit_false"
|
const dir = "testdata/includes_optional_implicit_false"
|
||||||
wd, _ := os.Getwd()
|
wd, _ := os.Getwd()
|
||||||
|
|
||||||
message := "stat %s/%s/TaskfileOptional.yml: no such file or directory"
|
message := "task: No Taskfile found at \"%s/%s/TaskfileOptional.yml\""
|
||||||
expected := fmt.Sprintf(message, wd, dir)
|
expected := fmt.Sprintf(message, wd, dir)
|
||||||
|
|
||||||
e := task.NewExecutor(
|
e := task.NewExecutor(
|
||||||
@@ -1072,7 +1097,7 @@ func TestIncludesOptionalExplicitFalse(t *testing.T) {
|
|||||||
const dir = "testdata/includes_optional_explicit_false"
|
const dir = "testdata/includes_optional_explicit_false"
|
||||||
wd, _ := os.Getwd()
|
wd, _ := os.Getwd()
|
||||||
|
|
||||||
message := "stat %s/%s/TaskfileOptional.yml: no such file or directory"
|
message := "task: No Taskfile found at \"%s/%s/TaskfileOptional.yml\""
|
||||||
expected := fmt.Sprintf(message, wd, dir)
|
expected := fmt.Sprintf(message, wd, dir)
|
||||||
|
|
||||||
e := task.NewExecutor(
|
e := task.NewExecutor(
|
||||||
@@ -1849,6 +1874,29 @@ func TestRunOnceSharedDeps(t *testing.T) {
|
|||||||
assert.Contains(t, buff.String(), `task: [service-b:build] echo "build b"`)
|
assert.Contains(t, buff.String(), `task: [service-b:build] echo "build b"`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRunWhenChanged(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
const dir = "testdata/run_when_changed"
|
||||||
|
|
||||||
|
var buff bytes.Buffer
|
||||||
|
e := task.NewExecutor(
|
||||||
|
task.WithDir(dir),
|
||||||
|
task.WithStdout(&buff),
|
||||||
|
task.WithStderr(&buff),
|
||||||
|
task.WithForceAll(true),
|
||||||
|
task.WithSilent(true),
|
||||||
|
)
|
||||||
|
require.NoError(t, e.Setup())
|
||||||
|
require.NoError(t, e.Run(t.Context(), &task.Call{Task: "start"}))
|
||||||
|
expectedOutputOrder := strings.TrimSpace(`
|
||||||
|
login server=fubar user=fubar
|
||||||
|
login server=foo user=foo
|
||||||
|
login server=bar user=bar
|
||||||
|
`)
|
||||||
|
assert.Contains(t, buff.String(), expectedOutputOrder)
|
||||||
|
}
|
||||||
|
|
||||||
func TestDeferredCmds(t *testing.T) {
|
func TestDeferredCmds(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
@@ -93,6 +93,7 @@ func (c *Cmd) UnmarshalYAML(node *yaml.Node) error {
|
|||||||
c.Vars = cmdStruct.Vars
|
c.Vars = cmdStruct.Vars
|
||||||
c.For = cmdStruct.For
|
c.For = cmdStruct.For
|
||||||
c.Silent = cmdStruct.Silent
|
c.Silent = cmdStruct.Silent
|
||||||
|
c.IgnoreError = cmdStruct.IgnoreError
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
|
|
||||||
// Task represents a task
|
// Task represents a task
|
||||||
type Task struct {
|
type Task struct {
|
||||||
Task string
|
Task string `hash:"ignore"`
|
||||||
Cmds []*Cmd
|
Cmds []*Cmd
|
||||||
Deps []*Dep
|
Deps []*Dep
|
||||||
Label string
|
Label string
|
||||||
@@ -36,18 +36,19 @@ type Task struct {
|
|||||||
Interactive bool
|
Interactive bool
|
||||||
Internal bool
|
Internal bool
|
||||||
Method string
|
Method string
|
||||||
Prefix string
|
Prefix string `hash:"ignore"`
|
||||||
IgnoreError bool
|
IgnoreError bool
|
||||||
Run string
|
Run string
|
||||||
Platforms []*Platform
|
Platforms []*Platform
|
||||||
Watch bool
|
Watch bool
|
||||||
Location *Location
|
Location *Location
|
||||||
|
Failfast bool
|
||||||
// Populated during merging
|
// Populated during merging
|
||||||
Namespace string
|
Namespace string `hash:"ignore"`
|
||||||
IncludeVars *Vars
|
IncludeVars *Vars
|
||||||
IncludedTaskfileVars *Vars
|
IncludedTaskfileVars *Vars
|
||||||
|
|
||||||
FullName string
|
FullName string `hash:"ignore"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Task) Name() string {
|
func (t *Task) Name() string {
|
||||||
@@ -143,6 +144,7 @@ func (t *Task) UnmarshalYAML(node *yaml.Node) error {
|
|||||||
Platforms []*Platform
|
Platforms []*Platform
|
||||||
Requires *Requires
|
Requires *Requires
|
||||||
Watch bool
|
Watch bool
|
||||||
|
Failfast bool
|
||||||
}
|
}
|
||||||
if err := node.Decode(&task); err != nil {
|
if err := node.Decode(&task); err != nil {
|
||||||
return errors.NewTaskfileDecodeError(err, node)
|
return errors.NewTaskfileDecodeError(err, node)
|
||||||
@@ -181,6 +183,7 @@ func (t *Task) UnmarshalYAML(node *yaml.Node) error {
|
|||||||
t.Platforms = task.Platforms
|
t.Platforms = task.Platforms
|
||||||
t.Requires = task.Requires
|
t.Requires = task.Requires
|
||||||
t.Watch = task.Watch
|
t.Watch = task.Watch
|
||||||
|
t.Failfast = task.Failfast
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,6 +229,7 @@ func (t *Task) DeepCopy() *Task {
|
|||||||
Requires: t.Requires.DeepCopy(),
|
Requires: t.Requires.DeepCopy(),
|
||||||
Namespace: t.Namespace,
|
Namespace: t.Namespace,
|
||||||
FullName: t.FullName,
|
FullName: t.FullName,
|
||||||
|
Failfast: t.Failfast,
|
||||||
}
|
}
|
||||||
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"
|
||||||
@@ -244,8 +244,8 @@ func (t *Tasks) UnmarshalYAML(node *yaml.Node) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func taskNameWithNamespace(taskName string, namespace string) string {
|
func taskNameWithNamespace(taskName string, namespace string) string {
|
||||||
if strings.HasPrefix(taskName, NamespaceSeparator) {
|
if after, ok := strings.CutPrefix(taskName, NamespaceSeparator); ok {
|
||||||
return strings.TrimPrefix(taskName, NamespaceSeparator)
|
return after
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%s%s%s", namespace, NamespaceSeparator, taskName)
|
return fmt.Sprintf("%s%s%s", namespace, NamespaceSeparator, taskName)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
@@ -113,12 +113,12 @@ 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
|
||||||
// the include parameter is not nil and its it is an advanced import, the
|
// the include parameter is not nil and its it is an advanced import, the
|
||||||
// directory is set set to the value of the include parameter.
|
// directory is set to the value of the include parameter.
|
||||||
func (vars *Vars) Merge(other *Vars, include *Include) {
|
func (vars *Vars) Merge(other *Vars, include *Include) {
|
||||||
if vars == nil || vars.om == nil || other == nil {
|
if vars == nil || vars.om == nil || other == nil {
|
||||||
return
|
return
|
||||||
@@ -133,6 +133,35 @@ func (vars *Vars) Merge(other *Vars, include *Include) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReverseMerge merges other variables with the existing variables in vars, but
|
||||||
|
// keeps the other variables first in order. If the include parameter is not
|
||||||
|
// nil and it is an advanced import, the directory is set to the value of the
|
||||||
|
// include parameter.
|
||||||
|
func (vars *Vars) ReverseMerge(other *Vars, include *Include) {
|
||||||
|
if vars == nil || vars.om == nil || other == nil || other.om == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
newOM := orderedmap.NewOrderedMap[string, Var]()
|
||||||
|
|
||||||
|
other.mutex.RLock()
|
||||||
|
for pair := other.om.Front(); pair != nil; pair = pair.Next() {
|
||||||
|
val := pair.Value
|
||||||
|
if include != nil && include.AdvancedImport {
|
||||||
|
val.Dir = include.Dir
|
||||||
|
}
|
||||||
|
newOM.Set(pair.Key, val)
|
||||||
|
}
|
||||||
|
other.mutex.RUnlock()
|
||||||
|
|
||||||
|
vars.mutex.Lock()
|
||||||
|
for pair := vars.om.Front(); pair != nil; pair = pair.Next() {
|
||||||
|
newOM.Set(pair.Key, pair.Value)
|
||||||
|
}
|
||||||
|
vars.om = newOM
|
||||||
|
vars.mutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
func (vs *Vars) DeepCopy() *Vars {
|
func (vs *Vars) DeepCopy() *Vars {
|
||||||
if vs == nil {
|
if vs == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -72,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"
|
||||||
@@ -19,8 +19,11 @@ type FileNode struct {
|
|||||||
|
|
||||||
func NewFileNode(entrypoint, dir string, opts ...NodeOption) (*FileNode, error) {
|
func NewFileNode(entrypoint, dir string, opts ...NodeOption) (*FileNode, error) {
|
||||||
// Find the entrypoint file
|
// Find the entrypoint file
|
||||||
resolvedEntrypoint, err := fsext.Search(entrypoint, dir, defaultTaskfiles)
|
resolvedEntrypoint, err := fsext.Search(entrypoint, dir, DefaultTaskfiles)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
|
return nil, errors.TaskfileNotFoundError{URI: entrypoint, Walk: false}
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,16 +3,14 @@ package taskfile
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
giturls "github.com/chainguard-dev/git-urls"
|
giturls "github.com/chainguard-dev/git-urls"
|
||||||
"github.com/go-git/go-billy/v5/memfs"
|
"github.com/hashicorp/go-getter"
|
||||||
"github.com/go-git/go-git/v5"
|
|
||||||
"github.com/go-git/go-git/v5/plumbing"
|
|
||||||
"github.com/go-git/go-git/v5/storage/memory"
|
|
||||||
|
|
||||||
"github.com/go-task/task/v3/errors"
|
"github.com/go-task/task/v3/errors"
|
||||||
"github.com/go-task/task/v3/internal/execext"
|
"github.com/go-task/task/v3/internal/execext"
|
||||||
@@ -28,6 +26,36 @@ type GitNode struct {
|
|||||||
path string
|
path string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type gitRepoCache struct {
|
||||||
|
mu sync.Mutex // Protects the locks map
|
||||||
|
locks map[string]*sync.Mutex // One mutex per repo cache key
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *gitRepoCache) getLockForRepo(cacheKey string) *sync.Mutex {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
|
if _, exists := c.locks[cacheKey]; !exists {
|
||||||
|
c.locks[cacheKey] = &sync.Mutex{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.locks[cacheKey]
|
||||||
|
}
|
||||||
|
|
||||||
|
var globalGitRepoCache = &gitRepoCache{
|
||||||
|
locks: make(map[string]*sync.Mutex),
|
||||||
|
}
|
||||||
|
|
||||||
|
func CleanGitCache() error {
|
||||||
|
// Clear the in-memory locks map to prevent memory leak
|
||||||
|
globalGitRepoCache.mu.Lock()
|
||||||
|
globalGitRepoCache.locks = make(map[string]*sync.Mutex)
|
||||||
|
globalGitRepoCache.mu.Unlock()
|
||||||
|
|
||||||
|
cacheDir := filepath.Join(os.TempDir(), "task-git-repos")
|
||||||
|
return os.RemoveAll(cacheDir)
|
||||||
|
}
|
||||||
|
|
||||||
func NewGitNode(
|
func NewGitNode(
|
||||||
entrypoint string,
|
entrypoint string,
|
||||||
dir string,
|
dir string,
|
||||||
@@ -72,24 +100,78 @@ func (node *GitNode) Read() ([]byte, error) {
|
|||||||
return node.ReadContext(context.Background())
|
return node.ReadContext(context.Background())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (node *GitNode) ReadContext(_ context.Context) ([]byte, error) {
|
func (node *GitNode) buildURL() string {
|
||||||
fs := memfs.New()
|
// Get the base URL
|
||||||
storer := memory.NewStorage()
|
baseURL := node.url.String()
|
||||||
_, err := git.Clone(storer, fs, &git.CloneOptions{
|
|
||||||
URL: node.url.String(),
|
ref := node.ref
|
||||||
ReferenceName: plumbing.ReferenceName(node.ref),
|
if ref == "" {
|
||||||
SingleBranch: true,
|
ref = "HEAD"
|
||||||
Depth: 1,
|
}
|
||||||
})
|
// Always use git:: prefix for git URLs (following Terraform's pattern)
|
||||||
|
// This forces go-getter to use git protocol
|
||||||
|
return fmt.Sprintf("git::%s?ref=%s&depth=1", baseURL, ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getOrCloneRepo returns the path to a cached git repository.
|
||||||
|
// If the repository is not cached, it clones it first.
|
||||||
|
// This function is thread-safe: multiple goroutines cloning the same repo+ref
|
||||||
|
// will synchronize, and only one clone operation will occur.
|
||||||
|
//
|
||||||
|
// The cache directory is /tmp/task-git-repos/{cache_key}/
|
||||||
|
func (node *GitNode) getOrCloneRepo(ctx context.Context) (string, error) {
|
||||||
|
cacheKey := node.repoCacheKey()
|
||||||
|
|
||||||
|
repoMutex := globalGitRepoCache.getLockForRepo(cacheKey)
|
||||||
|
repoMutex.Lock()
|
||||||
|
defer repoMutex.Unlock()
|
||||||
|
|
||||||
|
// Check if context was cancelled while waiting for lock
|
||||||
|
if err := ctx.Err(); err != nil {
|
||||||
|
return "", fmt.Errorf("context cancelled while waiting for repository lock: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cacheDir := filepath.Join(os.TempDir(), "task-git-repos", cacheKey)
|
||||||
|
|
||||||
|
// check if repo is already cached (under the lock)
|
||||||
|
gitDir := filepath.Join(cacheDir, ".git")
|
||||||
|
if _, err := os.Stat(gitDir); err == nil {
|
||||||
|
return cacheDir, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
getterURL := node.buildURL()
|
||||||
|
|
||||||
|
client := &getter.Client{
|
||||||
|
Ctx: ctx,
|
||||||
|
Src: getterURL,
|
||||||
|
Dst: cacheDir,
|
||||||
|
Mode: getter.ClientModeDir,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := client.Get(); err != nil {
|
||||||
|
_ = os.RemoveAll(cacheDir)
|
||||||
|
return "", fmt.Errorf("failed to clone repository: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cacheDir, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (node *GitNode) ReadContext(ctx context.Context) ([]byte, error) {
|
||||||
|
// Get or clone the repository into cache
|
||||||
|
repoDir, err := node.getOrCloneRepo(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
file, err := fs.Open(node.path)
|
|
||||||
if err != nil {
|
// Build path to Taskfile in the cached repo
|
||||||
return nil, err
|
taskfilePath := node.path
|
||||||
|
if taskfilePath == "" {
|
||||||
|
taskfilePath = "Taskfile.yml"
|
||||||
}
|
}
|
||||||
// Read the entire response body
|
filePath := filepath.Join(repoDir, taskfilePath)
|
||||||
b, err := io.ReadAll(file)
|
|
||||||
|
// Read file from cached repo
|
||||||
|
b, err := os.ReadFile(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -98,6 +180,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 != "" {
|
||||||
@@ -133,6 +220,22 @@ func (node *GitNode) CacheKey() string {
|
|||||||
return fmt.Sprintf("git.%s.%s.%s", node.url.Host, prefix, checksum)
|
return fmt.Sprintf("git.%s.%s.%s", node.url.Host, prefix, checksum)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// repoCacheKey generates a unique cache key for the repository+ref combination.
|
||||||
|
// Unlike CacheKey() which includes the file path, this identifies the repository itself.
|
||||||
|
// Two GitNodes with the same repo+ref but different file paths will share the same cache.
|
||||||
|
//
|
||||||
|
// Returns a path like: github.com/user/repo.git/main
|
||||||
|
func (node *GitNode) repoCacheKey() string {
|
||||||
|
repoPath := strings.Trim(node.url.Path, "/")
|
||||||
|
|
||||||
|
ref := node.ref
|
||||||
|
if ref == "" {
|
||||||
|
ref = "HEAD"
|
||||||
|
}
|
||||||
|
|
||||||
|
return filepath.Join(node.url.Host, repoPath, ref)
|
||||||
|
}
|
||||||
|
|
||||||
func splitURLOnDoubleSlash(u *url.URL) (string, string) {
|
func splitURLOnDoubleSlash(u *url.URL) (string, string) {
|
||||||
x := strings.Split(u.Path, "//")
|
x := strings.Split(u.Path, "//")
|
||||||
switch len(x) {
|
switch len(x) {
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|
||||||
@@ -91,3 +102,146 @@ func TestGitNode_CacheKey(t *testing.T) {
|
|||||||
assert.Equal(t, tt.expectedKey, key)
|
assert.Equal(t, tt.expectedKey, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGitNode_buildURL(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
entrypoint string
|
||||||
|
expectedURL string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "HTTPS with ref",
|
||||||
|
entrypoint: "https://github.com/foo/bar.git//Taskfile.yml?ref=main",
|
||||||
|
expectedURL: "git::https://github.com/foo/bar.git?ref=main&depth=1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "SSH with ref",
|
||||||
|
entrypoint: "git@github.com:foo/bar.git//Taskfile.yml?ref=main",
|
||||||
|
expectedURL: "git::ssh://git@github.com/foo/bar.git?ref=main&depth=1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "HTTPS with tag ref",
|
||||||
|
entrypoint: "https://github.com/foo/bar.git//Taskfile.yml?ref=v1.0.0",
|
||||||
|
expectedURL: "git::https://github.com/foo/bar.git?ref=v1.0.0&depth=1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "HTTPS without ref (uses remote HEAD)",
|
||||||
|
entrypoint: "https://github.com/foo/bar.git//Taskfile.yml",
|
||||||
|
expectedURL: "git::https://github.com/foo/bar.git?ref=HEAD&depth=1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "SSH with directory path",
|
||||||
|
entrypoint: "git@github.com:foo/bar.git//directory/Taskfile.yml?ref=dev",
|
||||||
|
expectedURL: "git::ssh://git@github.com/foo/bar.git?ref=dev&depth=1",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
node, err := NewGitNode(tt.entrypoint, "", false)
|
||||||
|
require.NoError(t, err)
|
||||||
|
gotURL := node.buildURL()
|
||||||
|
assert.Equal(t, tt.expectedURL, gotURL)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRepoCacheKey_SameRepoSameRef(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Same repo, same ref, different files should have SAME cache key
|
||||||
|
node1, err := NewGitNode("https://github.com/foo/bar.git//file1.yml?ref=main", "", false)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
node2, err := NewGitNode("https://github.com/foo/bar.git//dir/file2.yml?ref=main", "", false)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
key1 := node1.repoCacheKey()
|
||||||
|
key2 := node2.repoCacheKey()
|
||||||
|
|
||||||
|
assert.Equal(t, key1, key2, "Same repo+ref should generate same cache key regardless of file path")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRepoCacheKey_SameRepoDifferentRef(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Same repo, different ref should have DIFFERENT cache keys
|
||||||
|
node1, err := NewGitNode("https://github.com/foo/bar.git//file.yml?ref=main", "", false)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
node2, err := NewGitNode("https://github.com/foo/bar.git//file.yml?ref=dev", "", false)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
key1 := node1.repoCacheKey()
|
||||||
|
key2 := node2.repoCacheKey()
|
||||||
|
|
||||||
|
assert.NotEqual(t, key1, key2, "Different refs should generate different cache keys")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRepoCacheKey_DifferentRepos(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Different repos should have DIFFERENT cache keys
|
||||||
|
node1, err := NewGitNode("https://github.com/foo/bar.git//file.yml?ref=main", "", false)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
node2, err := NewGitNode("https://github.com/foo/other.git//file.yml?ref=main", "", false)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
key1 := node1.repoCacheKey()
|
||||||
|
key2 := node2.repoCacheKey()
|
||||||
|
|
||||||
|
assert.NotEqual(t, key1, key2, "Different repos should generate different cache keys")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRepoCacheKey_NoRefVsHead(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// No ref (defaults to HEAD) vs explicit HEAD should have SAME cache key
|
||||||
|
node1, err := NewGitNode("https://github.com/foo/bar.git//file.yml", "", false)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
node2, err := NewGitNode("https://github.com/foo/bar.git//file.yml?ref=HEAD", "", false)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
key1 := node1.repoCacheKey()
|
||||||
|
key2 := node2.repoCacheKey()
|
||||||
|
|
||||||
|
assert.Equal(t, key1, key2, "No ref and explicit HEAD should generate same cache key")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRepoCacheKey_SSHvsHTTPS(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// SSH vs HTTPS pointing to same repo should have SAME cache key
|
||||||
|
// They clone the same repo, so we want to share the cache
|
||||||
|
node1, err := NewGitNode("git@github.com:foo/bar.git//file.yml?ref=main", "", false)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
node2, err := NewGitNode("https://github.com/foo/bar.git//file.yml?ref=main", "", false)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
key1 := node1.repoCacheKey()
|
||||||
|
key2 := node2.repoCacheKey()
|
||||||
|
|
||||||
|
assert.Equal(t, key1, key2, "SSH and HTTPS for same repo should share cache")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRepoCacheKey_Consistency(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Calling repoCacheKey multiple times on same node should return same key
|
||||||
|
node, err := NewGitNode("https://github.com/foo/bar.git//file.yml?ref=main", "", false)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
key1 := node.repoCacheKey()
|
||||||
|
key2 := node.repoCacheKey()
|
||||||
|
key3 := node.repoCacheKey()
|
||||||
|
|
||||||
|
assert.Equal(t, key1, key2)
|
||||||
|
assert.Equal(t, key2, key3)
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,13 +3,14 @@ package taskfile
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"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"
|
||||||
@@ -43,6 +44,7 @@ type (
|
|||||||
insecure bool
|
insecure bool
|
||||||
download bool
|
download bool
|
||||||
offline bool
|
offline bool
|
||||||
|
trustedHosts []string
|
||||||
tempDir string
|
tempDir string
|
||||||
cacheExpiryDuration time.Duration
|
cacheExpiryDuration time.Duration
|
||||||
debugFunc DebugFunc
|
debugFunc DebugFunc
|
||||||
@@ -59,6 +61,7 @@ func NewReader(opts ...ReaderOption) *Reader {
|
|||||||
insecure: false,
|
insecure: false,
|
||||||
download: false,
|
download: false,
|
||||||
offline: false,
|
offline: false,
|
||||||
|
trustedHosts: nil,
|
||||||
tempDir: os.TempDir(),
|
tempDir: os.TempDir(),
|
||||||
cacheExpiryDuration: 0,
|
cacheExpiryDuration: 0,
|
||||||
debugFunc: nil,
|
debugFunc: nil,
|
||||||
@@ -119,6 +122,20 @@ func (o *offlineOption) ApplyToReader(r *Reader) {
|
|||||||
r.offline = o.offline
|
r.offline = o.offline
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithTrustedHosts configures the [Reader] with a list of trusted hosts for remote
|
||||||
|
// Taskfiles. Hosts in this list will not prompt for user confirmation.
|
||||||
|
func WithTrustedHosts(trustedHosts []string) ReaderOption {
|
||||||
|
return &trustedHostsOption{trustedHosts: trustedHosts}
|
||||||
|
}
|
||||||
|
|
||||||
|
type trustedHostsOption struct {
|
||||||
|
trustedHosts []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *trustedHostsOption) ApplyToReader(r *Reader) {
|
||||||
|
r.trustedHosts = o.trustedHosts
|
||||||
|
}
|
||||||
|
|
||||||
// WithTempDir sets the temporary directory that will be used by the [Reader].
|
// WithTempDir sets the temporary directory that will be used by the [Reader].
|
||||||
// By default, the reader uses [os.TempDir].
|
// By default, the reader uses [os.TempDir].
|
||||||
func WithTempDir(tempDir string) ReaderOption {
|
func WithTempDir(tempDir string) ReaderOption {
|
||||||
@@ -187,9 +204,15 @@ func (o *promptFuncOption) ApplyToReader(r *Reader) {
|
|||||||
// building an [ast.TaskfileGraph] as it goes. If any errors occur, they will be
|
// building an [ast.TaskfileGraph] as it goes. If any errors occur, they will be
|
||||||
// returned immediately.
|
// returned immediately.
|
||||||
func (r *Reader) Read(ctx context.Context, node Node) (*ast.TaskfileGraph, error) {
|
func (r *Reader) Read(ctx context.Context, node Node) (*ast.TaskfileGraph, error) {
|
||||||
|
// Clean up git cache after reading all taskfiles
|
||||||
|
defer func() {
|
||||||
|
_ = CleanGitCache()
|
||||||
|
}()
|
||||||
|
|
||||||
if err := r.include(ctx, node); err != nil {
|
if err := r.include(ctx, node); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return r.graph, nil
|
return r.graph, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,6 +229,28 @@ func (r *Reader) promptf(format string, a ...any) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isTrusted checks if a URI's host matches any of the trusted hosts patterns.
|
||||||
|
func (r *Reader) isTrusted(uri string) bool {
|
||||||
|
if len(r.trustedHosts) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the URI to extract the host
|
||||||
|
parsedURL, err := url.Parse(uri)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
host := parsedURL.Host
|
||||||
|
|
||||||
|
// Check against each trusted pattern (exact match including port if provided)
|
||||||
|
for _, pattern := range r.trustedHosts {
|
||||||
|
if host == pattern {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Reader) include(ctx context.Context, node Node) error {
|
func (r *Reader) include(ctx context.Context, node Node) error {
|
||||||
// Create a new vertex for the Taskfile
|
// Create a new vertex for the Taskfile
|
||||||
vertex := &ast.TaskfileVertex{
|
vertex := &ast.TaskfileVertex{
|
||||||
@@ -459,9 +504,9 @@ func (r *Reader) readRemoteNodeContent(ctx context.Context, node RemoteNode) ([]
|
|||||||
|
|
||||||
// If there is no manual checksum pin, run the automatic checks
|
// If there is no manual checksum pin, run the automatic checks
|
||||||
if node.Checksum() == "" {
|
if node.Checksum() == "" {
|
||||||
// Prompt the user if required
|
// Prompt the user if required (unless host is trusted)
|
||||||
prompt := cache.ChecksumPrompt(checksum)
|
prompt := cache.ChecksumPrompt(checksum)
|
||||||
if prompt != "" {
|
if prompt != "" && !r.isTrusted(node.Location()) {
|
||||||
if err := func() error {
|
if err := func() error {
|
||||||
r.promptMutex.Lock()
|
r.promptMutex.Lock()
|
||||||
defer r.promptMutex.Unlock()
|
defer r.promptMutex.Unlock()
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
defaultTaskfiles = []string{
|
// DefaultTaskfiles is the list of Taskfile file names supported by default.
|
||||||
|
DefaultTaskfiles = []string{
|
||||||
"Taskfile.yml",
|
"Taskfile.yml",
|
||||||
"taskfile.yml",
|
"taskfile.yml",
|
||||||
"Taskfile.yaml",
|
"Taskfile.yaml",
|
||||||
@@ -28,6 +29,7 @@ var (
|
|||||||
"text/x-yaml",
|
"text/x-yaml",
|
||||||
"application/yaml",
|
"application/yaml",
|
||||||
"application/x-yaml",
|
"application/x-yaml",
|
||||||
|
"application/octet-stream",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -66,7 +68,7 @@ func RemoteExists(ctx context.Context, u url.URL) (*url.URL, error) {
|
|||||||
|
|
||||||
// If the request was not successful, append the default Taskfile names to
|
// If the request was not successful, append the default Taskfile names to
|
||||||
// the URL and return the URL of the first successful request
|
// the URL and return the URL of the first successful request
|
||||||
for _, taskfile := range defaultTaskfiles {
|
for _, taskfile := range DefaultTaskfiles {
|
||||||
// Fixes a bug with JoinPath where a leading slash is not added to the
|
// Fixes a bug with JoinPath where a leading slash is not added to the
|
||||||
// path if it is empty
|
// path if it is empty
|
||||||
if u.Path == "" {
|
if u.Path == "" {
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
# https://taskfile.dev
|
# yaml-language-server: $schema=https://taskfile.dev/schema.json
|
||||||
|
|
||||||
version: '3'
|
version: '3'
|
||||||
|
|
||||||
vars:
|
vars:
|
||||||
GREETING: Hello, World!
|
GREETING: Hello, world!
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
default:
|
default:
|
||||||
|
desc: Print a greeting message
|
||||||
cmds:
|
cmds:
|
||||||
- echo "{{.GREETING}}"
|
- echo "{{.GREETING}}"
|
||||||
silent: true
|
silent: true
|
||||||
|
|||||||
@@ -3,24 +3,30 @@ package ast
|
|||||||
import (
|
import (
|
||||||
"cmp"
|
"cmp"
|
||||||
"maps"
|
"maps"
|
||||||
|
"slices"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Masterminds/semver/v3"
|
"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"`
|
Verbose *bool `yaml:"verbose"`
|
||||||
Concurrency *int `yaml:"concurrency"`
|
Color *bool `yaml:"color"`
|
||||||
Remote Remote `yaml:"remote"`
|
DisableFuzzy *bool `yaml:"disable-fuzzy"`
|
||||||
Experiments map[string]int `yaml:"experiments"`
|
Concurrency *int `yaml:"concurrency"`
|
||||||
|
Remote Remote `yaml:"remote"`
|
||||||
|
Failfast bool `yaml:"failfast"`
|
||||||
|
Experiments map[string]int `yaml:"experiments"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Remote struct {
|
type Remote struct {
|
||||||
Insecure *bool `yaml:"insecure"`
|
Insecure *bool `yaml:"insecure"`
|
||||||
Offline *bool `yaml:"offline"`
|
Offline *bool `yaml:"offline"`
|
||||||
Timeout *time.Duration `yaml:"timeout"`
|
Timeout *time.Duration `yaml:"timeout"`
|
||||||
CacheExpiry *time.Duration `yaml:"cache-expiry"`
|
CacheExpiry *time.Duration `yaml:"cache-expiry"`
|
||||||
|
CacheDir *string `yaml:"cache-dir"`
|
||||||
|
TrustedHosts []string `yaml:"trusted-hosts"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge combines the current TaskRC with another TaskRC, prioritizing non-nil fields from the other TaskRC.
|
// Merge combines the current TaskRC with another TaskRC, prioritizing non-nil fields from the other TaskRC.
|
||||||
@@ -42,7 +48,16 @@ func (t *TaskRC) Merge(other *TaskRC) {
|
|||||||
t.Remote.Offline = cmp.Or(other.Remote.Offline, t.Remote.Offline)
|
t.Remote.Offline = cmp.Or(other.Remote.Offline, t.Remote.Offline)
|
||||||
t.Remote.Timeout = cmp.Or(other.Remote.Timeout, t.Remote.Timeout)
|
t.Remote.Timeout = cmp.Or(other.Remote.Timeout, t.Remote.Timeout)
|
||||||
t.Remote.CacheExpiry = cmp.Or(other.Remote.CacheExpiry, t.Remote.CacheExpiry)
|
t.Remote.CacheExpiry = cmp.Or(other.Remote.CacheExpiry, t.Remote.CacheExpiry)
|
||||||
|
t.Remote.CacheDir = cmp.Or(other.Remote.CacheDir, t.Remote.CacheDir)
|
||||||
|
if len(other.Remote.TrustedHosts) > 0 {
|
||||||
|
merged := slices.Concat(other.Remote.TrustedHosts, t.Remote.TrustedHosts)
|
||||||
|
slices.Sort(merged)
|
||||||
|
t.Remote.TrustedHosts = slices.Compact(merged)
|
||||||
|
}
|
||||||
|
|
||||||
t.Verbose = cmp.Or(other.Verbose, t.Verbose)
|
t.Verbose = cmp.Or(other.Verbose, t.Verbose)
|
||||||
|
t.Color = cmp.Or(other.Color, t.Color)
|
||||||
|
t.DisableFuzzy = cmp.Or(other.DisableFuzzy, t.DisableFuzzy)
|
||||||
t.Concurrency = cmp.Or(other.Concurrency, t.Concurrency)
|
t.Concurrency = cmp.Or(other.Concurrency, t.Concurrency)
|
||||||
|
t.Failfast = cmp.Or(other.Failfast, t.Failfast)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -59,11 +59,11 @@ func GetConfig(dir string) (*ast.TaskRC, error) {
|
|||||||
// Find all the nodes from the given directory up to the users home directory
|
// Find all the nodes from the given directory up to the users home directory
|
||||||
absDir, err := filepath.Abs(dir)
|
absDir, err := filepath.Abs(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return config, err
|
||||||
}
|
}
|
||||||
entrypoints, err := fsext.SearchAll("", absDir, defaultTaskRCs)
|
entrypoints, err := fsext.SearchAll("", absDir, defaultTaskRCs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return config, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reverse the entrypoints since we want the child files to override parent ones
|
// Reverse the entrypoints since we want the child files to override parent ones
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@@ -135,3 +136,174 @@ func TestGetConfig_All(t *testing.T) { //nolint:paralleltest // cannot run in pa
|
|||||||
},
|
},
|
||||||
}, cfg)
|
}, cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetConfig_RemoteTrustedHosts(t *testing.T) { //nolint:paralleltest // cannot run in parallel
|
||||||
|
_, _, localDir := setupDirs(t)
|
||||||
|
|
||||||
|
// Test with single host
|
||||||
|
configYAML := `
|
||||||
|
remote:
|
||||||
|
trusted-hosts:
|
||||||
|
- github.com
|
||||||
|
`
|
||||||
|
writeFile(t, localDir, ".taskrc.yml", configYAML)
|
||||||
|
|
||||||
|
cfg, err := GetConfig(localDir)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, cfg)
|
||||||
|
assert.Equal(t, []string{"github.com"}, cfg.Remote.TrustedHosts)
|
||||||
|
|
||||||
|
// Test with multiple hosts
|
||||||
|
configYAML = `
|
||||||
|
remote:
|
||||||
|
trusted-hosts:
|
||||||
|
- github.com
|
||||||
|
- gitlab.com
|
||||||
|
- example.com:8080
|
||||||
|
`
|
||||||
|
writeFile(t, localDir, ".taskrc.yml", configYAML)
|
||||||
|
|
||||||
|
cfg, err = GetConfig(localDir)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, cfg)
|
||||||
|
assert.Equal(t, []string{"github.com", "gitlab.com", "example.com:8080"}, cfg.Remote.TrustedHosts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetConfig_RemoteTrustedHostsMerge(t *testing.T) { //nolint:paralleltest // cannot run in parallel
|
||||||
|
t.Run("file-based merge precedence", func(t *testing.T) { //nolint:paralleltest // parent test cannot run in parallel
|
||||||
|
xdgConfigDir, homeDir, localDir := setupDirs(t)
|
||||||
|
|
||||||
|
// XDG config has github.com and gitlab.com
|
||||||
|
xdgConfig := `
|
||||||
|
remote:
|
||||||
|
trusted-hosts:
|
||||||
|
- github.com
|
||||||
|
- gitlab.com
|
||||||
|
timeout: "30s"
|
||||||
|
`
|
||||||
|
writeFile(t, xdgConfigDir, "taskrc.yml", xdgConfig)
|
||||||
|
|
||||||
|
// Home config has example.com (should be combined with XDG)
|
||||||
|
homeConfig := `
|
||||||
|
remote:
|
||||||
|
trusted-hosts:
|
||||||
|
- example.com
|
||||||
|
`
|
||||||
|
writeFile(t, homeDir, ".taskrc.yml", homeConfig)
|
||||||
|
|
||||||
|
cfg, err := GetConfig(localDir)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, cfg)
|
||||||
|
// Home config entries come first, then XDG
|
||||||
|
assert.Equal(t, []string{"example.com", "github.com", "gitlab.com"}, cfg.Remote.TrustedHosts)
|
||||||
|
|
||||||
|
// Test with local config too
|
||||||
|
localConfig := `
|
||||||
|
remote:
|
||||||
|
trusted-hosts:
|
||||||
|
- local.dev
|
||||||
|
`
|
||||||
|
writeFile(t, localDir, ".taskrc.yml", localConfig)
|
||||||
|
|
||||||
|
cfg, err = GetConfig(localDir)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, cfg)
|
||||||
|
// Local config entries come first
|
||||||
|
assert.Equal(t, []string{"example.com", "github.com", "gitlab.com", "local.dev"}, cfg.Remote.TrustedHosts)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("merge edge cases", func(t *testing.T) { //nolint:paralleltest // parent test cannot run in parallel
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
base *ast.TaskRC
|
||||||
|
other *ast.TaskRC
|
||||||
|
expected []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "merge hosts into empty",
|
||||||
|
base: &ast.TaskRC{},
|
||||||
|
other: &ast.TaskRC{
|
||||||
|
Remote: ast.Remote{
|
||||||
|
TrustedHosts: []string{"github.com"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: []string{"github.com"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "merge combines lists",
|
||||||
|
base: &ast.TaskRC{
|
||||||
|
Remote: ast.Remote{
|
||||||
|
TrustedHosts: []string{"base.com"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
other: &ast.TaskRC{
|
||||||
|
Remote: ast.Remote{
|
||||||
|
TrustedHosts: []string{"other.com"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: []string{"base.com", "other.com"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "merge empty list does not override",
|
||||||
|
base: &ast.TaskRC{
|
||||||
|
Remote: ast.Remote{
|
||||||
|
TrustedHosts: []string{"base.com"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
other: &ast.TaskRC{
|
||||||
|
Remote: ast.Remote{
|
||||||
|
TrustedHosts: []string{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: []string{"base.com"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "merge nil does not override",
|
||||||
|
base: &ast.TaskRC{
|
||||||
|
Remote: ast.Remote{
|
||||||
|
TrustedHosts: []string{"base.com"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
other: &ast.TaskRC{
|
||||||
|
Remote: ast.Remote{
|
||||||
|
TrustedHosts: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: []string{"base.com"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) { //nolint:paralleltest // parent test cannot run in parallel
|
||||||
|
tt.base.Merge(tt.other)
|
||||||
|
assert.Equal(t, tt.expected, tt.base.Remote.TrustedHosts)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("all remote fields merge", func(t *testing.T) { //nolint:paralleltest // parent test cannot run in parallel
|
||||||
|
insecureTrue := true
|
||||||
|
offlineTrue := true
|
||||||
|
timeout := 30 * time.Second
|
||||||
|
cacheExpiry := 1 * time.Hour
|
||||||
|
|
||||||
|
base := &ast.TaskRC{}
|
||||||
|
other := &ast.TaskRC{
|
||||||
|
Remote: ast.Remote{
|
||||||
|
Insecure: &insecureTrue,
|
||||||
|
Offline: &offlineTrue,
|
||||||
|
Timeout: &timeout,
|
||||||
|
CacheExpiry: &cacheExpiry,
|
||||||
|
TrustedHosts: []string{"github.com", "gitlab.com"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
base.Merge(other)
|
||||||
|
|
||||||
|
assert.Equal(t, &insecureTrue, base.Remote.Insecure)
|
||||||
|
assert.Equal(t, &offlineTrue, base.Remote.Offline)
|
||||||
|
assert.Equal(t, &timeout, base.Remote.Timeout)
|
||||||
|
assert.Equal(t, &cacheExpiry, base.Remote.CacheExpiry)
|
||||||
|
assert.Equal(t, []string{"github.com", "gitlab.com"}, base.Remote.TrustedHosts)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
14
testdata/failfast/default/Taskfile.yaml
vendored
Normal file
14
testdata/failfast/default/Taskfile.yaml
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
default:
|
||||||
|
deps:
|
||||||
|
- dep1
|
||||||
|
- dep2
|
||||||
|
- dep3
|
||||||
|
- dep4
|
||||||
|
|
||||||
|
dep1: sleep 0.1 && echo 'dep1'
|
||||||
|
dep2: sleep 0.2 && echo 'dep2'
|
||||||
|
dep3: sleep 0.3 && echo 'dep3'
|
||||||
|
dep4: exit 1
|
||||||
1
testdata/failfast/default/testdata/TestFailfast-Default-default-err-run.golden
vendored
Normal file
1
testdata/failfast/default/testdata/TestFailfast-Default-default-err-run.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
task: Failed to run task "default": task: Failed to run task "dep4": exit status 1
|
||||||
3
testdata/failfast/default/testdata/TestFailfast-Default-default.golden
vendored
Normal file
3
testdata/failfast/default/testdata/TestFailfast-Default-default.golden
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
dep1
|
||||||
|
dep2
|
||||||
|
dep3
|
||||||
1
testdata/failfast/default/testdata/TestFailfast-Option-default-err-run.golden
vendored
Normal file
1
testdata/failfast/default/testdata/TestFailfast-Option-default-err-run.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
task: Failed to run task "default": task: Failed to run task "dep4": exit status 1
|
||||||
1
testdata/failfast/default/testdata/TestFailfast-Option-default.golden
vendored
Normal file
1
testdata/failfast/default/testdata/TestFailfast-Option-default.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
15
testdata/failfast/task/Taskfile.yaml
vendored
Normal file
15
testdata/failfast/task/Taskfile.yaml
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
default:
|
||||||
|
deps:
|
||||||
|
- dep1
|
||||||
|
- dep2
|
||||||
|
- dep3
|
||||||
|
- dep4
|
||||||
|
failfast: true
|
||||||
|
|
||||||
|
dep1: sleep 0.1 && echo 'dep1'
|
||||||
|
dep2: sleep 0.2 && echo 'dep2'
|
||||||
|
dep3: sleep 0.3 && echo 'dep3'
|
||||||
|
dep4: exit 1
|
||||||
1
testdata/failfast/task/testdata/TestFailfast-Task-task-err-run.golden
vendored
Normal file
1
testdata/failfast/task/testdata/TestFailfast-Task-task-err-run.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
task: Failed to run task "default": task: Failed to run task "dep4": exit status 1
|
||||||
1
testdata/failfast/task/testdata/TestFailfast-Task-task.golden
vendored
Normal file
1
testdata/failfast/task/testdata/TestFailfast-Task-task.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
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_when_changed/Taskfile.yml
vendored
Normal file
11
testdata/run_when_changed/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
includes:
|
||||||
|
service-a: ./service-a
|
||||||
|
service-b: ./service-b
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
start:
|
||||||
|
cmds:
|
||||||
|
- task: service-a:start
|
||||||
|
- task: service-b:start
|
||||||
7
testdata/run_when_changed/library/Taskfile.yml
vendored
Normal file
7
testdata/run_when_changed/library/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
login:
|
||||||
|
run: when_changed
|
||||||
|
cmds:
|
||||||
|
- echo "login server={{.SERVER}} user={{.USER}}"
|
||||||
18
testdata/run_when_changed/service-a/Taskfile.yml
vendored
Normal file
18
testdata/run_when_changed/service-a/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
includes:
|
||||||
|
library:
|
||||||
|
taskfile: ../library/Taskfile.yml
|
||||||
|
dir: ../library
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
start:
|
||||||
|
cmds:
|
||||||
|
- task: library:login
|
||||||
|
vars:
|
||||||
|
SERVER: fubar
|
||||||
|
USER: fubar
|
||||||
|
- task: library:login
|
||||||
|
vars:
|
||||||
|
SERVER: foo
|
||||||
|
USER: foo
|
||||||
18
testdata/run_when_changed/service-b/Taskfile.yml
vendored
Normal file
18
testdata/run_when_changed/service-b/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
includes:
|
||||||
|
library:
|
||||||
|
taskfile: ../library/Taskfile.yml
|
||||||
|
dir: ../library
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
start:
|
||||||
|
cmds:
|
||||||
|
- task: library:login
|
||||||
|
vars:
|
||||||
|
SERVER: fubar
|
||||||
|
USER: fubar
|
||||||
|
- task: library:login
|
||||||
|
vars:
|
||||||
|
SERVER: bar
|
||||||
|
USER: bar
|
||||||
21
testdata/summary-vars-requires/Taskfile-with-env.yml
vendored
Normal file
21
testdata/summary-vars-requires/Taskfile-with-env.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
version: 3
|
||||||
|
|
||||||
|
vars:
|
||||||
|
GLOBAL_VAR: "I am a global var"
|
||||||
|
|
||||||
|
env:
|
||||||
|
GLOBAL_ENV: "I am a global env"
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
test-env:
|
||||||
|
desc: Task with vars and env
|
||||||
|
vars:
|
||||||
|
LOCAL_VAR: "I am a local var"
|
||||||
|
env:
|
||||||
|
LOCAL_ENV: "I am a local env"
|
||||||
|
DATABASE_URL: "postgres://localhost/mydb"
|
||||||
|
requires:
|
||||||
|
vars:
|
||||||
|
- API_KEY
|
||||||
|
cmds:
|
||||||
|
- echo "Testing env vars"
|
||||||
16
testdata/summary-vars-requires/Taskfile-with-globals.yml
vendored
Normal file
16
testdata/summary-vars-requires/Taskfile-with-globals.yml
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
version: 3
|
||||||
|
|
||||||
|
vars:
|
||||||
|
GLOBAL_VAR: "I am global"
|
||||||
|
ANOTHER_GLOBAL: "Also global"
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
test-globals:
|
||||||
|
desc: Task with global and local vars
|
||||||
|
vars:
|
||||||
|
LOCAL_VAR: "I am local"
|
||||||
|
requires:
|
||||||
|
vars:
|
||||||
|
- REQUIRED_VAR
|
||||||
|
cmds:
|
||||||
|
- echo {{ .GLOBAL_VAR }} {{ .LOCAL_VAR }}
|
||||||
36
testdata/summary-vars-requires/Taskfile.yml
vendored
Normal file
36
testdata/summary-vars-requires/Taskfile.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
version: 3
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
mytask:
|
||||||
|
desc: It does things
|
||||||
|
summary: |
|
||||||
|
It does things and has optional and required variables.
|
||||||
|
vars:
|
||||||
|
OPTIONAL_VAR: "hello"
|
||||||
|
requires:
|
||||||
|
vars:
|
||||||
|
- REQUIRED_VAR
|
||||||
|
cmds:
|
||||||
|
- cmd: echo {{ .OPTIONAL_VAR }} {{ .REQUIRED_VAR }}
|
||||||
|
|
||||||
|
with-sh-var:
|
||||||
|
desc: Task with shell variable
|
||||||
|
vars:
|
||||||
|
DYNAMIC_VAR:
|
||||||
|
sh: echo "world"
|
||||||
|
STATIC_VAR: "hello"
|
||||||
|
cmds:
|
||||||
|
- echo {{ .DYNAMIC_VAR }}
|
||||||
|
|
||||||
|
no-vars:
|
||||||
|
desc: Task without variables
|
||||||
|
cmds:
|
||||||
|
- echo "no vars here"
|
||||||
|
|
||||||
|
only-requires:
|
||||||
|
desc: Task with only requires
|
||||||
|
requires:
|
||||||
|
vars:
|
||||||
|
- NEEDED_VAR
|
||||||
|
cmds:
|
||||||
|
- echo {{ .NEEDED_VAR }}
|
||||||
10
testdata/summary-vars-requires/testdata/TestSummaryWithVarsAndRequires-shell-vars.golden
vendored
Normal file
10
testdata/summary-vars-requires/testdata/TestSummaryWithVarsAndRequires-shell-vars.golden
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
task: with-sh-var
|
||||||
|
|
||||||
|
Task with shell variable
|
||||||
|
|
||||||
|
vars:
|
||||||
|
DYNAMIC_VAR: sh: echo "world"
|
||||||
|
STATIC_VAR: "hello"
|
||||||
|
|
||||||
|
commands:
|
||||||
|
- echo
|
||||||
13
testdata/summary-vars-requires/testdata/TestSummaryWithVarsAndRequires-vars-and-requires.golden
vendored
Normal file
13
testdata/summary-vars-requires/testdata/TestSummaryWithVarsAndRequires-vars-and-requires.golden
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
task: mytask
|
||||||
|
|
||||||
|
It does things and has optional and required variables.
|
||||||
|
|
||||||
|
vars:
|
||||||
|
OPTIONAL_VAR: "hello"
|
||||||
|
|
||||||
|
requires:
|
||||||
|
vars:
|
||||||
|
- REQUIRED_VAR
|
||||||
|
|
||||||
|
commands:
|
||||||
|
- echo hello
|
||||||
7
testdata/vars/Taskfile.yml
vendored
7
testdata/vars/Taskfile.yml
vendored
@@ -49,3 +49,10 @@ tasks:
|
|||||||
- echo "{{.MESSAGE}}"
|
- echo "{{.MESSAGE}}"
|
||||||
|
|
||||||
from-dot-env: echo '{{.DOT_ENV_VAR}}'
|
from-dot-env: echo '{{.DOT_ENV_VAR}}'
|
||||||
|
|
||||||
|
# Test that CLI variables take priority over Taskfile defaults
|
||||||
|
cli-var-priority:
|
||||||
|
vars:
|
||||||
|
CLI_VAR: '{{.CLI_VAR | default "default_value"}}'
|
||||||
|
cmds:
|
||||||
|
- echo '{{.CLI_VAR}}'
|
||||||
|
|||||||
1
testdata/vars/testdata/TestVars-cli-var-priority-default.golden
vendored
Normal file
1
testdata/vars/testdata/TestVars-cli-var-priority-default.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
default_value
|
||||||
1
testdata/vars/testdata/TestVars-cli-var-priority-override.golden
vendored
Normal file
1
testdata/vars/testdata/TestVars-cli-var-priority-override.golden
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from_cli
|
||||||
47
variables.go
47
variables.go
@@ -29,6 +29,52 @@ func (e *Executor) FastCompiledTask(call *Call) (*ast.Task, error) {
|
|||||||
return e.compiledTask(call, false)
|
return e.compiledTask(call, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *Executor) CompiledTaskForTaskList(call *Call) (*ast.Task, error) {
|
||||||
|
origTask, err := e.GetTask(call)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
vars, err := e.Compiler.FastGetVariables(origTask, call)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cache := &templater.Cache{Vars: vars}
|
||||||
|
|
||||||
|
return &ast.Task{
|
||||||
|
Task: origTask.Task,
|
||||||
|
Label: templater.Replace(origTask.Label, cache),
|
||||||
|
Desc: templater.Replace(origTask.Desc, cache),
|
||||||
|
Prompt: templater.Replace(origTask.Prompt, cache),
|
||||||
|
Summary: templater.Replace(origTask.Summary, cache),
|
||||||
|
Aliases: origTask.Aliases,
|
||||||
|
Sources: origTask.Sources,
|
||||||
|
Generates: origTask.Generates,
|
||||||
|
Dir: origTask.Dir,
|
||||||
|
Set: origTask.Set,
|
||||||
|
Shopt: origTask.Shopt,
|
||||||
|
Vars: vars,
|
||||||
|
Env: nil,
|
||||||
|
Dotenv: origTask.Dotenv,
|
||||||
|
Silent: origTask.Silent,
|
||||||
|
Interactive: origTask.Interactive,
|
||||||
|
Internal: origTask.Internal,
|
||||||
|
Method: origTask.Method,
|
||||||
|
Prefix: origTask.Prefix,
|
||||||
|
IgnoreError: origTask.IgnoreError,
|
||||||
|
Run: origTask.Run,
|
||||||
|
IncludeVars: origTask.IncludeVars,
|
||||||
|
IncludedTaskfileVars: origTask.IncludedTaskfileVars,
|
||||||
|
Platforms: origTask.Platforms,
|
||||||
|
Location: origTask.Location,
|
||||||
|
Requires: origTask.Requires,
|
||||||
|
Watch: origTask.Watch,
|
||||||
|
Namespace: origTask.Namespace,
|
||||||
|
Failfast: origTask.Failfast,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (e *Executor) compiledTask(call *Call, evaluateShVars bool) (*ast.Task, error) {
|
func (e *Executor) compiledTask(call *Call, evaluateShVars bool) (*ast.Task, error) {
|
||||||
origTask, err := e.GetTask(call)
|
origTask, err := e.GetTask(call)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -80,6 +126,7 @@ func (e *Executor) compiledTask(call *Call, evaluateShVars bool) (*ast.Task, err
|
|||||||
Location: origTask.Location,
|
Location: origTask.Location,
|
||||||
Requires: origTask.Requires,
|
Requires: origTask.Requires,
|
||||||
Watch: origTask.Watch,
|
Watch: origTask.Watch,
|
||||||
|
Failfast: origTask.Failfast,
|
||||||
Namespace: origTask.Namespace,
|
Namespace: origTask.Namespace,
|
||||||
FullName: fullName,
|
FullName: fullName,
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user