mirror of
https://github.com/go-task/task.git
synced 2026-07-01 16:44:34 +00:00
Compare commits
2 Commits
nightly
...
feat/gitla
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b8abadb4f0 | ||
|
|
542fe465e9 |
17
.github/renovate.json
vendored
17
.github/renovate.json
vendored
@@ -6,31 +6,20 @@
|
|||||||
"schedule:weekly",
|
"schedule:weekly",
|
||||||
":semanticCommitTypeAll(chore)"
|
":semanticCommitTypeAll(chore)"
|
||||||
],
|
],
|
||||||
|
"mode": "full",
|
||||||
"addLabels":["area: dependencies"],
|
"addLabels":["area: dependencies"],
|
||||||
"osvVulnerabilityAlerts": true,
|
|
||||||
"postUpdateOptions": ["gomodTidy"],
|
|
||||||
"customManagers": [
|
"customManagers": [
|
||||||
{
|
{
|
||||||
"customType": "regex",
|
"customType": "regex",
|
||||||
"managerFilePatterns": ["/^\\.github/workflows/.*\\.ya?ml$/"],
|
"fileMatch": ["^\\.github/workflows/.*\\.ya?ml$"],
|
||||||
"matchStrings": [
|
"matchStrings": [
|
||||||
"uses:\\s*golangci/golangci-lint-action@\\S+(?:\\s*#[^\\n]*)?\\s+with:\\s+version:\\s*(?<currentValue>v[\\d.]+)"
|
"uses:\\s*golangci/golangci-lint-action@\\S+\\s+with:\\s+version:\\s*(?<currentValue>v[\\d.]+)"
|
||||||
],
|
],
|
||||||
"datasourceTemplate": "github-releases",
|
"datasourceTemplate": "github-releases",
|
||||||
"depNameTemplate": "golangci/golangci-lint"
|
"depNameTemplate": "golangci/golangci-lint"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"packageRules": [
|
"packageRules": [
|
||||||
{
|
|
||||||
"matchManagers": ["gomod"],
|
|
||||||
"matchDepTypes": ["indirect"],
|
|
||||||
"enabled": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"matchUpdateTypes": ["digest", "pinDigest"],
|
|
||||||
"groupName": "all non-major dependencies",
|
|
||||||
"groupSlug": "all-minor-patch"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"matchManagers": ["github-actions"],
|
"matchManagers": ["github-actions"],
|
||||||
"addLabels": ["area: github actions"]
|
"addLabels": ["area: github actions"]
|
||||||
|
|||||||
14
.github/workflows/lint.yml
vendored
14
.github/workflows/lint.yml
vendored
@@ -16,28 +16,28 @@ jobs:
|
|||||||
name: Lint
|
name: Lint
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go-version: [1.25.10, 1.26.x]
|
go-version: [1.25.x, 1.26.x]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
|
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
|
||||||
with:
|
with:
|
||||||
go-version: ${{matrix.go-version}}
|
go-version: ${{matrix.go-version}}
|
||||||
|
|
||||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@ba0d7d2ec06a0ea1cb5fa41b2e4a3ab91d21278a # v9.3.0
|
uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0
|
||||||
with:
|
with:
|
||||||
version: v2.12.2
|
version: v2.11.4
|
||||||
|
|
||||||
lint-jsonschema:
|
lint-jsonschema:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
|
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||||
with:
|
with:
|
||||||
python-version: 3.14
|
python-version: 3.14
|
||||||
|
|
||||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
|
||||||
- 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'
|
||||||
|
|||||||
69
.github/workflows/pr-build.yml
vendored
Normal file
69
.github/workflows/pr-build.yml
vendored
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
name: PR Build
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
types: [labeled, synchronize]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
if: contains(github.event.pull_request.labels.*.name, 'needs-build')
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
fetch-depth: 0
|
||||||
|
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
|
||||||
|
with:
|
||||||
|
go-version: "1.26.x"
|
||||||
|
cache: true
|
||||||
|
- uses: goreleaser/goreleaser-action@ec59f474b9834571250b370d4735c50f8e2d1e29 # v7
|
||||||
|
with:
|
||||||
|
version: "~> v2"
|
||||||
|
args: release --snapshot --clean --config .goreleaser-pr.yml
|
||||||
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
with:
|
||||||
|
name: task_linux_amd64
|
||||||
|
path: dist/task_linux_amd64.tar.gz
|
||||||
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
with:
|
||||||
|
name: task_linux_arm64
|
||||||
|
path: dist/task_linux_arm64.tar.gz
|
||||||
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
with:
|
||||||
|
name: task_darwin_amd64
|
||||||
|
path: dist/task_darwin_amd64.tar.gz
|
||||||
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
with:
|
||||||
|
name: task_darwin_arm64
|
||||||
|
path: dist/task_darwin_arm64.tar.gz
|
||||||
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
with:
|
||||||
|
name: task_windows_amd64
|
||||||
|
path: dist/task_windows_amd64.zip
|
||||||
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||||
|
with:
|
||||||
|
name: checksums
|
||||||
|
path: dist/task_checksums.txt
|
||||||
|
- uses: peter-evans/find-comment@b30e6a3c0ed37e7c023ccd3f1db5c6c0b0c23aad # v4.0.0
|
||||||
|
id: find-comment
|
||||||
|
with:
|
||||||
|
token: ${{secrets.GITHUB_TOKEN}}
|
||||||
|
issue-number: ${{ github.event.pull_request.number }}
|
||||||
|
body-includes: "📦 Build artifacts ready!"
|
||||||
|
- uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0
|
||||||
|
with:
|
||||||
|
token: ${{secrets.GITHUB_TOKEN}}
|
||||||
|
comment-id: ${{ steps.find-comment.outputs.comment-id }}
|
||||||
|
issue-number: ${{ github.event.pull_request.number }}
|
||||||
|
body: |
|
||||||
|
## 📦 Build artifacts ready!
|
||||||
|
|
||||||
|
Download binaries from [this workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}).
|
||||||
|
|
||||||
|
Available platforms: Linux, macOS, Windows (amd64, arm64)
|
||||||
|
edit-mode: replace
|
||||||
6
.github/workflows/release-nightly.yml
vendored
6
.github/workflows/release-nightly.yml
vendored
@@ -13,17 +13,17 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
|
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
|
||||||
with:
|
with:
|
||||||
go-version: 1.26.x
|
go-version: 1.26.x
|
||||||
|
|
||||||
- name: Run GoReleaser
|
- name: Run GoReleaser
|
||||||
uses: goreleaser/goreleaser-action@f06c13b6b1a9625abc9e6e439d9c05a8f2190e94 # v7
|
uses: goreleaser/goreleaser-action@ec59f474b9834571250b370d4735c50f8e2d1e29 # v7
|
||||||
with:
|
with:
|
||||||
distribution: goreleaser-pro
|
distribution: goreleaser-pro
|
||||||
version: latest
|
version: latest
|
||||||
|
|||||||
13
.github/workflows/release.yml
vendored
13
.github/workflows/release.yml
vendored
@@ -14,16 +14,16 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
|
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
|
||||||
with:
|
with:
|
||||||
go-version: 1.26.x
|
go-version: 1.26.x
|
||||||
|
|
||||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||||
with:
|
with:
|
||||||
node-version: "24"
|
node-version: "24"
|
||||||
registry-url: "https://registry.npmjs.org"
|
registry-url: "https://registry.npmjs.org"
|
||||||
@@ -32,23 +32,22 @@ jobs:
|
|||||||
run: npm install -g npm@latest
|
run: npm install -g npm@latest
|
||||||
|
|
||||||
- name: Install Task
|
- name: Install Task
|
||||||
uses: go-task/setup-task@01a4adf9db2d14c1de7a560f09170b6e0df736aa # v2
|
uses: go-task/setup-task@3be4020d41929789a01026e0e427a4321ce0ad44 # v2
|
||||||
|
|
||||||
- name: Install pnpm
|
- name: Install pnpm
|
||||||
uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6
|
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5
|
||||||
with:
|
with:
|
||||||
package_json_file: "website/package.json"
|
package_json_file: "website/package.json"
|
||||||
run_install: "true"
|
run_install: "true"
|
||||||
|
|
||||||
- name: Run GoReleaser
|
- name: Run GoReleaser
|
||||||
uses: goreleaser/goreleaser-action@f06c13b6b1a9625abc9e6e439d9c05a8f2190e94 # v7
|
uses: goreleaser/goreleaser-action@ec59f474b9834571250b370d4735c50f8e2d1e29 # v7
|
||||||
with:
|
with:
|
||||||
distribution: goreleaser-pro
|
distribution: goreleaser-pro
|
||||||
version: latest
|
version: latest
|
||||||
args: release --clean --draft
|
args: release --clean --draft
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||||
GH_GORELEASER_TOKEN: ${{secrets.GH_GORELEASER_TOKEN}}
|
|
||||||
GORELEASER_KEY: ${{secrets.GORELEASER_KEY}}
|
GORELEASER_KEY: ${{secrets.GORELEASER_KEY}}
|
||||||
CLOUDSMITH_TOKEN: ${{secrets.CLOUDSMITH_TOKEN}}
|
CLOUDSMITH_TOKEN: ${{secrets.CLOUDSMITH_TOKEN}}
|
||||||
|
|
||||||
|
|||||||
6
.github/workflows/test.yml
vendored
6
.github/workflows/test.yml
vendored
@@ -17,15 +17,15 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
go-version: [1.25.10, 1.26.x]
|
go-version: [1.25.x, 1.26.x]
|
||||||
platform: [ubuntu-latest, macos-latest, windows-latest]
|
platform: [ubuntu-latest, macos-latest, windows-latest]
|
||||||
runs-on: ${{matrix.platform}}
|
runs-on: ${{matrix.platform}}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
|
||||||
- name: Set up Go ${{matrix.go-version}}
|
- name: Set up Go ${{matrix.go-version}}
|
||||||
uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0
|
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
|
||||||
with:
|
with:
|
||||||
go-version: ${{matrix.go-version}}
|
go-version: ${{matrix.go-version}}
|
||||||
|
|
||||||
|
|||||||
@@ -2,16 +2,33 @@ version: "2"
|
|||||||
|
|
||||||
formatters:
|
formatters:
|
||||||
enable:
|
enable:
|
||||||
|
- gofmt
|
||||||
- gofumpt
|
- gofumpt
|
||||||
- goimports
|
- goimports
|
||||||
- gci
|
- gci
|
||||||
settings:
|
settings:
|
||||||
|
gofmt:
|
||||||
|
simplify: true
|
||||||
|
rewrite-rules:
|
||||||
|
- pattern: interface{}
|
||||||
|
replacement: any
|
||||||
|
gofumpt:
|
||||||
|
module-path: github.com/go-task/task/v3
|
||||||
|
goimports:
|
||||||
|
local-prefixes:
|
||||||
|
- github.com/go-task
|
||||||
gci:
|
gci:
|
||||||
sections:
|
sections:
|
||||||
- standard
|
- standard
|
||||||
- default
|
- default
|
||||||
- prefix(github.com/go-task)
|
- prefix(github.com/go-task)
|
||||||
- localmodule
|
- localmodule
|
||||||
|
exclusions:
|
||||||
|
generated: lax
|
||||||
|
paths:
|
||||||
|
- third_party$
|
||||||
|
- builtin$
|
||||||
|
- examples$
|
||||||
|
|
||||||
linters:
|
linters:
|
||||||
enable:
|
enable:
|
||||||
@@ -19,7 +36,6 @@ linters:
|
|||||||
- gosec
|
- gosec
|
||||||
- mirror
|
- mirror
|
||||||
- misspell
|
- misspell
|
||||||
- modernize
|
|
||||||
- noctx
|
- noctx
|
||||||
- paralleltest
|
- paralleltest
|
||||||
- thelper
|
- thelper
|
||||||
@@ -40,8 +56,13 @@ linters:
|
|||||||
- pkg: errors
|
- pkg: errors
|
||||||
desc: Use github.com/go-task/task/v3/errors instead
|
desc: Use github.com/go-task/task/v3/errors instead
|
||||||
exclusions:
|
exclusions:
|
||||||
|
generated: lax
|
||||||
presets:
|
presets:
|
||||||
- comments
|
- comments
|
||||||
- common-false-positives
|
- common-false-positives
|
||||||
- legacy
|
- legacy
|
||||||
- std-error-handling
|
- std-error-handling
|
||||||
|
paths:
|
||||||
|
- third_party$
|
||||||
|
- builtin$
|
||||||
|
- examples$
|
||||||
|
|||||||
31
.goreleaser-pr.yml
Normal file
31
.goreleaser-pr.yml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
|
||||||
|
version: 2
|
||||||
|
|
||||||
|
builds:
|
||||||
|
- binary: task
|
||||||
|
main: ./cmd/task
|
||||||
|
goos: [windows, darwin, linux]
|
||||||
|
goarch: [amd64, arm64]
|
||||||
|
env:
|
||||||
|
- CGO_ENABLED=0
|
||||||
|
mod_timestamp: '{{ .CommitTimestamp }}'
|
||||||
|
flags:
|
||||||
|
- -trimpath
|
||||||
|
ldflags:
|
||||||
|
- "-s -w"
|
||||||
|
|
||||||
|
archives:
|
||||||
|
- name_template: '{{.Binary}}_{{.Os}}_{{.Arch}}'
|
||||||
|
files:
|
||||||
|
- README.md
|
||||||
|
- LICENSE
|
||||||
|
- completion/**/*
|
||||||
|
format_overrides:
|
||||||
|
- goos: windows
|
||||||
|
formats: [zip]
|
||||||
|
|
||||||
|
snapshot:
|
||||||
|
version_template: 'pr-{{ .ShortCommit }}'
|
||||||
|
|
||||||
|
checksum:
|
||||||
|
name_template: 'task_checksums.txt'
|
||||||
@@ -76,7 +76,7 @@ nfpms:
|
|||||||
- src: completion/bash/task.bash
|
- src: completion/bash/task.bash
|
||||||
dst: /etc/bash_completion.d/task
|
dst: /etc/bash_completion.d/task
|
||||||
- src: completion/fish/task.fish
|
- src: completion/fish/task.fish
|
||||||
dst: /usr/share/fish/vendor_completions.d/task.fish
|
dst: /usr/share/fish/completions/task.fish
|
||||||
- src: completion/zsh/_task
|
- src: completion/zsh/_task
|
||||||
dst: /usr/local/share/zsh/site-functions/_task
|
dst: /usr/local/share/zsh/site-functions/_task
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ brews:
|
|||||||
repository:
|
repository:
|
||||||
owner: go-task
|
owner: go-task
|
||||||
name: homebrew-tap
|
name: homebrew-tap
|
||||||
token: "{{.Env.GH_GORELEASER_TOKEN}}" # So that it runs as the task-bot user
|
token: "{{secrets.GH_GORELEASER_TOKEN}}" # So that it runs as the task-bot user
|
||||||
test: system "#{bin}/task", "--help"
|
test: system "#{bin}/task", "--help"
|
||||||
install: |-
|
install: |-
|
||||||
bin.install "task"
|
bin.install "task"
|
||||||
@@ -131,7 +131,7 @@ winget:
|
|||||||
owner: go-task
|
owner: go-task
|
||||||
name: winget-pkgs
|
name: winget-pkgs
|
||||||
branch: 'task-{{.Version}}'
|
branch: 'task-{{.Version}}'
|
||||||
token: "{{.Env.GH_GORELEASER_TOKEN}}" # So that it runs as the task-bot user
|
token: "{{secrets.GH_GORELEASER_TOKEN}}" # So that it runs as the task-bot user
|
||||||
pull_request:
|
pull_request:
|
||||||
enabled: true
|
enabled: true
|
||||||
draft: false
|
draft: false
|
||||||
|
|||||||
6
.vscode/settings-sample.json
vendored
6
.vscode/settings-sample.json
vendored
@@ -5,12 +5,6 @@
|
|||||||
"Taskfile.yaml",
|
"Taskfile.yaml",
|
||||||
"taskfile.yml",
|
"taskfile.yml",
|
||||||
"taskfile.yaml"
|
"taskfile.yaml"
|
||||||
],
|
|
||||||
"./website/src/public/schema-taskrc.json": [
|
|
||||||
".taskrc.yml",
|
|
||||||
".taskrc.yaml",
|
|
||||||
"taskrc.yml",
|
|
||||||
"taskrc.yaml"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"gopls": {
|
"gopls": {
|
||||||
|
|||||||
51
CHANGELOG.md
51
CHANGELOG.md
@@ -2,51 +2,8 @@
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
- Fixed --interactive prompts for required vars sometimes appearing in a random
|
|
||||||
order. Prompts now follow the order the vars are declared in the Taskfile.
|
|
||||||
(#2871 by @caproven)
|
|
||||||
- Fixed Fish completions not being picked up correctly by installing them to
|
|
||||||
Fish's `vendor_completions.d` directory instead of `completions` (#2850, #2859
|
|
||||||
by @Legimity).
|
|
||||||
- PowerShell completions now work with aliases of the `task` command, not just
|
|
||||||
the `task` binary itself (#2852 by @kojiishi).
|
|
||||||
- Fixed task and namespace aliases not being completed by the Zsh completion. A
|
|
||||||
`show-aliases` zstyle can turn this off (#2865, #2864 by @vmaerten).
|
|
||||||
- Fixed task names containing certain characters (e.g. `\`, `_`, `^`) leaking
|
|
||||||
into checksum/timestamp filenames, breaking `sources:`/`generates:` up-to-date
|
|
||||||
detection (#2886 by @s3onghyun).
|
|
||||||
- Fixed `for: matrix:` loops using `ref:` rows producing wrong values when the
|
|
||||||
same task was run concurrently (e.g. by parallel `deps`) with different vars
|
|
||||||
(#2890, #2894 by @amitmishra11).
|
|
||||||
- Added a `secret: true` flag for variables that masks their value in logs,
|
|
||||||
`task --summary`, and command output (#2514 by @vmaerten).
|
|
||||||
- Added the `use_gitignore` setting (global or per-task) to skip files matched
|
|
||||||
by your `.gitignore` when fingerprinting `sources`/`generates` and when
|
|
||||||
watching (#2773 by @vmaerten).
|
|
||||||
- Added support for configuring output flags (`--output`,
|
|
||||||
`--output-group-begin`, `--output-group-end`, `--output-group-error-only`) via
|
|
||||||
the `TASK_OUTPUT*` environment variables (#2873 by @liiight).
|
|
||||||
- Added a `--temp-dir` flag (with `TASK_TEMP_DIR` env var and `temp-dir` taskrc
|
|
||||||
config) to customise the directory where Task stores temporary files such as
|
|
||||||
checksums. Relative paths are resolved against the root Taskfile (#2891 by
|
|
||||||
@kjasn).
|
|
||||||
- Defined environment variable behavior for remote taskfiles (#2267, #2847 by
|
|
||||||
@vmaerten).
|
|
||||||
|
|
||||||
## v3.51.1 - 2026-05-16
|
|
||||||
|
|
||||||
- A significant performance boost was achieved for large Taskfiles (monorepos)
|
|
||||||
by skipping templating altogether when the string is static (#2820 by @romnn).
|
|
||||||
- Added `absPath` template function that resolves a path to its absolute form,
|
- Added `absPath` template function that resolves a path to its absolute form,
|
||||||
cleaning `..` and `.` components (#2681, #2788 by @mateenanjum).
|
cleaning `..` and `.` components (#2681, #2788 by @mateenanjum).
|
||||||
- Added `joinEnv` function to join paths based on your oprating system: `;` for
|
|
||||||
Windows and `:` elsewhere, and `joinUrl` to join URL paths. Also, added two
|
|
||||||
new special variables: `FILE_PATH_SEPARATOR` which returns `\` on Windows and
|
|
||||||
`/` elsewhere, and `PATH_LIST_SEPARATOR` which returns `;` on Windows and `:`
|
|
||||||
elsewhere (#2406, #2408 by @solvingj).
|
|
||||||
- Update the shell interpreter with a regression fix (#2812, #2832 by
|
|
||||||
@andreynering).
|
|
||||||
- Fix potential panic with the shell interpreter (#2810 by @trulede).
|
|
||||||
|
|
||||||
## v3.50.0 - 2026-04-13
|
## v3.50.0 - 2026-04-13
|
||||||
|
|
||||||
@@ -59,13 +16,13 @@
|
|||||||
- Fixed watch mode ignoring SIGHUP signal, causing the watcher to exit instead
|
- Fixed watch mode ignoring SIGHUP signal, causing the watcher to exit instead
|
||||||
of restarting (#2764, #2642).
|
of restarting (#2764, #2642).
|
||||||
- Fixed a long time bug where the task wouldn't re-run as it should when using
|
- Fixed a long time bug where the task wouldn't re-run as it should when using
|
||||||
`method: timestamp` and the files listed on `generates:` were deleted. This
|
`method: timestamp` and the files listed on `generates:` were deleted.
|
||||||
makes `method: timestamp` behaves the same as `method: checksum` (#1230, #2716
|
This makes `method: timestamp` behaves the same as `method: checksum`
|
||||||
by @drichardson).
|
(#1230, #2716 by @drichardson).
|
||||||
|
|
||||||
## v3.49.1 - 2026-03-08
|
## v3.49.1 - 2026-03-08
|
||||||
|
|
||||||
- Reverted #2632 for now, which caused some regressions. That change will be
|
* Reverted #2632 for now, which caused some regressions. That change will be
|
||||||
reworked (#2720, #2722, #2723).
|
reworked (#2720, #2722, #2723).
|
||||||
|
|
||||||
## v3.49.0 - 2026-03-07
|
## v3.49.0 - 2026-03-07
|
||||||
|
|||||||
46
compiler.go
46
compiler.go
@@ -15,7 +15,6 @@ import (
|
|||||||
"github.com/go-task/task/v3/internal/logger"
|
"github.com/go-task/task/v3/internal/logger"
|
||||||
"github.com/go-task/task/v3/internal/templater"
|
"github.com/go-task/task/v3/internal/templater"
|
||||||
"github.com/go-task/task/v3/internal/version"
|
"github.com/go-task/task/v3/internal/version"
|
||||||
"github.com/go-task/task/v3/taskfile"
|
|
||||||
"github.com/go-task/task/v3/taskfile/ast"
|
"github.com/go-task/task/v3/taskfile/ast"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -52,7 +51,7 @@ func (c *Compiler) getVariables(t *ast.Task, call *Call, evaluateShVars bool) (*
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for k, v := range specialVars {
|
for k, v := range specialVars {
|
||||||
result.Set(k, ast.Var{Value: v, Secret: false})
|
result.Set(k, ast.Var{Value: v})
|
||||||
}
|
}
|
||||||
|
|
||||||
getRangeFunc := func(dir string) func(k string, v ast.Var) error {
|
getRangeFunc := func(dir string) func(k string, v ast.Var) error {
|
||||||
@@ -64,12 +63,12 @@ func (c *Compiler) getVariables(t *ast.Task, call *Call, evaluateShVars bool) (*
|
|||||||
// 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
|
// 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: "", Sh: newVar.Sh, Secret: v.Secret})
|
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, Sh: newVar.Sh, Secret: v.Secret})
|
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
|
||||||
@@ -78,7 +77,7 @@ func (c *Compiler) getVariables(t *ast.Task, call *Call, evaluateShVars bool) (*
|
|||||||
}
|
}
|
||||||
// If the variable is already set, we can set it and return
|
// If the variable is already set, we can set it and return
|
||||||
if newVar.Value != nil || newVar.Sh == nil {
|
if newVar.Value != nil || newVar.Sh == nil {
|
||||||
result.Set(k, ast.Var{Value: newVar.Value, Secret: v.Secret})
|
result.Set(k, ast.Var{Value: newVar.Value})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// If the variable is dynamic, we need to resolve it first
|
// If the variable is dynamic, we need to resolve it first
|
||||||
@@ -86,7 +85,7 @@ func (c *Compiler) getVariables(t *ast.Task, call *Call, evaluateShVars bool) (*
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
result.Set(k, ast.Var{Value: static, Secret: v.Secret})
|
result.Set(k, ast.Var{Value: static})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -185,12 +184,7 @@ func (c *Compiler) HandleDynamicVar(v ast.Var, dir string, e []string) (string,
|
|||||||
result = strings.TrimSuffix(result, "\n")
|
result = strings.TrimSuffix(result, "\n")
|
||||||
|
|
||||||
c.dynamicCache[*v.Sh] = result
|
c.dynamicCache[*v.Sh] = result
|
||||||
// Never print the resolved value of a secret variable, even in verbose mode
|
c.Logger.VerboseErrf(logger.Magenta, "task: dynamic variable: %q result: %q\n", *v.Sh, result)
|
||||||
logResult := result
|
|
||||||
if v.Secret {
|
|
||||||
logResult = "*****"
|
|
||||||
}
|
|
||||||
c.Logger.VerboseErrf(logger.Magenta, "task: dynamic variable: %q result: %q\n", *v.Sh, logResult)
|
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
@@ -207,42 +201,18 @@ func (c *Compiler) getSpecialVars(t *ast.Task, call *Call) (map[string]string, e
|
|||||||
// Use filepath.ToSlash for all paths to ensure consistent forward slashes
|
// Use filepath.ToSlash for all paths to ensure consistent forward slashes
|
||||||
// across platforms. This prevents issues with backslashes being interpreted
|
// across platforms. This prevents issues with backslashes being interpreted
|
||||||
// as escape sequences when paths are used in shell commands on Windows.
|
// as escape sequences when paths are used in shell commands on Windows.
|
||||||
var rootTaskfile, rootDir string
|
|
||||||
if taskfile.IsRemoteEntrypoint(c.Entrypoint) {
|
|
||||||
rootTaskfile = c.Entrypoint
|
|
||||||
rootDir = ""
|
|
||||||
} else {
|
|
||||||
rootTaskfile = filepath.ToSlash(filepathext.SmartJoin(c.Dir, c.Entrypoint))
|
|
||||||
rootDir = filepath.ToSlash(c.Dir)
|
|
||||||
}
|
|
||||||
|
|
||||||
allVars := map[string]string{
|
allVars := map[string]string{
|
||||||
"TASK_EXE": filepath.ToSlash(os.Args[0]),
|
"TASK_EXE": filepath.ToSlash(os.Args[0]),
|
||||||
"ROOT_TASKFILE": rootTaskfile,
|
"ROOT_TASKFILE": filepath.ToSlash(filepathext.SmartJoin(c.Dir, c.Entrypoint)),
|
||||||
"ROOT_DIR": rootDir,
|
"ROOT_DIR": filepath.ToSlash(c.Dir),
|
||||||
"USER_WORKING_DIR": filepath.ToSlash(c.UserWorkingDir),
|
"USER_WORKING_DIR": filepath.ToSlash(c.UserWorkingDir),
|
||||||
"TASK_VERSION": version.GetVersion(),
|
"TASK_VERSION": version.GetVersion(),
|
||||||
"PATH_LIST_SEPARATOR": string(os.PathListSeparator),
|
|
||||||
"FILE_PATH_SEPARATOR": string(os.PathSeparator),
|
|
||||||
}
|
}
|
||||||
if t != nil {
|
if t != nil {
|
||||||
allVars["TASK"] = t.Task
|
allVars["TASK"] = t.Task
|
||||||
if taskfile.IsRemoteEntrypoint(t.Location.Taskfile) {
|
|
||||||
allVars["TASKFILE"] = t.Location.Taskfile
|
|
||||||
allVars["TASKFILE_DIR"] = ""
|
|
||||||
switch {
|
|
||||||
case t.Dir == "":
|
|
||||||
allVars["TASK_DIR"] = filepath.ToSlash(c.UserWorkingDir)
|
|
||||||
case filepath.IsAbs(t.Dir):
|
|
||||||
allVars["TASK_DIR"] = filepath.ToSlash(t.Dir)
|
|
||||||
default:
|
|
||||||
allVars["TASK_DIR"] = filepath.ToSlash(filepathext.SmartJoin(c.UserWorkingDir, t.Dir))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
allVars["TASK_DIR"] = filepath.ToSlash(filepathext.SmartJoin(c.Dir, t.Dir))
|
allVars["TASK_DIR"] = filepath.ToSlash(filepathext.SmartJoin(c.Dir, t.Dir))
|
||||||
allVars["TASKFILE"] = filepath.ToSlash(t.Location.Taskfile)
|
allVars["TASKFILE"] = filepath.ToSlash(t.Location.Taskfile)
|
||||||
allVars["TASKFILE_DIR"] = filepath.ToSlash(filepath.Dir(t.Location.Taskfile))
|
allVars["TASKFILE_DIR"] = filepath.ToSlash(filepath.Dir(t.Location.Taskfile))
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
allVars["TASK"] = ""
|
allVars["TASK"] = ""
|
||||||
allVars["TASK_DIR"] = ""
|
allVars["TASK_DIR"] = ""
|
||||||
|
|||||||
@@ -1,135 +0,0 @@
|
|||||||
package task
|
|
||||||
|
|
||||||
import (
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
|
|
||||||
"github.com/go-task/task/v3/internal/filepathext"
|
|
||||||
"github.com/go-task/task/v3/taskfile/ast"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestGetSpecialVarsRemote(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
uwd := t.TempDir()
|
|
||||||
uwdSlash := filepath.ToSlash(uwd)
|
|
||||||
localProj := filepath.Join(uwd, "proj")
|
|
||||||
localProjSlash := filepath.ToSlash(localProj)
|
|
||||||
localTaskfile := filepath.Join(localProj, "Taskfile.yml")
|
|
||||||
localTaskfileSlash := filepath.ToSlash(localTaskfile)
|
|
||||||
absTaskDir := filepath.Join(uwd, "opt", "work")
|
|
||||||
absTaskDirSlash := filepath.ToSlash(absTaskDir)
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
entrypoint string
|
|
||||||
compilerDir string
|
|
||||||
taskDir string
|
|
||||||
taskfileLocation string
|
|
||||||
wantRootTaskfile string
|
|
||||||
wantRootDir string
|
|
||||||
wantTaskfile string
|
|
||||||
wantTaskfileDir string
|
|
||||||
wantTaskDir string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "local entrypoint, local task",
|
|
||||||
entrypoint: localTaskfile,
|
|
||||||
compilerDir: localProj,
|
|
||||||
taskDir: "",
|
|
||||||
taskfileLocation: localTaskfile,
|
|
||||||
wantRootTaskfile: localTaskfileSlash,
|
|
||||||
wantRootDir: localProjSlash,
|
|
||||||
wantTaskfile: localTaskfileSlash,
|
|
||||||
wantTaskfileDir: localProjSlash,
|
|
||||||
wantTaskDir: localProjSlash,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "https entrypoint, empty task.dir",
|
|
||||||
entrypoint: "https://taskfile.dev/Taskfile.yml",
|
|
||||||
compilerDir: "",
|
|
||||||
taskDir: "",
|
|
||||||
taskfileLocation: "https://taskfile.dev/Taskfile.yml",
|
|
||||||
wantRootTaskfile: "https://taskfile.dev/Taskfile.yml",
|
|
||||||
wantRootDir: "",
|
|
||||||
wantTaskfile: "https://taskfile.dev/Taskfile.yml",
|
|
||||||
wantTaskfileDir: "",
|
|
||||||
wantTaskDir: uwdSlash,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "https entrypoint, relative task.dir",
|
|
||||||
entrypoint: "https://taskfile.dev/Taskfile.yml",
|
|
||||||
compilerDir: "",
|
|
||||||
taskDir: "subdir",
|
|
||||||
taskfileLocation: "https://taskfile.dev/Taskfile.yml",
|
|
||||||
wantRootTaskfile: "https://taskfile.dev/Taskfile.yml",
|
|
||||||
wantRootDir: "",
|
|
||||||
wantTaskfile: "https://taskfile.dev/Taskfile.yml",
|
|
||||||
wantTaskfileDir: "",
|
|
||||||
wantTaskDir: filepath.ToSlash(filepathext.SmartJoin(uwd, "subdir")),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "https entrypoint, absolute task.dir",
|
|
||||||
entrypoint: "https://taskfile.dev/Taskfile.yml",
|
|
||||||
compilerDir: "",
|
|
||||||
taskDir: absTaskDir,
|
|
||||||
taskfileLocation: "https://taskfile.dev/Taskfile.yml",
|
|
||||||
wantRootTaskfile: "https://taskfile.dev/Taskfile.yml",
|
|
||||||
wantRootDir: "",
|
|
||||||
wantTaskfile: "https://taskfile.dev/Taskfile.yml",
|
|
||||||
wantTaskfileDir: "",
|
|
||||||
wantTaskDir: absTaskDirSlash,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "git entrypoint",
|
|
||||||
entrypoint: "https://github.com/foo/bar.git//Taskfile.yml?ref=main",
|
|
||||||
compilerDir: "",
|
|
||||||
taskDir: "",
|
|
||||||
taskfileLocation: "https://github.com/foo/bar.git//Taskfile.yml?ref=main",
|
|
||||||
wantRootTaskfile: "https://github.com/foo/bar.git//Taskfile.yml?ref=main",
|
|
||||||
wantRootDir: "",
|
|
||||||
wantTaskfile: "https://github.com/foo/bar.git//Taskfile.yml?ref=main",
|
|
||||||
wantTaskfileDir: "",
|
|
||||||
wantTaskDir: uwdSlash,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "local root, remote included task",
|
|
||||||
entrypoint: localTaskfile,
|
|
||||||
compilerDir: localProj,
|
|
||||||
taskDir: "",
|
|
||||||
taskfileLocation: "https://taskfile.dev/included.yml",
|
|
||||||
wantRootTaskfile: localTaskfileSlash,
|
|
||||||
wantRootDir: localProjSlash,
|
|
||||||
wantTaskfile: "https://taskfile.dev/included.yml",
|
|
||||||
wantTaskfileDir: "",
|
|
||||||
wantTaskDir: uwdSlash,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
c := &Compiler{
|
|
||||||
Dir: tt.compilerDir,
|
|
||||||
Entrypoint: tt.entrypoint,
|
|
||||||
UserWorkingDir: uwd,
|
|
||||||
}
|
|
||||||
task := &ast.Task{
|
|
||||||
Task: "mytask",
|
|
||||||
Dir: tt.taskDir,
|
|
||||||
Location: &ast.Location{Taskfile: tt.taskfileLocation},
|
|
||||||
}
|
|
||||||
|
|
||||||
vars, err := c.getSpecialVars(task, nil)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, tt.wantRootTaskfile, vars["ROOT_TASKFILE"], "ROOT_TASKFILE")
|
|
||||||
assert.Equal(t, tt.wantRootDir, vars["ROOT_DIR"], "ROOT_DIR")
|
|
||||||
assert.Equal(t, tt.wantTaskfile, vars["TASKFILE"], "TASKFILE")
|
|
||||||
assert.Equal(t, tt.wantTaskfileDir, vars["TASKFILE_DIR"], "TASKFILE_DIR")
|
|
||||||
assert.Equal(t, tt.wantTaskDir, vars["TASK_DIR"], "TASK_DIR")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
using namespace System.Management.Automation
|
using namespace System.Management.Automation
|
||||||
|
|
||||||
$cmdNames = @('task') + (Get-Alias -Definition task,task.exe,*\task,*\task.exe -ErrorAction SilentlyContinue).Name | Select-Object -Unique
|
Register-ArgumentCompleter -CommandName task -ScriptBlock {
|
||||||
|
|
||||||
Register-ArgumentCompleter -CommandName $cmdNames -ScriptBlock {
|
|
||||||
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
|
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
|
||||||
|
|
||||||
if ($commandName.StartsWith('-')) {
|
if ($commandName.StartsWith('-')) {
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ function __task_is_experiment_enabled() {
|
|||||||
|
|
||||||
# Listing commands from Taskfile.yml
|
# Listing commands from Taskfile.yml
|
||||||
function __task_list() {
|
function __task_list() {
|
||||||
local -a scripts cmd task_aliases match mbegin mend
|
local -a scripts cmd
|
||||||
local -i enabled=0
|
local -i enabled=0
|
||||||
local taskfile item task desc task_alias
|
local taskfile item task desc
|
||||||
|
|
||||||
cmd=($TASK_CMD)
|
cmd=($TASK_CMD)
|
||||||
taskfile=${(Qv)opt_args[(i)-t|--taskfile]}
|
taskfile=${(Qv)opt_args[(i)-t|--taskfile]}
|
||||||
@@ -50,31 +50,14 @@ function __task_list() {
|
|||||||
local show_desc
|
local show_desc
|
||||||
zstyle -T ":completion:${curcontext}:" verbose && show_desc=true || show_desc=false
|
zstyle -T ":completion:${curcontext}:" verbose && show_desc=true || show_desc=false
|
||||||
|
|
||||||
# Read zstyle show-aliases option (default = true via -T)
|
|
||||||
local show_aliases
|
|
||||||
zstyle -T ":completion:${curcontext}:" show-aliases && show_aliases=true || show_aliases=false
|
|
||||||
|
|
||||||
for item in "${(@)${(f)output}[2,-1]#\* }"; do
|
for item in "${(@)${(f)output}[2,-1]#\* }"; do
|
||||||
task="${item%%:[[:space:]]*}"
|
task="${item%%:[[:space:]]*}"
|
||||||
|
|
||||||
# Extract the aliases listed in the trailing "(aliases: a, b)" column.
|
|
||||||
# NB: `aliases` is a reserved zsh parameter, so use a different name.
|
|
||||||
task_aliases=()
|
|
||||||
if [[ "$show_aliases" == "true" && "$item" == (#b)*'(aliases: '(*)')' ]]; then
|
|
||||||
task_aliases=( "${(@s:, :)match[1]}" )
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$show_desc" == "true" ]]; then
|
if [[ "$show_desc" == "true" ]]; then
|
||||||
local desc="${item##[^[:space:]]##[[:space:]]##}"
|
local desc="${item##[^[:space:]]##[[:space:]]##}"
|
||||||
scripts+=( "${task//:/\\:}:$desc" )
|
scripts+=( "${task//:/\\:}:$desc" )
|
||||||
for task_alias in $task_aliases; do
|
|
||||||
scripts+=( "${task_alias//:/\\:}:$desc (alias of $task)" )
|
|
||||||
done
|
|
||||||
else
|
else
|
||||||
scripts+=( "$task" )
|
scripts+=( "$task" )
|
||||||
for task_alias in $task_aliases; do
|
|
||||||
scripts+=( "$task_alias" )
|
|
||||||
done
|
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|||||||
33
executor.go
33
executor.go
@@ -29,7 +29,6 @@ type (
|
|||||||
Dir string
|
Dir string
|
||||||
Entrypoint string
|
Entrypoint string
|
||||||
TempDir TempDir
|
TempDir TempDir
|
||||||
TempDirPath string
|
|
||||||
Force bool
|
Force bool
|
||||||
ForceAll bool
|
ForceAll bool
|
||||||
Insecure bool
|
Insecure bool
|
||||||
@@ -68,6 +67,7 @@ type (
|
|||||||
Compiler *Compiler
|
Compiler *Compiler
|
||||||
Output output.Output
|
Output output.Output
|
||||||
OutputStyle ast.Output
|
OutputStyle ast.Output
|
||||||
|
OutputCIAuto bool
|
||||||
TaskSorter sort.Sorter
|
TaskSorter sort.Sorter
|
||||||
UserWorkingDir string
|
UserWorkingDir string
|
||||||
EnableVersionCheck bool
|
EnableVersionCheck bool
|
||||||
@@ -166,22 +166,6 @@ func (o *tempDirOption) ApplyToExecutor(e *Executor) {
|
|||||||
e.TempDir = o.tempDir
|
e.TempDir = o.tempDir
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithTempDirPath sets an unresolved path used to build [Executor.TempDir]
|
|
||||||
// during [Executor.Setup]. Relative paths are resolved from the root Taskfile
|
|
||||||
// directory. Use [WithTempDir] when the remote and fingerprint directories have
|
|
||||||
// already been resolved.
|
|
||||||
func WithTempDirPath(path string) ExecutorOption {
|
|
||||||
return &tempDirPathOption{path: path}
|
|
||||||
}
|
|
||||||
|
|
||||||
type tempDirPathOption struct {
|
|
||||||
path string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (o *tempDirPathOption) ApplyToExecutor(e *Executor) {
|
|
||||||
e.TempDirPath = o.path
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithForce ensures that the [Executor] always runs a task, even when
|
// WithForce ensures that the [Executor] always runs a task, even when
|
||||||
// fingerprinting or prompts would normally stop it.
|
// fingerprinting or prompts would normally stop it.
|
||||||
func WithForce(force bool) ExecutorOption {
|
func WithForce(force bool) ExecutorOption {
|
||||||
@@ -539,6 +523,21 @@ func (o *outputStyleOption) ApplyToExecutor(e *Executor) {
|
|||||||
e.OutputStyle = o.outputStyle
|
e.OutputStyle = o.outputStyle
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithOutputCIAuto enables automatic selection of a CI-aware output style
|
||||||
|
// (e.g. "gitlab") when a supported CI environment is detected and no explicit
|
||||||
|
// output style is configured in the Taskfile or via CLI.
|
||||||
|
func WithOutputCIAuto(enabled bool) ExecutorOption {
|
||||||
|
return &outputCIAutoOption{enabled}
|
||||||
|
}
|
||||||
|
|
||||||
|
type outputCIAutoOption struct {
|
||||||
|
enabled bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *outputCIAutoOption) ApplyToExecutor(e *Executor) {
|
||||||
|
e.OutputCIAuto = o.enabled
|
||||||
|
}
|
||||||
|
|
||||||
// WithTaskSorter sets the sorter that the [Executor] will use to sort tasks. By
|
// WithTaskSorter sets the sorter that the [Executor] will use to sort tasks. By
|
||||||
// default, the sorter is set to sort tasks alphabetically, but with tasks with
|
// default, the sorter is set to sort tasks alphabetically, but with tasks with
|
||||||
// no namespace (in the root Taskfile) first.
|
// no namespace (in the root Taskfile) first.
|
||||||
|
|||||||
@@ -283,68 +283,6 @@ func TestVars(t *testing.T) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSecretVars(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
NewExecutorTest(t,
|
|
||||||
WithName("secret vars are masked in logs"),
|
|
||||||
WithExecutorOptions(
|
|
||||||
task.WithDir("testdata/secrets"),
|
|
||||||
),
|
|
||||||
WithTask("test-secret-masking"),
|
|
||||||
)
|
|
||||||
NewExecutorTest(t,
|
|
||||||
WithName("multiple secrets masked"),
|
|
||||||
WithExecutorOptions(
|
|
||||||
task.WithDir("testdata/secrets"),
|
|
||||||
),
|
|
||||||
WithTask("test-multiple-secrets"),
|
|
||||||
)
|
|
||||||
NewExecutorTest(t,
|
|
||||||
WithName("mixed secret and public vars"),
|
|
||||||
WithExecutorOptions(
|
|
||||||
task.WithDir("testdata/secrets"),
|
|
||||||
),
|
|
||||||
WithTask("test-mixed"),
|
|
||||||
)
|
|
||||||
NewExecutorTest(t,
|
|
||||||
WithName("deferred command with secrets"),
|
|
||||||
WithExecutorOptions(
|
|
||||||
task.WithDir("testdata/secrets"),
|
|
||||||
),
|
|
||||||
WithTask("test-deferred-secret"),
|
|
||||||
)
|
|
||||||
NewExecutorTest(t,
|
|
||||||
WithName("env secret limitation"),
|
|
||||||
WithExecutorOptions(
|
|
||||||
task.WithDir("testdata/secrets"),
|
|
||||||
),
|
|
||||||
WithTask("test-env-secret-limitation"),
|
|
||||||
)
|
|
||||||
NewExecutorTest(t,
|
|
||||||
WithName("secret vars are masked in summary"),
|
|
||||||
WithExecutorOptions(
|
|
||||||
task.WithDir("testdata/secrets"),
|
|
||||||
task.WithSummary(true),
|
|
||||||
),
|
|
||||||
WithTask("test-secret-masking"),
|
|
||||||
)
|
|
||||||
NewExecutorTest(t,
|
|
||||||
WithName("dynamic secret masked in verbose"),
|
|
||||||
WithExecutorOptions(
|
|
||||||
task.WithDir("testdata/secrets"),
|
|
||||||
task.WithVerbose(true),
|
|
||||||
),
|
|
||||||
WithTask("test-dynamic-secret-verbose"),
|
|
||||||
)
|
|
||||||
NewExecutorTest(t,
|
|
||||||
WithName("secret key order independent"),
|
|
||||||
WithExecutorOptions(
|
|
||||||
task.WithDir("testdata/secrets"),
|
|
||||||
),
|
|
||||||
WithTask("test-secret-key-order"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRequires(t *testing.T) {
|
func TestRequires(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
NewExecutorTest(t,
|
NewExecutorTest(t,
|
||||||
|
|||||||
156
go.mod
156
go.mod
@@ -1,136 +1,136 @@
|
|||||||
module github.com/go-task/task/v3
|
module github.com/go-task/task/v3
|
||||||
|
|
||||||
go 1.25.10
|
go 1.25.8
|
||||||
|
|
||||||
require (
|
require (
|
||||||
charm.land/bubbles/v2 v2.1.0
|
charm.land/bubbles/v2 v2.1.0
|
||||||
charm.land/bubbletea/v2 v2.0.7
|
charm.land/bubbletea/v2 v2.0.2
|
||||||
charm.land/lipgloss/v2 v2.0.4
|
charm.land/lipgloss/v2 v2.0.2
|
||||||
github.com/Ladicle/tabwriter v1.0.0
|
github.com/Ladicle/tabwriter v1.0.0
|
||||||
github.com/Masterminds/semver/v3 v3.5.0
|
github.com/Masterminds/semver/v3 v3.4.0
|
||||||
github.com/alecthomas/chroma/v2 v2.27.0
|
github.com/alecthomas/chroma/v2 v2.23.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.2-0.20180830191138-d8f796af33cc
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
|
||||||
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.19.0
|
github.com/fatih/color v1.19.0
|
||||||
github.com/fsnotify/fsnotify v1.10.1
|
github.com/fsnotify/fsnotify v1.9.0
|
||||||
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.6
|
github.com/hashicorp/go-getter v1.8.6
|
||||||
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/v4 v4.5.0
|
github.com/puzpuzpuz/xsync/v4 v4.4.0
|
||||||
github.com/sajari/fuzzy v1.0.0
|
github.com/sajari/fuzzy v1.0.0
|
||||||
github.com/sebdah/goldie/v2 v2.8.0
|
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.1.0
|
github.com/zeebo/xxh3 v1.1.0
|
||||||
go.yaml.in/yaml/v3 v3.0.4
|
go.yaml.in/yaml/v3 v3.0.4
|
||||||
golang.org/x/sync v0.21.0
|
golang.org/x/sync v0.20.0
|
||||||
golang.org/x/term v0.44.0
|
golang.org/x/term v0.42.0
|
||||||
mvdan.cc/sh/moreinterp v0.0.0-20260120230322-19def062a997
|
mvdan.cc/sh/moreinterp v0.0.0-20260120230322-19def062a997
|
||||||
mvdan.cc/sh/v3 v3.13.2-0.20260613075524-2255122b577b
|
mvdan.cc/sh/v3 v3.13.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cel.dev/expr v0.25.2 // indirect
|
cel.dev/expr v0.25.1 // indirect
|
||||||
cloud.google.com/go v0.123.0 // indirect
|
cloud.google.com/go v0.123.0 // indirect
|
||||||
cloud.google.com/go/auth v0.20.0 // indirect
|
cloud.google.com/go/auth v0.18.2 // indirect
|
||||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
||||||
cloud.google.com/go/compute/metadata v0.9.0 // indirect
|
cloud.google.com/go/compute/metadata v0.9.0 // indirect
|
||||||
cloud.google.com/go/iam v1.11.0 // indirect
|
cloud.google.com/go/iam v1.5.3 // indirect
|
||||||
cloud.google.com/go/monitoring v1.29.0 // indirect
|
cloud.google.com/go/monitoring v1.24.3 // indirect
|
||||||
cloud.google.com/go/storage v1.63.0 // indirect
|
cloud.google.com/go/storage v1.61.3 // indirect
|
||||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.33.0 // indirect
|
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 // indirect
|
||||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.57.0 // indirect
|
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0 // indirect
|
||||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.57.0 // indirect
|
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0 // indirect
|
||||||
github.com/atotto/clipboard v0.1.4 // indirect
|
github.com/atotto/clipboard v0.1.4 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2 v1.42.0 // indirect
|
github.com/aws/aws-sdk-go-v2 v1.41.5 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.13 // indirect
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.32.26 // indirect
|
github.com/aws/aws-sdk-go-v2/config v1.32.12 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.25 // indirect
|
github.com/aws/aws-sdk-go-v2/credentials v1.19.12 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.29 // indirect
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.30 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.12 // indirect
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.22 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.29 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.30 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.104.1 // indirect
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/signin v1.2.1 // indirect
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.3 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.31.4 // indirect
|
github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.7 // indirect
|
github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 // indirect
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.43.4 // indirect
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 // indirect
|
||||||
github.com/aws/smithy-go v1.27.3 // indirect
|
github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 // indirect
|
||||||
|
github.com/aws/smithy-go v1.24.2 // indirect
|
||||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
|
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/charmbracelet/colorprofile v0.4.3 // indirect
|
github.com/charmbracelet/colorprofile v0.4.2 // indirect
|
||||||
github.com/charmbracelet/ultraviolet v0.0.0-20260622092850-f39628c8a989 // indirect
|
github.com/charmbracelet/ultraviolet v0.0.0-20260205113103-524a6607adb8 // indirect
|
||||||
github.com/charmbracelet/x/ansi v0.11.7 // indirect
|
github.com/charmbracelet/x/ansi v0.11.6 // indirect
|
||||||
github.com/charmbracelet/x/term v0.2.2 // indirect
|
github.com/charmbracelet/x/term v0.2.2 // indirect
|
||||||
github.com/charmbracelet/x/termios v0.1.1 // indirect
|
github.com/charmbracelet/x/termios v0.1.1 // indirect
|
||||||
github.com/charmbracelet/x/windows v0.2.2 // indirect
|
github.com/charmbracelet/x/windows v0.2.2 // indirect
|
||||||
github.com/clipperhouse/displaywidth v0.11.0 // indirect
|
github.com/clipperhouse/displaywidth v0.11.0 // indirect
|
||||||
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
|
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
|
||||||
github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 // indirect
|
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 // indirect
|
||||||
github.com/dlclark/regexp2/v2 v2.2.2 // 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/envoyproxy/go-control-plane/envoy v1.37.0 // indirect
|
github.com/envoyproxy/go-control-plane/envoy v1.36.0 // indirect
|
||||||
github.com/envoyproxy/protoc-gen-validate v1.3.3 // indirect
|
github.com/envoyproxy/protoc-gen-validate v1.3.0 // indirect
|
||||||
github.com/felixge/httpsnoop v1.1.0 // indirect
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
github.com/go-jose/go-jose/v4 v4.1.4 // indirect
|
github.com/go-jose/go-jose/v4 v4.1.4 // indirect
|
||||||
github.com/go-logr/logr v1.4.3 // indirect
|
github.com/go-logr/logr v1.4.3 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/google/s2a-go v0.1.9 // indirect
|
github.com/google/s2a-go v0.1.9 // indirect
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.3.17 // indirect
|
github.com/googleapis/enterprise-certificate-proxy v0.3.14 // indirect
|
||||||
github.com/googleapis/gax-go/v2 v2.22.0 // indirect
|
github.com/googleapis/gax-go/v2 v2.17.0 // indirect
|
||||||
github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.73 // indirect
|
github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.72 // indirect
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||||
github.com/hashicorp/go-version v1.9.0 // indirect
|
github.com/hashicorp/go-version v1.8.0 // indirect
|
||||||
github.com/klauspost/compress v1.18.7 // indirect
|
github.com/klauspost/compress v1.18.5 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.4.0 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
||||||
github.com/klauspost/pgzip v1.2.6 // indirect
|
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||||
github.com/lucasb-eyer/go-colorful v1.4.0 // indirect
|
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.15 // indirect
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.22 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.24 // indirect
|
github.com/mattn/go-runewidth v0.0.21 // indirect
|
||||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||||
github.com/pierrec/lz4/v4 v4.1.27 // indirect
|
github.com/pierrec/lz4/v4 v4.1.22 // indirect
|
||||||
github.com/planetscale/vtprotobuf v0.6.1-0.20250313105119-ba97887b0a25 // indirect
|
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
github.com/sergi/go-diff v1.4.0 // indirect
|
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
||||||
github.com/spiffe/go-spiffe/v2 v2.8.1 // indirect
|
github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect
|
||||||
github.com/stretchr/objx v0.5.3 // indirect
|
github.com/stretchr/objx v0.5.2 // indirect
|
||||||
github.com/u-root/u-root v0.16.0 // indirect
|
github.com/u-root/u-root v0.15.1-0.20251208185023-2f8c7e763cf8 // 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/ulikunitz/xz v0.5.15 // indirect
|
github.com/ulikunitz/xz v0.5.15 // indirect
|
||||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||||
go.opentelemetry.io/contrib/detectors/gcp v1.44.0 // indirect
|
go.opentelemetry.io/contrib/detectors/gcp v1.39.0 // indirect
|
||||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.69.0 // indirect
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 // indirect
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
|
||||||
go.opentelemetry.io/otel v1.44.0 // indirect
|
go.opentelemetry.io/otel v1.43.0 // indirect
|
||||||
go.opentelemetry.io/otel/metric v1.44.0 // indirect
|
go.opentelemetry.io/otel/metric v1.43.0 // indirect
|
||||||
go.opentelemetry.io/otel/sdk v1.44.0 // indirect
|
go.opentelemetry.io/otel/sdk v1.43.0 // indirect
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.44.0 // indirect
|
go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.44.0 // indirect
|
go.opentelemetry.io/otel/trace v1.43.0 // indirect
|
||||||
golang.org/x/crypto v0.53.0 // indirect
|
golang.org/x/crypto v0.49.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20260611194520-c48552f49976 // indirect
|
golang.org/x/net v0.52.0 // indirect
|
||||||
golang.org/x/net v0.56.0 // indirect
|
|
||||||
golang.org/x/oauth2 v0.36.0 // indirect
|
golang.org/x/oauth2 v0.36.0 // indirect
|
||||||
golang.org/x/sys v0.46.0 // indirect
|
golang.org/x/sys v0.43.0 // indirect
|
||||||
golang.org/x/text v0.38.0 // indirect
|
golang.org/x/text v0.35.0 // indirect
|
||||||
golang.org/x/time v0.15.0 // indirect
|
golang.org/x/time v0.15.0 // indirect
|
||||||
google.golang.org/api v0.287.0 // indirect
|
google.golang.org/api v0.271.0 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20260630182238-925bb5da69e7 // indirect
|
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20260630182238-925bb5da69e7 // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260630182238-925bb5da69e7 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect
|
||||||
google.golang.org/grpc v1.82.0 // indirect
|
google.golang.org/grpc v1.79.3 // indirect
|
||||||
google.golang.org/protobuf v1.36.11 // indirect
|
google.golang.org/protobuf v1.36.11 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
341
go.sum
341
go.sum
@@ -1,87 +1,89 @@
|
|||||||
cel.dev/expr v0.25.2 h1:K6j46C81hXtZQfuX60cVWQFBJahKSE2gfRbNuvr5bFs=
|
cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4=
|
||||||
cel.dev/expr v0.25.2/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4=
|
cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4=
|
||||||
charm.land/bubbles/v2 v2.1.0 h1:YSnNh5cPYlYjPxRrzs5VEn3vwhtEn3jVGRBT3M7/I0g=
|
charm.land/bubbles/v2 v2.1.0 h1:YSnNh5cPYlYjPxRrzs5VEn3vwhtEn3jVGRBT3M7/I0g=
|
||||||
charm.land/bubbles/v2 v2.1.0/go.mod h1:l97h4hym2hvWBVfmJDtrEHHCtkIKeTEb3TTJ4ZOB3wY=
|
charm.land/bubbles/v2 v2.1.0/go.mod h1:l97h4hym2hvWBVfmJDtrEHHCtkIKeTEb3TTJ4ZOB3wY=
|
||||||
charm.land/bubbletea/v2 v2.0.7 h1:7qw2tTAVar7m7klOPBYfTB0mniv/RuexsYwMRNxSeL0=
|
charm.land/bubbletea/v2 v2.0.2 h1:4CRtRnuZOdFDTWSff9r8QFt/9+z6Emubz3aDMnf/dx0=
|
||||||
charm.land/bubbletea/v2 v2.0.7/go.mod h1:DGW2q8gvzHnOpMpZTORs0aySVHCox5C+2Svk0fci1qs=
|
charm.land/bubbletea/v2 v2.0.2/go.mod h1:3LRff2U4WIYXy7MTxfbAQ+AdfM3D8Xuvz2wbsOD9OHQ=
|
||||||
charm.land/lipgloss/v2 v2.0.4 h1:lcPeVtcp23SNra7lHy8iYE4UC2aIipVQ47sbGyyxR5Q=
|
charm.land/lipgloss/v2 v2.0.2 h1:xFolbF8JdpNkM2cEPTfXEcW1p6NRzOWTSamRfYEw8cs=
|
||||||
charm.land/lipgloss/v2 v2.0.4/go.mod h1:0653x8epbZSzdDfO/XPS1a/uYPOBeSsCssOpJOqDzik=
|
charm.land/lipgloss/v2 v2.0.2/go.mod h1:KjPle2Qd3YmvP1KL5OMHiHysGcNwq6u83MUjYkFvEkM=
|
||||||
cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE=
|
cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE=
|
||||||
cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU=
|
cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU=
|
||||||
cloud.google.com/go/auth v0.20.0 h1:kXTssoVb4azsVDoUiF8KvxAqrsQcQtB53DcSgta74CA=
|
cloud.google.com/go/auth v0.18.2 h1:+Nbt5Ev0xEqxlNjd6c+yYUeosQ5TtEUaNcN/3FozlaM=
|
||||||
cloud.google.com/go/auth v0.20.0/go.mod h1:942/yi/itH1SsmpyrbnTMDgGfdy2BUqIKyd0cyYLc5Q=
|
cloud.google.com/go/auth v0.18.2/go.mod h1:xD+oY7gcahcu7G2SG2DsBerfFxgPAJz17zz2joOFF3M=
|
||||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
|
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
|
||||||
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
|
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
|
||||||
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
|
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
|
||||||
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
|
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
|
||||||
cloud.google.com/go/iam v1.11.0 h1:KieQ9Pb+LLPak1O3Rv3GgCxhnmkYf7Xyh0P5HfF1jFM=
|
cloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc=
|
||||||
cloud.google.com/go/iam v1.11.0/go.mod h1:KP+nKGugNJW4LcLx1uEZcq1ok5sQHFaQehQNl4QDgV4=
|
cloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU=
|
||||||
cloud.google.com/go/logging v1.18.0 h1:KhzZq+1cSkPH9YUaKLLhLtQxIHitVayBmk0sGfoM9+k=
|
cloud.google.com/go/logging v1.13.1 h1:O7LvmO0kGLaHY/gq8cV7T0dyp6zJhYAOtZPX4TF3QtY=
|
||||||
cloud.google.com/go/logging v1.18.0/go.mod h1:ZGKnpBaURITh+g/uom2VhbiFoFWvejcrHPDhxFtU/gI=
|
cloud.google.com/go/logging v1.13.1/go.mod h1:XAQkfkMBxQRjQek96WLPNze7vsOmay9H5PqfsNYDqvw=
|
||||||
cloud.google.com/go/longrunning v1.1.0 h1:qJ0R0IA8ONaRCNWTRPAS0iAmt1bj3TVgJ40z7ZGRslE=
|
cloud.google.com/go/longrunning v0.8.0 h1:LiKK77J3bx5gDLi4SMViHixjD2ohlkwBi+mKA7EhfW8=
|
||||||
cloud.google.com/go/longrunning v1.1.0/go.mod h1:tH+A/6UvNypiPJWAQaKCsh+xiGbB23wUO8egwUXlD2E=
|
cloud.google.com/go/longrunning v0.8.0/go.mod h1:UmErU2Onzi+fKDg2gR7dusz11Pe26aknR4kHmJJqIfk=
|
||||||
cloud.google.com/go/monitoring v1.29.0 h1:AHhDsFaSax1/4k+qlIDX/SDGe6hggnfXJ9dkgD9qBPY=
|
cloud.google.com/go/monitoring v1.24.3 h1:dde+gMNc0UhPZD1Azu6at2e79bfdztVDS5lvhOdsgaE=
|
||||||
cloud.google.com/go/monitoring v1.29.0/go.mod h1:72NOVjJXHY/HBfoLT0+qlCZBT059+9VXLeAnL2PeeVM=
|
cloud.google.com/go/monitoring v1.24.3/go.mod h1:nYP6W0tm3N9H/bOw8am7t62YTzZY+zUeQ+Bi6+2eonI=
|
||||||
cloud.google.com/go/storage v1.63.0 h1:hvXF2xfg9I32bjujggxgkEZn/Ej6sJ9pieFgeueBLrQ=
|
cloud.google.com/go/storage v1.61.3 h1:VS//ZfBuPGDvakfD9xyPW1RGF1Vy3BWUoVZXgW1KMOg=
|
||||||
cloud.google.com/go/storage v1.63.0/go.mod h1:tirWVptrFNo5GEX2DQ47JooF7yaweJdAJ1hYAVMvKzE=
|
cloud.google.com/go/storage v1.61.3/go.mod h1:JtqK8BBB7TWv0HVGHubtUdzYYrakOQIsMLffZ2Z/HWk=
|
||||||
cloud.google.com/go/trace v1.16.0 h1:GmQovzFc5F0CNfl0VLgL64aoTtu7xsM0YajW2GlG9+E=
|
cloud.google.com/go/trace v1.11.7 h1:kDNDX8JkaAG3R2nq1lIdkb7FCSi1rCmsEtKVsty7p+U=
|
||||||
cloud.google.com/go/trace v1.16.0/go.mod h1:r+bdAn16dKLSV1G2D5v3e58IlQlizfxWrUfjx7kM7X0=
|
cloud.google.com/go/trace v1.11.7/go.mod h1:TNn9d5V3fQVf6s4SCveVMIBS2LJUqo73GACmq/Tky0s=
|
||||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.33.0 h1:l7+6kwRMJNwdCvYdDl7Eax+wzEYHSnNY7zrrfbhDdTA=
|
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 h1:sBEjpZlNHzK1voKq9695PJSX2o5NEXl7/OL3coiIY0c=
|
||||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.33.0/go.mod h1:pJTkW8hEUIIi3Pf65lPZOnn4Y81yCllX6IWk2jNXdkM=
|
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0=
|
||||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.57.0 h1:jLdiS1vO+XJFyDSWRHBx56r4s/NNtcl5J6KyCcWUX/w=
|
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0 h1:UnDZ/zFfG1JhH/DqxIZYU/1CUAlTUScoXD/LcM2Ykk8=
|
||||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.57.0/go.mod h1:8lmpHY+1VRoteiOwyrQMDt1YGXOrFKCz+1wJW7n3ODY=
|
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0/go.mod h1:IA1C1U7jO/ENqm/vhi7V9YYpBsp+IMyqNrEN94N7tVc=
|
||||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.57.0 h1:cSjUzZ7KU8hicTgzaSv9NmSyM9fTVK3y5lsBUl3wOis=
|
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.55.0 h1:7t/qx5Ost0s0wbA/VDrByOooURhp+ikYwv20i9Y07TQ=
|
||||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.57.0/go.mod h1:dzcEjy1WJ0Q4u9twNR3LcLhNoYMRCrMCMafpxa0TjPQ=
|
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.55.0/go.mod h1:vB2GH9GAYYJTO3mEn8oYwzEdhlayZIdQz6zdzgUIRvA=
|
||||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.57.0 h1:RoO5+d7uCmDqovLrHCr2/BuViUXvdcrNxyNM1pN9dDQ=
|
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0 h1:0s6TxfCu2KHkkZPnBfsQ2y5qia0jl3MMrmBhu3nCOYk=
|
||||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.57.0/go.mod h1:YqwkQPrWSC7+byyc1VlKbWLBF5JsW5IoL6xUkemYSXk=
|
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0/go.mod h1:Mf6O40IAyB9zR/1J8nGDDPirZQQPbYJni8Yisy7NTMc=
|
||||||
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.5.0 h1:kQceYJfbupGfZOKZQg0kou0DgAKhzDg2NZPAwZ/2OOE=
|
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
||||||
github.com/Masterminds/semver/v3 v3.5.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||||
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.27.0 h1:FodwmyOBgJULFYmDqibcp9pvfDLWdtPRh9v/r5BXYZs=
|
github.com/alecthomas/chroma/v2 v2.23.1 h1:nv2AVZdTyClGbVQkIzlDm/rnhk1E9bU9nXwmZ/Vk/iY=
|
||||||
github.com/alecthomas/chroma/v2 v2.27.0/go.mod h1:NjJ3ciIgrqBNeIkWZ4e46nseoLDslxU1LmfCoL+wcY8=
|
github.com/alecthomas/chroma/v2 v2.23.1/go.mod h1:NqVhfBR0lte5Ouh3DcthuUCTUpDC9cxBOfyMbMQPs3o=
|
||||||
github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs=
|
github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs=
|
||||||
github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||||
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
||||||
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||||
github.com/aws/aws-sdk-go-v2 v1.42.0 h1:XvXMJTkFQtpBKIWZnmr9ZEOc2InWM2yldjXEJ/bymhA=
|
github.com/aws/aws-sdk-go-v2 v1.41.5 h1:dj5kopbwUsVUVFgO4Fi5BIT3t4WyqIDjGKCangnV/yY=
|
||||||
github.com/aws/aws-sdk-go-v2 v1.42.0/go.mod h1:27+ACypSLljLAEKsCYOmrjKh83vuTRkuAe9Uv/3A4bg=
|
github.com/aws/aws-sdk-go-v2 v1.41.5/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o=
|
||||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.13 h1:p1BBrg/Hhp6uK7zpejeI8QFXHJeC/mynzi04Sl03k9g=
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 h1:eBMB84YGghSocM7PsjmmPffTa+1FBUeNvGvFou6V/4o=
|
||||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.13/go.mod h1:8cIfkE9MDhkRZGpQ22aV6/lkYeYSozpz16Smrs5x4Ls=
|
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8/go.mod h1:lyw7GFp3qENLh7kwzf7iMzAxDn+NzjXEAGjKS2UOKqI=
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.32.26 h1:JI+W5B3jUA8UBz2ggbICGd9UCR6/+SB21G8EFl0SFTQ=
|
github.com/aws/aws-sdk-go-v2/config v1.32.12 h1:O3csC7HUGn2895eNrLytOJQdoL2xyJy0iYXhoZ1OmP0=
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.32.26/go.mod h1:RLE2Ls/wRstvdSz1GPrIWNnXcKZ/znDdWyMuiQxdBoY=
|
github.com/aws/aws-sdk-go-v2/config v1.32.12/go.mod h1:96zTvoOFR4FURjI+/5wY1vc1ABceROO4lWgWJuxgy0g=
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.25 h1:TzPVjfUZ1hsKafvYE+DIzKXIik2KufQxsPHanlkttbo=
|
github.com/aws/aws-sdk-go-v2/credentials v1.19.12 h1:oqtA6v+y5fZg//tcTWahyN9PEn5eDU/Wpvc2+kJ4aY8=
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.25/go.mod h1:K4hw0buguVvtC74HnVfTRr0LzQQHAWPqJbBU9QGk2Pg=
|
github.com/aws/aws-sdk-go-v2/credentials v1.19.12/go.mod h1:U3R1RtSHx6NB0DvEQFGyf/0sbrpJrluENHdPy1j/3TE=
|
||||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.29 h1:r6qZHbT+wxgWO/e9vYNUEtg7lv5+UN3pRqKhLXvnArg=
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 h1:zOgq3uezl5nznfoK3ODuqbhVg1JzAGDUhXOsU0IDCAo=
|
||||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.29/go.mod h1:QRnaRcTVGKPGRy8w78HMQtKUGRYcnMZAANATkeVA6Mo=
|
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20/go.mod h1:z/MVwUARehy6GAg/yQ1GO2IMl0k++cu1ohP9zo887wE=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29 h1:f3vKqSo13fhTYb+JEcXwXefZQE26I1FB5eTSniU67ko=
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 h1:Rgg6wvjjtX8bNHcvi9OnXWwcE0a2vGpbwmtICOsvcf4=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29/go.mod h1:MzoLFUArKGpGD+ukmPiTPG1X5x4o6M2kq4v2dr1FiEc=
|
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21/go.mod h1:A/kJFst/nm//cyqonihbdpQZwiUhhzpqTsdbhDdRF9c=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29 h1:RdwIf/CuUsvJX3RgJagbOyotl/cxoLY4xviKuE7p2GY=
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 h1:PEgGVtPoB6NTpPrBgqSE5hE/o47Ij9qk/SEZFbUOe9A=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29/go.mod h1:71wt8W2EgswdZy9Mf9KNnzxZ3TiZlv4caKghPktDOkA=
|
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21/go.mod h1:p+hz+PRAYlY3zcpJhPwXlLC4C+kqn70WIHwnzAfs6ps=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.30 h1:VTGy885W5DKBxWRUJbym9hytNaYzsyaPkCHGRRMAOhU=
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw=
|
||||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.30/go.mod h1:AS0HycUvJRFvTt613AYDOgO2jzw+00cVSMny8XB3yMY=
|
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.12 h1:ZD2+BSw9vFsNlKYIasSNt3uDbjqqXIBcM13UJv/Lx2k=
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 h1:rWyie/PxDRIdhNf4DzRk0lvjVOqFJuNnO8WwaIRVxzQ=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.12/go.mod h1:Ms4zlcVBbXbiP7EVLhl+lgjvA/a7YphqQ3Ih3174EmI=
|
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22/go.mod h1:zd/JsJ4P7oGfUhXn1VyLqaRZwPmZwg44Jf2dS84Dm3Y=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.22 h1:V51LGlOq/1VsDsHUdoklAQi7rMmx4qQubvFYAlP2254=
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.22/go.mod h1:4Pzhyz8hJOm2bepgl+NjvRx8vlUFAIIvJnZ/MkcNPpU=
|
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.29 h1:DRebniUGZ2MqiiIVmQJ04vIXr918hubdHMnarSLEWyU=
|
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13 h1:JRaIgADQS/U6uXDqlPiefP32yXTda7Kqfx+LgspooZM=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.29/go.mod h1:LfRkPCD8YHDM2E5eTkos2UpwYeZnBcVarTa8L59bJHA=
|
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13/go.mod h1:CEuVn5WqOMilYl+tbccq8+N2ieCy0gVn3OtRb0vBNNM=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.30 h1:4HbXxyipSYxexU0juMIpdS05dilL6dbB2VQHxxN2vGU=
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 h1:c31//R3xgIJMSC8S6hEVq+38DcvUlgFY0FM6mSI5oto=
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.30/go.mod h1:G7RP+uhagpKtKhd1BM9N6JQqjCcGEU47K5lBVZQyRQw=
|
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21/go.mod h1:r6+pf23ouCB718FUxaqzZdbpYFyDtehyZcmP5KL9FkA=
|
||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.104.1 h1:yb03KevaOAG5e8suo79Af74vjIQvoeKmjl79WQchLrs=
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21 h1:ZlvrNcHSFFWURB8avufQq9gFsheUgjVD9536obIknfM=
|
||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.104.1/go.mod h1:mreYODw0Y4yv7xeczvqC6vciwFao8lPE9k1l1ulfY6E=
|
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21/go.mod h1:cv3TNhVrssKR0O/xxLJVRfd2oazSnZnkUeTf6ctUwfQ=
|
||||||
github.com/aws/aws-sdk-go-v2/service/signin v1.2.1 h1:BeJmkm5YOZs6lGRGcNoIuLSoTTtGLLCEqlSiRKYodfM=
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.3 h1:HwxWTbTrIHm5qY+CAEur0s/figc3qwvLWsNkF4RPToo=
|
||||||
github.com/aws/aws-sdk-go-v2/service/signin v1.2.1/go.mod h1:LxYujSTLPRlp2vTtcUO/+1ilrew8ytt6SvQyOgejzFQ=
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.3/go.mod h1:uoA43SdFwacedBfSgfFSjjCvYe8aYBS7EnU5GZ/YKMM=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.31.4 h1:i465b/3c7xJd++pobNIDOggouekCuiWOnB0goQJy+94=
|
github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 h1:0GFOLzEbOyZABS3PhYfBIx2rNBACYcKty+XGkTgw1ow=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.31.4/go.mod h1:Lk7PlmoTYryQmyBG0EXqj5BcUbj3whXdU2s3yGI3EAc=
|
github.com/aws/aws-sdk-go-v2/service/signin v1.0.8/go.mod h1:LXypKvk85AROkKhOG6/YEcHFPoX+prKTowKnVdcaIxE=
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.7 h1:xbmJAnBbyYPkTzoCNCF/bpJ6ymQHRdXX1vquYfDIGYk=
|
github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 h1:kiIDLZ005EcKomYYITtfsjn7dtOwHDOFy7IbPXKek2o=
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.7/go.mod h1:Q5N6icH+KJZDLh+ESNwzdv6cZ6vLFF/egy3IOxWhmz4=
|
github.com/aws/aws-sdk-go-v2/service/sso v1.30.13/go.mod h1:2h/xGEowcW/g38g06g3KpRWDlT+OTfxxI0o1KqayAB8=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.43.4 h1:Np0vmL7op0Zs5xGacYMMX3v5O5pvZ46xhb5LwDgPj8M=
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 h1:jzKAXIlhZhJbnYwHbvUQZEB8KfgAEuG0dc08Bkda7NU=
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.43.4/go.mod h1:r8wkDOuLaaMFqFiYAb8dGY2A3gJCOujMc6CFOVC4Zhc=
|
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17/go.mod h1:Al9fFsXjv4KfbzQHGe6V4NZSZQXecFcvaIF4e70FoRA=
|
||||||
github.com/aws/smithy-go v1.27.3 h1:F3Zb497UhhskkfpJmfkXswyo+t0sh9OTBnIHjogWbVY=
|
github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 h1:Cng+OOwCHmFljXIxpEVXAGMnBia8MSU6Ch5i9PgBkcU=
|
||||||
github.com/aws/smithy-go v1.27.3/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
|
github.com/aws/aws-sdk-go-v2/service/sts v1.41.9/go.mod h1:LrlIndBDdjA/EeXeyNBle+gyCwTlizzW5ycgWnvIxkk=
|
||||||
|
github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng=
|
||||||
|
github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
|
||||||
github.com/aymanbagabas/go-udiff v0.4.1 h1:OEIrQ8maEeDBXQDoGCbbTTXYJMYRCRO1fnodZ12Gv5o=
|
github.com/aymanbagabas/go-udiff v0.4.1 h1:OEIrQ8maEeDBXQDoGCbbTTXYJMYRCRO1fnodZ12Gv5o=
|
||||||
github.com/aymanbagabas/go-udiff v0.4.1/go.mod h1:0L9PGwj20lrtmEMeyw4WKJ/TMyDtvAoK9bf2u/mNo3w=
|
github.com/aymanbagabas/go-udiff v0.4.1/go.mod h1:0L9PGwj20lrtmEMeyw4WKJ/TMyDtvAoK9bf2u/mNo3w=
|
||||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
|
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
|
||||||
@@ -90,12 +92,12 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF
|
|||||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
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/charmbracelet/colorprofile v0.4.3 h1:QPa1IWkYI+AOB+fE+mg/5/4HRMZcaXex9t5KX76i20Q=
|
github.com/charmbracelet/colorprofile v0.4.2 h1:BdSNuMjRbotnxHSfxy+PCSa4xAmz7szw70ktAtWRYrY=
|
||||||
github.com/charmbracelet/colorprofile v0.4.3/go.mod h1:/zT4BhpD5aGFpqQQqw7a+VtHCzu+zrQtt1zhMt9mR4Q=
|
github.com/charmbracelet/colorprofile v0.4.2/go.mod h1:0rTi81QpwDElInthtrQ6Ni7cG0sDtwAd4C4le060fT8=
|
||||||
github.com/charmbracelet/ultraviolet v0.0.0-20260622092850-f39628c8a989 h1:aLA9AmFNKnFr86XM3/Jm9g4xLOVjEgRuttBWUFujdVw=
|
github.com/charmbracelet/ultraviolet v0.0.0-20260205113103-524a6607adb8 h1:eyFRbAmexyt43hVfeyBofiGSEmJ7krjLOYt/9CF5NKA=
|
||||||
github.com/charmbracelet/ultraviolet v0.0.0-20260622092850-f39628c8a989/go.mod h1:f/jRa757WUmaOZrbPspXymbg/GnbF+rwe4OLsG7aXYo=
|
github.com/charmbracelet/ultraviolet v0.0.0-20260205113103-524a6607adb8/go.mod h1:SQpCTRNBtzJkwku5ye4S3HEuthAlGy2n9VXZnWkEW98=
|
||||||
github.com/charmbracelet/x/ansi v0.11.7 h1:kzv1kJvjg2S3r9KHo8hDdHFQLEqn4RBCb39dAYC84jI=
|
github.com/charmbracelet/x/ansi v0.11.6 h1:GhV21SiDz/45W9AnV2R61xZMRri5NlLnl6CVF7ihZW8=
|
||||||
github.com/charmbracelet/x/ansi v0.11.7/go.mod h1:9qGpnAVYz+8ACONkZBUWPtL7lulP9No6p1epAihUZwQ=
|
github.com/charmbracelet/x/ansi v0.11.6/go.mod h1:2JNYLgQUsyqaiLovhU2Rv/pb8r6ydXKS3NIttu3VGZQ=
|
||||||
github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f h1:pk6gmGpCE7F3FcjaOEKYriCvpmIN4+6OS/RD0vm4uIA=
|
github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f h1:pk6gmGpCE7F3FcjaOEKYriCvpmIN4+6OS/RD0vm4uIA=
|
||||||
github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f/go.mod h1:IfZAMTHB6XkZSeXUqriemErjAWCCzT0LwjKFYCZyw0I=
|
github.com/charmbracelet/x/exp/golden v0.0.0-20250806222409-83e3a29d542f/go.mod h1:IfZAMTHB6XkZSeXUqriemErjAWCCzT0LwjKFYCZyw0I=
|
||||||
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
|
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
|
||||||
@@ -108,16 +110,16 @@ github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSE
|
|||||||
github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
|
github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
|
||||||
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
|
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
|
||||||
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
|
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
|
||||||
github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 h1:aBangftG7EVZoUb69Os8IaYg++6uMOdKK83QtkkvJik=
|
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w=
|
||||||
github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2/go.mod h1:qwXFYgsP6T7XnJtbKlf1HP8AjxZZyzxMmc+Lq5GjlU4=
|
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI=
|
||||||
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/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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dlclark/regexp2/v2 v2.2.2 h1:MYWvNYw8okuqNhwTYO587EZMiDruVa2vhV6fsGpfya0=
|
github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
|
||||||
github.com/dlclark/regexp2/v2 v2.2.2/go.mod h1:avUrQvPaLz2DrFNHJF0taWAFFX2C1GMSSoeiqFjcBmU=
|
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||||
github.com/dominikbraun/graph v0.23.0 h1:TdZB4pPqCLFxYhdyMFb1TBdFxp8XLcJfTTBQucVPgCo=
|
github.com/dominikbraun/graph v0.23.0 h1:TdZB4pPqCLFxYhdyMFb1TBdFxp8XLcJfTTBQucVPgCo=
|
||||||
github.com/dominikbraun/graph v0.23.0/go.mod h1:yOjYyogZLY1LSG9E33JWZJiq5k83Qy2C6POAuiViluc=
|
github.com/dominikbraun/graph v0.23.0/go.mod h1:yOjYyogZLY1LSG9E33JWZJiq5k83Qy2C6POAuiViluc=
|
||||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
@@ -126,18 +128,18 @@ github.com/elliotchance/orderedmap/v3 v3.1.0 h1:j4DJ5ObEmMBt/lcwIecKcoRxIQUEnw0L
|
|||||||
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/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA=
|
github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA=
|
||||||
github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU=
|
github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU=
|
||||||
github.com/envoyproxy/go-control-plane/envoy v1.37.0 h1:u3riX6BoYRfF4Dr7dwSOroNfdSbEPe9Yyl09/B6wBrQ=
|
github.com/envoyproxy/go-control-plane/envoy v1.36.0 h1:yg/JjO5E7ubRyKX3m07GF3reDNEnfOboJ0QySbH736g=
|
||||||
github.com/envoyproxy/go-control-plane/envoy v1.37.0/go.mod h1:DReE9MMrmecPy+YvQOAOHNYMALuowAnbjjEMkkWOi6A=
|
github.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98=
|
||||||
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI=
|
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI=
|
||||||
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4=
|
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4=
|
||||||
github.com/envoyproxy/protoc-gen-validate v1.3.3 h1:MVQghNeW+LZcmXe7SY1V36Z+WFMDjpqGAGacLe2T0ds=
|
github.com/envoyproxy/protoc-gen-validate v1.3.0 h1:TvGH1wof4H33rezVKWSpqKz5NXWg5VPuZ0uONDT6eb4=
|
||||||
github.com/envoyproxy/protoc-gen-validate v1.3.3/go.mod h1:TsndJ/ngyIdQRhMcVVGDDHINPLWB7C82oDArY51KfB0=
|
github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA=
|
||||||
github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w=
|
github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w=
|
||||||
github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE=
|
github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE=
|
||||||
github.com/felixge/httpsnoop v1.1.0 h1:3YtUj32ZZkqZtt3sZZsClsymw/QDuVfpNhoA31zeORc=
|
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||||
github.com/felixge/httpsnoop v1.1.0/go.mod h1:Zqxgdd+1Rkcz8euOqdr7lqgCRJztwr5hp9vDSi5UZCE=
|
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho=
|
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||||
github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo=
|
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||||
github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=
|
github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=
|
||||||
github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
||||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
@@ -161,26 +163,26 @@ github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
|
|||||||
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
|
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
|
||||||
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.3.17 h1:73NfMHdiqo9JFU9+7a5ExpVa10/R29pXfZIaW559nrg=
|
github.com/googleapis/enterprise-certificate-proxy v0.3.14 h1:yh8ncqsbUY4shRD5dA6RlzjJaT4hi3kII+zYw8wmLb8=
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.3.17/go.mod h1:rSEsBUemEBZEexP2y6jPp16LUmUbjmSbcPMQizR0o4k=
|
github.com/googleapis/enterprise-certificate-proxy v0.3.14/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg=
|
||||||
github.com/googleapis/gax-go/v2 v2.22.0 h1:PjIWBpgGIVKGoCXuiCoP64altEJCj3/Ei+kSU5vlZD4=
|
github.com/googleapis/gax-go/v2 v2.17.0 h1:RksgfBpxqff0EZkDWYuz9q/uWsTVz+kf43LsZ1J6SMc=
|
||||||
github.com/googleapis/gax-go/v2 v2.22.0/go.mod h1:irWBbALSr0Sk3qlqb9SyJ1h68WjgeFuiOzI4Rqw5+aY=
|
github.com/googleapis/gax-go/v2 v2.17.0/go.mod h1:mzaqghpQp4JDh3HvADwrat+6M3MOIDp5YKHhb9PAgDY=
|
||||||
github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.73 h1:LXhjywNxHsex3qFY2p2iOaHK4nFvdqVp9T9QLdZfpjQ=
|
github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.72 h1:vTCWu1wbdYo7PEZFem/rlr01+Un+wwVmI7wiegFdRLk=
|
||||||
github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.73/go.mod h1:AsbUhwFfdK9ipM8G0i8WVHS0IesKck6M0M9NcuMQTJ8=
|
github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.72/go.mod h1:Vn+BBgKQHVQYdVQ4NZDICE1Brb+JfaONyDHr3q07oQc=
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
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-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||||
github.com/hashicorp/go-getter v1.8.6 h1:9sQboWULaydVphxc4S64oAI4YqpuCk7nPmvbk131ebY=
|
github.com/hashicorp/go-getter v1.8.6 h1:9sQboWULaydVphxc4S64oAI4YqpuCk7nPmvbk131ebY=
|
||||||
github.com/hashicorp/go-getter v1.8.6/go.mod h1:nVH12eOV2P58dIiL3rsU6Fh3wLeJEKBOJzhMmzlSWoo=
|
github.com/hashicorp/go-getter v1.8.6/go.mod h1:nVH12eOV2P58dIiL3rsU6Fh3wLeJEKBOJzhMmzlSWoo=
|
||||||
github.com/hashicorp/go-version v1.9.0 h1:CeOIz6k+LoN3qX9Z0tyQrPtiB1DFYRPfCIBtaXPSCnA=
|
github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4=
|
||||||
github.com/hashicorp/go-version v1.9.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
github.com/hashicorp/go-version v1.8.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/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/klauspost/compress v1.18.7 h1:aUyZsS4kH3QTKurYhAOwAHxllVPnOthb3vPfnF1Ehjw=
|
github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE=
|
||||||
github.com/klauspost/compress v1.18.7/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||||
github.com/klauspost/cpuid/v2 v2.4.0 h1:S6Hrbc7+ywsr0r+RLapfGBHfyefhCTwEh3A0tV913Dw=
|
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
|
||||||
github.com/klauspost/cpuid/v2 v2.4.0/go.mod h1:19jmZ9mjzoF//ddRSUsv0zfBTJWh3QJh9FNxZTMrGxU=
|
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||||
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
|
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
|
||||||
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
@@ -190,30 +192,30 @@ 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/lucasb-eyer/go-colorful v1.4.0 h1:UtrWVfLdarDgc44HcS7pYloGHJUjHV/4FwW4TvVgFr4=
|
github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
|
||||||
github.com/lucasb-eyer/go-colorful v1.4.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||||
github.com/mattn/go-colorable v0.1.15 h1:+u9SLTRGnXv73cEsnsmoZBom+dMU88B2M0aDcWy0/jY=
|
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||||
github.com/mattn/go-colorable v0.1.15/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||||
github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-runewidth v0.0.24 h1:cpokDiIn0MGnhdHwuWnJBITySJ20QyNGnY2kR/ay2DU=
|
github.com/mattn/go-runewidth v0.0.21 h1:jJKAZiQH+2mIinzCJIaIG9Be1+0NR+5sz/lYEEjdM8w=
|
||||||
github.com/mattn/go-runewidth v0.0.24/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
github.com/mattn/go-runewidth v0.0.21/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
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/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/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
||||||
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||||
github.com/pierrec/lz4/v4 v4.1.27 h1:+PhzhWDrjRj89TH2sw43nE3+4+W8lSxIuQadEHZyjUk=
|
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
|
||||||
github.com/pierrec/lz4/v4 v4.1.27/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4=
|
github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||||
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/planetscale/vtprotobuf v0.6.1-0.20250313105119-ba97887b0a25 h1:S1hI5JiKP7883xBzZAr1ydcxrKNSVNm7+3+JwjxZEsg=
|
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
|
||||||
github.com/planetscale/vtprotobuf v0.6.1-0.20250313105119-ba97887b0a25/go.mod h1:ZQntvDG8TkPgljxtA0R9frDoND4QORU1VXz015N5Ks4=
|
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
|
||||||
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/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/puzpuzpuz/xsync/v4 v4.5.0 h1:vOSWu6b57/emh+L/Cw0BeQfvxa/cogFywXHeGUxQxAg=
|
github.com/puzpuzpuz/xsync/v4 v4.4.0 h1:vlSN6/CkEY0pY8KaB0yqo/pCLZvp9nhdbBdjipT4gWo=
|
||||||
github.com/puzpuzpuz/xsync/v4 v4.5.0/go.mod h1:VJDmTCJMBt8igNxnkQd86r+8KUeN1quSfNKu5bLYFQo=
|
github.com/puzpuzpuz/xsync/v4 v4.4.0/go.mod h1:VJDmTCJMBt8igNxnkQd86r+8KUeN1quSfNKu5bLYFQo=
|
||||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||||
@@ -223,21 +225,21 @@ github.com/sajari/fuzzy v1.0.0/go.mod h1:OjYR6KxoWOe9+dOlXeiCJd4dIbED4Oo8wpS89o0
|
|||||||
github.com/sebdah/goldie/v2 v2.8.0 h1:dZb9wR8q5++oplmEiJT+U/5KyotVD+HNGCAc5gNr8rc=
|
github.com/sebdah/goldie/v2 v2.8.0 h1:dZb9wR8q5++oplmEiJT+U/5KyotVD+HNGCAc5gNr8rc=
|
||||||
github.com/sebdah/goldie/v2 v2.8.0/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.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
|
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
||||||
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||||
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/spiffe/go-spiffe/v2 v2.8.1 h1:eXZMLsu+3MLEPJyGJkolqtVrteZfQdUpOWj6LTiDl/E=
|
github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo=
|
||||||
github.com/spiffe/go-spiffe/v2 v2.8.1/go.mod h1:47Q0Q9/AqGha8QLHp+kxpH4Wca7X7EnOtlIJy3mxZ3U=
|
github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4=
|
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||||
github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0=
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
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.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.16.0 h1:wY40O83MBVks97+Is0WlFlOPSwKQMIrWP9R1IsrExg8=
|
github.com/u-root/u-root v0.15.1-0.20251208185023-2f8c7e763cf8 h1:cq+DjLAjz3ZPwh0+G571O/jMH0c0DzReDPLjQGL2/BA=
|
||||||
github.com/u-root/u-root v0.16.0/go.mod h1:yL/XdSSW27PdGLgUh4MNRBy54mKM+TBLzpwiB4nwj90=
|
github.com/u-root/u-root v0.15.1-0.20251208185023-2f8c7e763cf8/go.mod h1:JNauIV2zopCBv/6o+umxcT3bKe8YUqYJaTZQYSYpKss=
|
||||||
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/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY=
|
github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY=
|
||||||
@@ -250,58 +252,61 @@ github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs=
|
|||||||
github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s=
|
github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s=
|
||||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||||
go.opentelemetry.io/contrib/detectors/gcp v1.44.0 h1:NmLfL734pJhM0JKaYd2Y28+nY9dPRWYAAbxhRCrKXPw=
|
go.opentelemetry.io/contrib/detectors/gcp v1.39.0 h1:kWRNZMsfBHZ+uHjiH4y7Etn2FK26LAGkNFw7RHv1DhE=
|
||||||
go.opentelemetry.io/contrib/detectors/gcp v1.44.0/go.mod h1:tNAsgd8avTGke1+MndXlU5Cru4PQ9Ai/cCNWQv/ZJ/s=
|
go.opentelemetry.io/contrib/detectors/gcp v1.39.0/go.mod h1:t/OGqzHBa5v6RHZwrDBJ2OirWc+4q/w2fTbLZwAKjTk=
|
||||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.69.0 h1:2yEATaop1/a1I4psnSLgWVPLWwCzkqWakgJy7xTDVy0=
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo=
|
||||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.69.0/go.mod h1:D7J12YRapIekYyPWgGPlA/23pRmpSEZC5xJC/TTLI9U=
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ=
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 h1:8tvICD4vSTOOsNrsI4Ljf6C+6UKvpTEH5XY3JMoyPoo=
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0/go.mod h1:z9+yiacE0IHRqM4qFfkbt/JYlmYXgss8GY/jXoNuPJI=
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
|
||||||
go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU=
|
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
|
||||||
go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc=
|
go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
|
||||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.44.0 h1:hqxVTu/GtBF+vJ8d1fzW7fRxZFvgoDjWcxwwCaFDYpU=
|
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.40.0 h1:ZrPRak/kS4xI3AVXy8F7pipuDXmDsrO8Lg+yQjBLjw0=
|
||||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.44.0/go.mod h1:z5fVEF4X5v0ESvlJqBrrFlBVoj5EQuefZpzsu7R+x5Q=
|
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.40.0/go.mod h1:3y6kQCWztq6hyW8Z9YxQDDm0Je9AJoFar2G0yDcmhRk=
|
||||||
go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc=
|
go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
|
||||||
go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo=
|
go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
|
||||||
go.opentelemetry.io/otel/metric/x v0.66.0 h1:YkCrx1zLOChi9ZcZ6euupOcsgzbVlec7D/xoEU1+cTA=
|
go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
|
||||||
go.opentelemetry.io/otel/metric/x v0.66.0/go.mod h1:d1+BDj9t96do0/1LoU1ayfCv79ZgNE41qbhBvnMOBZk=
|
go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
|
||||||
go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58=
|
go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
|
||||||
go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0=
|
go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI=
|
go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA=
|
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
|
||||||
go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk=
|
|
||||||
go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE=
|
|
||||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||||
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
|
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
|
||||||
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
|
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
|
||||||
golang.org/x/exp v0.0.0-20260611194520-c48552f49976 h1:X8Hz2ImujgbmetVuW+w2YkyZChE3cBpZi2P158rTG9M=
|
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
|
||||||
golang.org/x/exp v0.0.0-20260611194520-c48552f49976/go.mod h1:vnf4pv9iKZXY58sQE1L86zmNWJ4159e1RkcWiLCkeEY=
|
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
|
||||||
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
|
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
||||||
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
|
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
||||||
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
|
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
|
||||||
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
|
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
|
||||||
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
|
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||||
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||||
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||||
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
|
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||||
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
|
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
|
||||||
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
|
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||||
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
|
golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU=
|
||||||
|
golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A=
|
||||||
|
golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY=
|
||||||
|
golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY=
|
||||||
|
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
|
||||||
|
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
|
||||||
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
|
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
|
||||||
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
|
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
|
||||||
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||||
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||||
google.golang.org/api v0.287.0 h1:CQDMqUiqZZ0U/Yge3zyjAhNQ0OSYEH0PaA7l4xtEen4=
|
google.golang.org/api v0.271.0 h1:cIPN4qcUc61jlh7oXu6pwOQqbJW2GqYh5PS6rB2C/JY=
|
||||||
google.golang.org/api v0.287.0/go.mod h1:pPW85yt3Iuc3unkpaMhFtMmOqnTdCwCqEOaUlnuxRlQ=
|
google.golang.org/api v0.271.0/go.mod h1:CGT29bhwkbF+i11qkRUJb2KMKqcJ1hdFceEIRd9u64Q=
|
||||||
google.golang.org/genproto v0.0.0-20260630182238-925bb5da69e7 h1:lQG76ePMKmtujel4VIVMiFoHVWVNtJdawbCZJtWlVXU=
|
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 h1:VQZ/yAbAtjkHgH80teYd2em3xtIkkHd7ZhqfH2N9CsM=
|
||||||
google.golang.org/genproto v0.0.0-20260630182238-925bb5da69e7/go.mod h1:LwlOWYBU335L+sR55UuR5fbbU8KmEX+3tUHf3SwMmhM=
|
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409/go.mod h1:rxKD3IEILWEu3P44seeNOAwZN4SaoKaQ/2eTg4mM6EM=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20260630182238-925bb5da69e7 h1:jQ9p21COKWjP3VwuFrNRiiOTMh3mPpN45R7SLrH/HUU=
|
google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 h1:7ei4lp52gK1uSejlA8AZl5AJjeLUOHBQscRQZUgAcu0=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20260630182238-925bb5da69e7/go.mod h1:KqHwBx2upmfa1XSi1WuRvC+2VGCLtooKkfmyvRbUmqA=
|
google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20/go.mod h1:ZdbssH/1SOVnjnDlXzxDHK2MCidiqXtbYccJNzNYPEE=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260630182238-925bb5da69e7 h1:eM/YSd5bBFagF51o1E745Ta7RwzpW0h+z+QDNZOgmQ8=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260630182238-925bb5da69e7/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||||
google.golang.org/grpc v1.82.0 h1:vguDnZUPjE26w09A63VoxZPnvPjB5Riyc0mkXPFmAIU=
|
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
|
||||||
google.golang.org/grpc v1.82.0/go.mod h1:yzTZ1TB1Z3SG+LIYaI+WiE8D5+PZ3ArnrSp8zF3+/ZA=
|
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||||
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=
|
||||||
@@ -314,5 +319,5 @@ 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-20260120230322-19def062a997 h1:3bbJwtPFh98dJ6lxRdR3eLHTH1CmR3BcU6TriIMiXjE=
|
mvdan.cc/sh/moreinterp v0.0.0-20260120230322-19def062a997 h1:3bbJwtPFh98dJ6lxRdR3eLHTH1CmR3BcU6TriIMiXjE=
|
||||||
mvdan.cc/sh/moreinterp v0.0.0-20260120230322-19def062a997/go.mod h1:Qy/zdaMDxq9sT72Gi43K3gsV+TtTohyDO3f1cyBVwuo=
|
mvdan.cc/sh/moreinterp v0.0.0-20260120230322-19def062a997/go.mod h1:Qy/zdaMDxq9sT72Gi43K3gsV+TtTohyDO3f1cyBVwuo=
|
||||||
mvdan.cc/sh/v3 v3.13.2-0.20260613075524-2255122b577b h1:NREoadYF42Gu7127VIccx/SRia+Bz8wpKBaqmXKiGXE=
|
mvdan.cc/sh/v3 v3.13.1 h1:DP3TfgZhDkT7lerUdnp6PTGKyxxzz6T+cOlY/xEvfWk=
|
||||||
mvdan.cc/sh/v3 v3.13.2-0.20260613075524-2255122b577b/go.mod h1:lXJ8SexMvEVcHCoDvAGLZgFJ9Wsm2sulmoNEXGhYZD0=
|
mvdan.cc/sh/v3 v3.13.1/go.mod h1:lXJ8SexMvEVcHCoDvAGLZgFJ9Wsm2sulmoNEXGhYZD0=
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ func TraverseStringsFunc[T any](v T, fn func(v string) (string, error)) (T, erro
|
|||||||
traverseFunc = func(copy, v reflect.Value) error {
|
traverseFunc = func(copy, v reflect.Value) error {
|
||||||
switch v.Kind() {
|
switch v.Kind() {
|
||||||
|
|
||||||
case reflect.Pointer:
|
case reflect.Ptr:
|
||||||
// Unwrap the pointer
|
// Unwrap the pointer
|
||||||
originalValue := v.Elem()
|
originalValue := v.Elem()
|
||||||
// If the pointer is nil, do nothing
|
// If the pointer is nil, do nothing
|
||||||
|
|||||||
@@ -127,10 +127,7 @@ func ExpandFields(s string) ([]string, error) {
|
|||||||
s = escape(s)
|
s = escape(s)
|
||||||
p := syntax.NewParser()
|
p := syntax.NewParser()
|
||||||
var words []*syntax.Word
|
var words []*syntax.Word
|
||||||
for w, err := range p.WordsSeq(strings.NewReader(s)) {
|
for w := range p.WordsSeq(strings.NewReader(s)) {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
words = append(words, w)
|
words = append(words, w)
|
||||||
}
|
}
|
||||||
cfg := &expand.Config{
|
cfg := &expand.Config{
|
||||||
|
|||||||
@@ -1,211 +0,0 @@
|
|||||||
package fingerprint
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/go-task/task/v3/internal/gitignore"
|
|
||||||
)
|
|
||||||
|
|
||||||
type linesCacheEntry struct {
|
|
||||||
mtime time.Time
|
|
||||||
lines []string
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
gitignoreLinesCache sync.Map // dir -> linesCacheEntry, invalidated by mtime
|
|
||||||
repoRootCache sync.Map // dir -> repo root (or "" when not in a repo)
|
|
||||||
)
|
|
||||||
|
|
||||||
// findRepoRoot returns the first ancestor of dir containing a .git entry, or
|
|
||||||
// ("", false) when dir is not inside a git repository.
|
|
||||||
func findRepoRoot(dir string) (string, bool) {
|
|
||||||
if v, ok := repoRootCache.Load(dir); ok {
|
|
||||||
root := v.(string)
|
|
||||||
return root, root != ""
|
|
||||||
}
|
|
||||||
|
|
||||||
current := dir
|
|
||||||
for {
|
|
||||||
if _, err := os.Stat(filepath.Join(current, ".git")); err == nil {
|
|
||||||
repoRootCache.Store(dir, current)
|
|
||||||
return current, true
|
|
||||||
}
|
|
||||||
parent := filepath.Dir(current)
|
|
||||||
if parent == current {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
current = parent
|
|
||||||
}
|
|
||||||
|
|
||||||
repoRootCache.Store(dir, "")
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
// filterGitignored marks entries matching gitignore rules as excluded (false).
|
|
||||||
// All .gitignore files from the repo root down to each candidate file's
|
|
||||||
// directory feed a single matcher so that precedence and cross-file negations
|
|
||||||
// (`!pattern`) resolve correctly.
|
|
||||||
func filterGitignored(files map[string]bool, dir string) map[string]bool {
|
|
||||||
if len(files) == 0 {
|
|
||||||
return files
|
|
||||||
}
|
|
||||||
|
|
||||||
absDir, err := filepath.Abs(dir)
|
|
||||||
if err != nil {
|
|
||||||
return files
|
|
||||||
}
|
|
||||||
repoRoot, ok := findRepoRoot(absDir)
|
|
||||||
if !ok {
|
|
||||||
return files
|
|
||||||
}
|
|
||||||
|
|
||||||
// Every directory from the repo root down to each candidate file's dir, so
|
|
||||||
// nested .gitignore files reached by deep globs are included too.
|
|
||||||
dirSet := make(map[string]struct{})
|
|
||||||
for path, included := range files {
|
|
||||||
if !included {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
absPath, err := filepath.Abs(path)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
d := filepath.Dir(absPath)
|
|
||||||
if !withinRepo(repoRoot, d) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
dirSet[d] = struct{}{}
|
|
||||||
if d == repoRoot {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
parent := filepath.Dir(d)
|
|
||||||
if parent == d {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
d = parent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shallow dirs first (lower priority): the matcher scans patterns last to
|
|
||||||
// first, so deeper rules win and can negate shallower ones.
|
|
||||||
dirs := make([]string, 0, len(dirSet))
|
|
||||||
for d := range dirSet {
|
|
||||||
dirs = append(dirs, d)
|
|
||||||
}
|
|
||||||
sort.Slice(dirs, func(i, j int) bool {
|
|
||||||
di := strings.Count(dirs[i], string(filepath.Separator))
|
|
||||||
dj := strings.Count(dirs[j], string(filepath.Separator))
|
|
||||||
if di != dj {
|
|
||||||
return di < dj
|
|
||||||
}
|
|
||||||
return dirs[i] < dirs[j]
|
|
||||||
})
|
|
||||||
|
|
||||||
var patterns []gitignore.Pattern
|
|
||||||
for _, d := range dirs {
|
|
||||||
lines := readGitignoreLines(d)
|
|
||||||
if len(lines) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// domain scopes each pattern to its .gitignore subtree (go-git semantics).
|
|
||||||
var domain []string
|
|
||||||
if rel, err := filepath.Rel(repoRoot, d); err == nil && rel != "." {
|
|
||||||
domain = strings.Split(filepath.ToSlash(rel), "/")
|
|
||||||
}
|
|
||||||
for _, line := range lines {
|
|
||||||
patterns = append(patterns, gitignore.ParsePattern(line, domain))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(patterns) == 0 {
|
|
||||||
return files
|
|
||||||
}
|
|
||||||
|
|
||||||
matcher := gitignore.NewMatcher(patterns)
|
|
||||||
for path, included := range files {
|
|
||||||
if !included {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
absPath, err := filepath.Abs(path)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
relPath, err := filepath.Rel(repoRoot, absPath)
|
|
||||||
if err != nil || relPath == ".." || strings.HasPrefix(relPath, ".."+string(filepath.Separator)) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if ignored(matcher, strings.Split(filepath.ToSlash(relPath), "/")) {
|
|
||||||
files[path] = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return files
|
|
||||||
}
|
|
||||||
|
|
||||||
// ignored honors Git's rule that a file under an ignored directory cannot be
|
|
||||||
// re-included by a deeper negation: if any ancestor directory is ignored, so is
|
|
||||||
// the file. Otherwise the file's own verdict applies (isDir=false).
|
|
||||||
func ignored(matcher gitignore.Matcher, segments []string) bool {
|
|
||||||
for i := 1; i < len(segments); i++ {
|
|
||||||
if matcher.Match(segments[:i], true) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return matcher.Match(segments, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func withinRepo(repoRoot, p string) bool {
|
|
||||||
rel, err := filepath.Rel(repoRoot, p)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return rel == "." || (rel != ".." && !strings.HasPrefix(rel, ".."+string(filepath.Separator)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// readGitignoreLines returns the .gitignore lines in dir (nil if none), cached
|
|
||||||
// per directory and invalidated by mtime so watch mode picks up edits.
|
|
||||||
func readGitignoreLines(dir string) []string {
|
|
||||||
path := filepath.Join(dir, ".gitignore")
|
|
||||||
info, err := os.Stat(path)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
mtime := info.ModTime()
|
|
||||||
|
|
||||||
if v, ok := gitignoreLinesCache.Load(dir); ok {
|
|
||||||
entry := v.(linesCacheEntry)
|
|
||||||
if entry.mtime.Equal(mtime) {
|
|
||||||
return entry.lines
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lines := parseGitignoreLines(path)
|
|
||||||
gitignoreLinesCache.Store(dir, linesCacheEntry{mtime: mtime, lines: lines})
|
|
||||||
return lines
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseGitignoreLines(path string) []string {
|
|
||||||
f, err := os.Open(path)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
var lines []string
|
|
||||||
scanner := bufio.NewScanner(f)
|
|
||||||
for scanner.Scan() {
|
|
||||||
line := strings.TrimRight(scanner.Text(), "\r")
|
|
||||||
if line != "" && !strings.HasPrefix(line, "#") {
|
|
||||||
lines = append(lines, line)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// On a scan error (e.g. an over-long line) keep what was parsed rather than
|
|
||||||
// dropping the whole file.
|
|
||||||
return lines
|
|
||||||
}
|
|
||||||
@@ -1,254 +0,0 @@
|
|||||||
package fingerprint
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
"github.com/go-task/task/v3/taskfile/ast"
|
|
||||||
)
|
|
||||||
|
|
||||||
func initGitRepo(t *testing.T, dir string) {
|
|
||||||
t.Helper()
|
|
||||||
require.NoError(t, os.MkdirAll(filepath.Join(dir, ".git"), 0o755))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGlobsWithGitignore(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
dir := t.TempDir()
|
|
||||||
initGitRepo(t, dir)
|
|
||||||
|
|
||||||
require.NoError(t, os.WriteFile(filepath.Join(dir, "included.txt"), []byte("included"), 0o644))
|
|
||||||
require.NoError(t, os.WriteFile(filepath.Join(dir, "ignored.log"), []byte("ignored"), 0o644))
|
|
||||||
require.NoError(t, os.WriteFile(filepath.Join(dir, "also-included.txt"), []byte("also included"), 0o644))
|
|
||||||
require.NoError(t, os.WriteFile(filepath.Join(dir, ".gitignore"), []byte("*.log\n"), 0o644))
|
|
||||||
|
|
||||||
globs := []*ast.Glob{
|
|
||||||
{Glob: "./*"},
|
|
||||||
}
|
|
||||||
|
|
||||||
filesWithout, err := Globs(dir, globs, false)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
filesWith, err := Globs(dir, globs, true)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
hasLog := false
|
|
||||||
for _, f := range filesWithout {
|
|
||||||
if filepath.Base(f) == "ignored.log" {
|
|
||||||
hasLog = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert.True(t, hasLog, "ignored.log should be present without gitignore filter")
|
|
||||||
|
|
||||||
hasLog = false
|
|
||||||
for _, f := range filesWith {
|
|
||||||
if filepath.Base(f) == "ignored.log" {
|
|
||||||
hasLog = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert.False(t, hasLog, "ignored.log should be excluded with gitignore filter")
|
|
||||||
|
|
||||||
txtCount := 0
|
|
||||||
for _, f := range filesWith {
|
|
||||||
if filepath.Ext(f) == ".txt" {
|
|
||||||
txtCount++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert.Equal(t, 2, txtCount, "both .txt files should remain")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGlobsWithGitignoreParentDirIgnored(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
dir := t.TempDir()
|
|
||||||
initGitRepo(t, dir)
|
|
||||||
|
|
||||||
buildDir := filepath.Join(dir, "build")
|
|
||||||
require.NoError(t, os.MkdirAll(buildDir, 0o755))
|
|
||||||
require.NoError(t, os.WriteFile(filepath.Join(buildDir, "keep.txt"), []byte("keep"), 0o644))
|
|
||||||
require.NoError(t, os.WriteFile(filepath.Join(buildDir, "other.txt"), []byte("other"), 0o644))
|
|
||||||
|
|
||||||
// Git cannot re-include a file under an ignored directory.
|
|
||||||
require.NoError(t, os.WriteFile(filepath.Join(dir, ".gitignore"), []byte("build/\n"), 0o644))
|
|
||||||
require.NoError(t, os.WriteFile(filepath.Join(buildDir, ".gitignore"), []byte("!keep.txt\n"), 0o644))
|
|
||||||
|
|
||||||
globs := []*ast.Glob{
|
|
||||||
{Glob: "./**/*"},
|
|
||||||
}
|
|
||||||
|
|
||||||
files, err := Globs(dir, globs, true)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
for _, f := range files {
|
|
||||||
base := filepath.Base(f)
|
|
||||||
assert.NotEqual(t, "keep.txt", base, "keep.txt must stay excluded under ignored build/")
|
|
||||||
assert.NotEqual(t, "other.txt", base, "other.txt must stay excluded under ignored build/")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGlobsWithGitignoreNested(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
dir := t.TempDir()
|
|
||||||
initGitRepo(t, dir)
|
|
||||||
|
|
||||||
subDir := filepath.Join(dir, "sub")
|
|
||||||
require.NoError(t, os.MkdirAll(subDir, 0o755))
|
|
||||||
|
|
||||||
require.NoError(t, os.WriteFile(filepath.Join(subDir, "keep.txt"), []byte("keep"), 0o644))
|
|
||||||
require.NoError(t, os.WriteFile(filepath.Join(subDir, "build.out"), []byte("build"), 0o644))
|
|
||||||
|
|
||||||
require.NoError(t, os.WriteFile(filepath.Join(dir, ".gitignore"), []byte("*.log\n"), 0o644))
|
|
||||||
require.NoError(t, os.WriteFile(filepath.Join(subDir, ".gitignore"), []byte("*.out\n"), 0o644))
|
|
||||||
|
|
||||||
globs := []*ast.Glob{
|
|
||||||
{Glob: "./*"},
|
|
||||||
}
|
|
||||||
|
|
||||||
files, err := Globs(subDir, globs, true)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
for _, f := range files {
|
|
||||||
assert.NotEqual(t, "build.out", filepath.Base(f), "build.out should be excluded by nested .gitignore")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGlobsWithGitignoreCrossFileNegation(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
dir := t.TempDir()
|
|
||||||
initGitRepo(t, dir)
|
|
||||||
|
|
||||||
subDir := filepath.Join(dir, "sub")
|
|
||||||
require.NoError(t, os.MkdirAll(subDir, 0o755))
|
|
||||||
|
|
||||||
require.NoError(t, os.WriteFile(filepath.Join(subDir, "debug.log"), []byte("debug"), 0o644))
|
|
||||||
require.NoError(t, os.WriteFile(filepath.Join(subDir, "other.log"), []byte("other"), 0o644))
|
|
||||||
|
|
||||||
// Root ignores all *.log; a nested .gitignore re-includes debug.log.
|
|
||||||
require.NoError(t, os.WriteFile(filepath.Join(dir, ".gitignore"), []byte("*.log\n"), 0o644))
|
|
||||||
require.NoError(t, os.WriteFile(filepath.Join(subDir, ".gitignore"), []byte("!debug.log\n"), 0o644))
|
|
||||||
|
|
||||||
globs := []*ast.Glob{
|
|
||||||
{Glob: "./*"},
|
|
||||||
}
|
|
||||||
|
|
||||||
files, err := Globs(subDir, globs, true)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
hasDebug, hasOther := false, false
|
|
||||||
for _, f := range files {
|
|
||||||
switch filepath.Base(f) {
|
|
||||||
case "debug.log":
|
|
||||||
hasDebug = true
|
|
||||||
case "other.log":
|
|
||||||
hasOther = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert.True(t, hasDebug, "debug.log should be re-included by the nested negation")
|
|
||||||
assert.False(t, hasOther, "other.log should remain excluded by the root *.log rule")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGlobsWithGitignoreDeepGlob(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
dir := t.TempDir()
|
|
||||||
initGitRepo(t, dir)
|
|
||||||
|
|
||||||
subDir := filepath.Join(dir, "sub")
|
|
||||||
require.NoError(t, os.MkdirAll(subDir, 0o755))
|
|
||||||
|
|
||||||
require.NoError(t, os.WriteFile(filepath.Join(subDir, "keep.txt"), []byte("keep"), 0o644))
|
|
||||||
require.NoError(t, os.WriteFile(filepath.Join(subDir, "gen.out"), []byte("gen"), 0o644))
|
|
||||||
|
|
||||||
require.NoError(t, os.WriteFile(filepath.Join(subDir, ".gitignore"), []byte("*.out\n"), 0o644))
|
|
||||||
|
|
||||||
globs := []*ast.Glob{
|
|
||||||
{Glob: "./**/*"},
|
|
||||||
}
|
|
||||||
|
|
||||||
files, err := Globs(dir, globs, true)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
for _, f := range files {
|
|
||||||
assert.NotEqual(t, "gen.out", filepath.Base(f), "gen.out should be excluded by the nested .gitignore reached via deep glob")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGlobsWithGitignoreDoubleDotFile(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
dir := t.TempDir()
|
|
||||||
initGitRepo(t, dir)
|
|
||||||
|
|
||||||
// A ".."-prefixed name must not be skipped by the out-of-tree guard.
|
|
||||||
require.NoError(t, os.WriteFile(filepath.Join(dir, "..keep.log"), []byte("x"), 0o644))
|
|
||||||
require.NoError(t, os.WriteFile(filepath.Join(dir, "keep.txt"), []byte("y"), 0o644))
|
|
||||||
require.NoError(t, os.WriteFile(filepath.Join(dir, ".gitignore"), []byte("*.log\n"), 0o644))
|
|
||||||
|
|
||||||
globs := []*ast.Glob{
|
|
||||||
{Glob: "./*"},
|
|
||||||
}
|
|
||||||
|
|
||||||
files, err := Globs(dir, globs, true)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
for _, f := range files {
|
|
||||||
assert.NotEqual(t, "..keep.log", filepath.Base(f), "..keep.log should be excluded by *.log")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGlobsWithGitignoreLongLine(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
dir := t.TempDir()
|
|
||||||
initGitRepo(t, dir)
|
|
||||||
|
|
||||||
require.NoError(t, os.WriteFile(filepath.Join(dir, "ignored.log"), []byte("x"), 0o644))
|
|
||||||
require.NoError(t, os.WriteFile(filepath.Join(dir, "keep.txt"), []byte("y"), 0o644))
|
|
||||||
|
|
||||||
// A line over bufio.Scanner's 64KB limit triggers a scan error; patterns
|
|
||||||
// parsed before it must survive.
|
|
||||||
longLine := strings.Repeat("a", 70*1024)
|
|
||||||
content := "*.log\n" + longLine + "\n"
|
|
||||||
require.NoError(t, os.WriteFile(filepath.Join(dir, ".gitignore"), []byte(content), 0o644))
|
|
||||||
|
|
||||||
globs := []*ast.Glob{
|
|
||||||
{Glob: "./*"},
|
|
||||||
}
|
|
||||||
|
|
||||||
files, err := Globs(dir, globs, true)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
for _, f := range files {
|
|
||||||
assert.NotEqual(t, "ignored.log", filepath.Base(f), "*.log parsed before the long line should still apply")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGlobsWithGitignoreNoRepo(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
// Cannot use t.TempDir() here because it creates a dir inside the
|
|
||||||
// go-task repo which has a .git parent, defeating the "no repo" test.
|
|
||||||
dir, err := os.MkdirTemp("", "task-gitignore-norepo-*") //nolint:usetesting
|
|
||||||
require.NoError(t, err)
|
|
||||||
t.Cleanup(func() { os.RemoveAll(dir) })
|
|
||||||
|
|
||||||
require.NoError(t, os.WriteFile(filepath.Join(dir, "file.txt"), []byte("content"), 0o644))
|
|
||||||
|
|
||||||
globs := []*ast.Glob{
|
|
||||||
{Glob: "./*"},
|
|
||||||
}
|
|
||||||
|
|
||||||
files, err := Globs(dir, globs, true)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Len(t, files, 1)
|
|
||||||
}
|
|
||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
"github.com/go-task/task/v3/taskfile/ast"
|
"github.com/go-task/task/v3/taskfile/ast"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Globs(dir string, globs []*ast.Glob, useGitignore bool) ([]string, error) {
|
func Globs(dir string, globs []*ast.Glob) ([]string, error) {
|
||||||
resultMap := make(map[string]bool)
|
resultMap := make(map[string]bool)
|
||||||
for _, g := range globs {
|
for _, g := range globs {
|
||||||
matches, err := glob(dir, g.Glob)
|
matches, err := glob(dir, g.Glob)
|
||||||
@@ -21,11 +21,6 @@ func Globs(dir string, globs []*ast.Glob, useGitignore bool) ([]string, error) {
|
|||||||
resultMap[match] = !g.Negate
|
resultMap[match] = !g.Negate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if useGitignore {
|
|
||||||
resultMap = filterGitignored(resultMap, dir)
|
|
||||||
}
|
|
||||||
|
|
||||||
return collectKeys(resultMap), nil
|
return collectKeys(resultMap), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ func (*ChecksumChecker) Kind() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *ChecksumChecker) checksum(t *ast.Task) (string, error) {
|
func (c *ChecksumChecker) checksum(t *ast.Task) (string, error) {
|
||||||
sources, err := Globs(t.Dir, t.Sources, t.ShouldUseGitignore())
|
sources, err := Globs(t.Dir, t.Sources)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -119,7 +119,7 @@ func (checker *ChecksumChecker) checksumFilePath(t *ast.Task) string {
|
|||||||
return filepath.Join(checker.tempDir, "checksum", normalizeFilename(t.Name()))
|
return filepath.Join(checker.tempDir, "checksum", normalizeFilename(t.Name()))
|
||||||
}
|
}
|
||||||
|
|
||||||
var checksumFilenameRegexp = regexp.MustCompile("[^[:alnum:]]")
|
var checksumFilenameRegexp = regexp.MustCompile("[^A-z0-9]")
|
||||||
|
|
||||||
// replaces invalid characters on filenames with "-"
|
// replaces invalid characters on filenames with "-"
|
||||||
func normalizeFilename(f string) string {
|
func normalizeFilename(f string) string {
|
||||||
|
|||||||
@@ -16,10 +16,6 @@ func TestNormalizeFilename(t *testing.T) {
|
|||||||
{"foo/bar/baz", "foo-bar-baz"},
|
{"foo/bar/baz", "foo-bar-baz"},
|
||||||
{"foo@bar/baz", "foo-bar-baz"},
|
{"foo@bar/baz", "foo-bar-baz"},
|
||||||
{"foo1bar2baz3", "foo1bar2baz3"},
|
{"foo1bar2baz3", "foo1bar2baz3"},
|
||||||
{"foo\\bar", "foo-bar"},
|
|
||||||
{"foo_bar", "foo-bar"},
|
|
||||||
{"foo[bar]baz", "foo-bar-baz"},
|
|
||||||
{"foo^bar`baz", "foo-bar-baz"},
|
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
assert.Equal(t, test.Out, normalizeFilename(test.In))
|
assert.Equal(t, test.Out, normalizeFilename(test.In))
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ func (checker *TimestampChecker) IsUpToDate(t *ast.Task) (bool, error) {
|
|||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
sources, err := Globs(t.Dir, t.Sources, t.ShouldUseGitignore())
|
sources, err := Globs(t.Dir, t.Sources)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
@@ -54,7 +54,7 @@ func (checker *TimestampChecker) IsUpToDate(t *ast.Task) (bool, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
generates, err := Globs(t.Dir, t.Generates, t.ShouldUseGitignore())
|
generates, err := Globs(t.Dir, t.Generates)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
@@ -112,7 +112,7 @@ func (checker *TimestampChecker) Kind() string {
|
|||||||
|
|
||||||
// Value implements the Checker Interface
|
// Value implements the Checker Interface
|
||||||
func (checker *TimestampChecker) Value(t *ast.Task) (any, error) {
|
func (checker *TimestampChecker) Value(t *ast.Task) (any, error) {
|
||||||
sources, err := Globs(t.Dir, t.Sources, t.ShouldUseGitignore())
|
sources, err := Globs(t.Dir, t.Sources)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return time.Now(), err
|
return time.Now(), err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ var (
|
|||||||
Dir string
|
Dir string
|
||||||
Entrypoint string
|
Entrypoint string
|
||||||
Output ast.Output
|
Output ast.Output
|
||||||
|
OutputCIAuto bool
|
||||||
Color bool
|
Color bool
|
||||||
Interval time.Duration
|
Interval time.Duration
|
||||||
Failfast bool
|
Failfast bool
|
||||||
@@ -87,7 +88,6 @@ var (
|
|||||||
Cert string
|
Cert string
|
||||||
CertKey string
|
CertKey string
|
||||||
Interactive bool
|
Interactive bool
|
||||||
TempDir string
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -144,11 +144,11 @@ func init() {
|
|||||||
pflag.BoolVarP(&ExitCode, "exit-code", "x", false, "Pass-through the exit code of the task command.")
|
pflag.BoolVarP(&ExitCode, "exit-code", "x", false, "Pass-through the exit code of the task command.")
|
||||||
pflag.StringVarP(&Dir, "dir", "d", "", "Sets the directory in which Task will execute and look for a Taskfile.")
|
pflag.StringVarP(&Dir, "dir", "d", "", "Sets the directory in which Task will execute and look for a Taskfile.")
|
||||||
pflag.StringVarP(&Entrypoint, "taskfile", "t", "", `Choose which Taskfile to run. Defaults to "Taskfile.yml".`)
|
pflag.StringVarP(&Entrypoint, "taskfile", "t", "", `Choose which Taskfile to run. Defaults to "Taskfile.yml".`)
|
||||||
pflag.StringVar(&TempDir, "temp-dir", getConfig(config, "TEMP_DIR", func() *string { return config.TempDir }, ""), "Sets the directory used to store Task temporary files, such as checksums. Relative paths are relative to the root Taskfile.")
|
pflag.StringVarP(&Output.Name, "output", "o", "", "Sets output style: [interleaved|group|prefixed|gitlab].")
|
||||||
pflag.StringVarP(&Output.Name, "output", "o", getConfig(config, "OUTPUT", func() *string { return nil }, ""), "Sets output style: [interleaved|group|prefixed].")
|
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", getConfig(config, "OUTPUT_GROUP_BEGIN", func() *string { return nil }, ""), "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", getConfig(config, "OUTPUT_GROUP_END", func() *string { return nil }, ""), "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", getConfig(config, "OUTPUT_GROUP_ERROR_ONLY", func() *bool { return nil }, false), "Swallow output from successful tasks.")
|
OutputCIAuto = getConfig(config, "OUTPUT_CI_AUTO", func() *bool { return config.OutputCIAuto }, false)
|
||||||
pflag.BoolVarP(&Color, "color", "c", getConfig(config, "COLOR", func() *bool { return config.Color }, true), "Colored output. Enabled by default. Set flag to false or use NO_COLOR=1 to disable.")
|
pflag.BoolVarP(&Color, "color", "c", getConfig(config, "COLOR", 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, "CONCURRENCY", func() *int { return config.Concurrency }, 0), "Limit number of tasks to run concurrently.")
|
pflag.IntVarP(&Concurrency, "concurrency", "C", getConfig(config, "CONCURRENCY", 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.")
|
||||||
@@ -307,10 +307,10 @@ func (o *flagsOption) ApplyToExecutor(e *task.Executor) {
|
|||||||
task.WithConcurrency(Concurrency),
|
task.WithConcurrency(Concurrency),
|
||||||
task.WithInterval(Interval),
|
task.WithInterval(Interval),
|
||||||
task.WithOutputStyle(Output),
|
task.WithOutputStyle(Output),
|
||||||
|
task.WithOutputCIAuto(OutputCIAuto),
|
||||||
task.WithTaskSorter(sorter),
|
task.WithTaskSorter(sorter),
|
||||||
task.WithVersionCheck(true),
|
task.WithVersionCheck(true),
|
||||||
task.WithFailfast(Failfast),
|
task.WithFailfast(Failfast),
|
||||||
task.WithTempDirPath(TempDir),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,201 +0,0 @@
|
|||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright 2018 Sourced Technologies, S.L.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
// Vendored from go-git: github.com/go-git/go-git/v5 v5.19.1,
|
|
||||||
// plumbing/format/gitignore/matcher.go. Licensed under the Apache License 2.0;
|
|
||||||
// see the LICENSE file in this directory.
|
|
||||||
|
|
||||||
package gitignore
|
|
||||||
|
|
||||||
// Matcher defines a global multi-pattern matcher for gitignore patterns
|
|
||||||
type Matcher interface {
|
|
||||||
// Match matches patterns in the order of priorities. As soon as an inclusion or
|
|
||||||
// exclusion is found, not further matching is performed.
|
|
||||||
Match(path []string, isDir bool) bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewMatcher constructs a new global matcher. Patterns must be given in the order of
|
|
||||||
// increasing priority. That is most generic settings files first, then the content of
|
|
||||||
// the repo .gitignore, then content of .gitignore down the path or the repo and then
|
|
||||||
// the content command line arguments.
|
|
||||||
func NewMatcher(ps []Pattern) Matcher {
|
|
||||||
return &matcher{ps}
|
|
||||||
}
|
|
||||||
|
|
||||||
type matcher struct {
|
|
||||||
patterns []Pattern
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *matcher) Match(path []string, isDir bool) bool {
|
|
||||||
n := len(m.patterns)
|
|
||||||
for i := n - 1; i >= 0; i-- {
|
|
||||||
if match := m.patterns[i].Match(path, isDir); match > NoMatch {
|
|
||||||
return match == Exclude
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
// Test cases ported from go-git: github.com/go-git/go-git/v5 v5.19.1,
|
|
||||||
// plumbing/format/gitignore/matcher_test.go. Licensed under the Apache
|
|
||||||
// License 2.0; see LICENSE.
|
|
||||||
|
|
||||||
package gitignore
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestMatcher_Match(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
m := NewMatcher([]Pattern{
|
|
||||||
ParsePattern("**/middle/v[uo]l?ano", nil),
|
|
||||||
ParsePattern("!volcano", nil),
|
|
||||||
})
|
|
||||||
|
|
||||||
if got := m.Match([]string{"head", "middle", "vulkano"}, false); got != true {
|
|
||||||
t.Errorf("Match(vulkano) = %t, want true", got)
|
|
||||||
}
|
|
||||||
if got := m.Match([]string{"head", "middle", "volcano"}, false); got != false {
|
|
||||||
t.Errorf("Match(volcano) = %t, want false (negated)", got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,165 +0,0 @@
|
|||||||
// Package gitignore implements gitignore pattern matching.
|
|
||||||
//
|
|
||||||
// This package is vendored from go-git:
|
|
||||||
//
|
|
||||||
// github.com/go-git/go-git/v5 v5.19.1, plumbing/format/gitignore
|
|
||||||
//
|
|
||||||
// Only the pattern parsing and matching logic (pattern.go and matcher.go) is
|
|
||||||
// copied; the file-walking helpers (dir.go) are omitted as they pull in
|
|
||||||
// go-billy and other go-git internals. The original code is licensed under the
|
|
||||||
// Apache License 2.0; see the LICENSE file in this directory.
|
|
||||||
package gitignore
|
|
||||||
|
|
||||||
import (
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MatchResult defines outcomes of a match, no match, exclusion or inclusion.
|
|
||||||
type MatchResult int
|
|
||||||
|
|
||||||
const (
|
|
||||||
// NoMatch defines the no match outcome of a match check
|
|
||||||
NoMatch MatchResult = iota
|
|
||||||
// Exclude defines an exclusion of a file as a result of a match check
|
|
||||||
Exclude
|
|
||||||
// Include defines an explicit inclusion of a file as a result of a match check
|
|
||||||
Include
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
inclusionPrefix = "!"
|
|
||||||
zeroToManyDirs = "**"
|
|
||||||
patternDirSep = "/"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Pattern defines a single gitignore pattern.
|
|
||||||
type Pattern interface {
|
|
||||||
// Match matches the given path to the pattern.
|
|
||||||
Match(path []string, isDir bool) MatchResult
|
|
||||||
}
|
|
||||||
|
|
||||||
type pattern struct {
|
|
||||||
domain []string
|
|
||||||
pattern []string
|
|
||||||
inclusion bool
|
|
||||||
dirOnly bool
|
|
||||||
isGlob bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParsePattern parses a gitignore pattern string into the Pattern structure.
|
|
||||||
func ParsePattern(p string, domain []string) Pattern {
|
|
||||||
// storing domain, copy it to ensure it isn't changed externally
|
|
||||||
domain = append([]string(nil), domain...)
|
|
||||||
res := pattern{domain: domain}
|
|
||||||
|
|
||||||
if strings.HasPrefix(p, inclusionPrefix) {
|
|
||||||
res.inclusion = true
|
|
||||||
p = p[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.HasSuffix(p, "\\ ") {
|
|
||||||
p = strings.TrimRight(p, " ")
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasSuffix(p, patternDirSep) {
|
|
||||||
res.dirOnly = true
|
|
||||||
p = p[:len(p)-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.Contains(p, patternDirSep) {
|
|
||||||
res.isGlob = true
|
|
||||||
}
|
|
||||||
|
|
||||||
res.pattern = strings.Split(p, patternDirSep)
|
|
||||||
return &res
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *pattern) Match(path []string, isDir bool) MatchResult {
|
|
||||||
if len(path) <= len(p.domain) {
|
|
||||||
return NoMatch
|
|
||||||
}
|
|
||||||
for i, e := range p.domain {
|
|
||||||
if path[i] != e {
|
|
||||||
return NoMatch
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
path = path[len(p.domain):]
|
|
||||||
if p.isGlob && !p.globMatch(path, isDir) {
|
|
||||||
return NoMatch
|
|
||||||
} else if !p.isGlob && !p.simpleNameMatch(path, isDir) {
|
|
||||||
return NoMatch
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.inclusion {
|
|
||||||
return Include
|
|
||||||
} else {
|
|
||||||
return Exclude
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *pattern) simpleNameMatch(path []string, isDir bool) bool {
|
|
||||||
for i, name := range path {
|
|
||||||
if match, err := filepath.Match(p.pattern[0], name); err != nil {
|
|
||||||
return false
|
|
||||||
} else if !match {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if p.dirOnly && !isDir && i == len(path)-1 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *pattern) globMatch(path []string, isDir bool) bool {
|
|
||||||
matched := false
|
|
||||||
canTraverse := false
|
|
||||||
for i, pattern := range p.pattern {
|
|
||||||
if pattern == "" {
|
|
||||||
canTraverse = false
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if pattern == zeroToManyDirs {
|
|
||||||
if i == len(p.pattern)-1 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
canTraverse = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if strings.Contains(pattern, zeroToManyDirs) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if len(path) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if canTraverse {
|
|
||||||
canTraverse = false
|
|
||||||
for len(path) > 0 {
|
|
||||||
e := path[0]
|
|
||||||
path = path[1:]
|
|
||||||
if match, err := filepath.Match(pattern, e); err != nil {
|
|
||||||
return false
|
|
||||||
} else if match {
|
|
||||||
matched = true
|
|
||||||
break
|
|
||||||
} else if len(path) == 0 {
|
|
||||||
// if nothing left then fail
|
|
||||||
matched = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if match, err := filepath.Match(pattern, path[0]); err != nil || !match {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
matched = true
|
|
||||||
path = path[1:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if matched && p.dirOnly && !isDir && len(path) == 0 {
|
|
||||||
matched = false
|
|
||||||
}
|
|
||||||
return matched
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
// Test cases ported from go-git: github.com/go-git/go-git/v5 v5.19.1,
|
|
||||||
// plumbing/format/gitignore/pattern_test.go (originally written against
|
|
||||||
// gopkg.in/check.v1; rewritten here as table-driven stdlib tests to avoid an
|
|
||||||
// extra test dependency). Licensed under the Apache License 2.0; see LICENSE.
|
|
||||||
|
|
||||||
package gitignore
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestParsePattern_Match(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
pattern string
|
|
||||||
domain []string
|
|
||||||
path []string
|
|
||||||
isDir bool
|
|
||||||
want MatchResult
|
|
||||||
}{
|
|
||||||
{"inclusion", "!vul?ano", nil, []string{"value", "vulkano", "tail"}, false, Include},
|
|
||||||
{"domainLonger_mismatch", "value", []string{"head", "middle", "tail"}, []string{"head", "middle"}, false, NoMatch},
|
|
||||||
{"domainSameLength_mismatch", "value", []string{"head", "middle", "tail"}, []string{"head", "middle", "tail"}, false, NoMatch},
|
|
||||||
{"domainMismatch_mismatch", "value", []string{"head", "middle", "tail"}, []string{"head", "middle", "_tail_", "value"}, false, NoMatch},
|
|
||||||
{"withDomain", "middle/", []string{"value", "volcano"}, []string{"value", "volcano", "middle", "tail"}, false, Exclude},
|
|
||||||
{"onlyMatchInDomain_mismatch", "volcano/", []string{"value", "volcano"}, []string{"value", "volcano", "tail"}, true, NoMatch},
|
|
||||||
{"atStart", "value", nil, []string{"value", "tail"}, false, Exclude},
|
|
||||||
{"inTheMiddle", "value", nil, []string{"head", "value", "tail"}, false, Exclude},
|
|
||||||
{"atEnd", "value", nil, []string{"head", "value"}, false, Exclude},
|
|
||||||
{"atStart_dirWanted", "value/", nil, []string{"value", "tail"}, false, Exclude},
|
|
||||||
{"inTheMiddle_dirWanted", "value/", nil, []string{"head", "value", "tail"}, false, Exclude},
|
|
||||||
{"atEnd_dirWanted", "value/", nil, []string{"head", "value"}, true, Exclude},
|
|
||||||
{"atEnd_dirWanted_notADir_mismatch", "value/", nil, []string{"head", "value"}, false, NoMatch},
|
|
||||||
{"mismatch", "value", nil, []string{"head", "val", "tail"}, false, NoMatch},
|
|
||||||
{"valueLonger_mismatch", "val", nil, []string{"head", "value", "tail"}, false, NoMatch},
|
|
||||||
{"withAsterisk", "v*o", nil, []string{"value", "vulkano", "tail"}, false, Exclude},
|
|
||||||
{"withQuestionMark", "vul?ano", nil, []string{"value", "vulkano", "tail"}, false, Exclude},
|
|
||||||
{"magicChars", "v[ou]l[kc]ano", nil, []string{"value", "volcano"}, false, Exclude},
|
|
||||||
{"wrongPattern_mismatch", "v[ou]l[", nil, []string{"value", "vol["}, false, NoMatch},
|
|
||||||
{"glob_fromRootWithSlash", "/value/vul?ano", nil, []string{"value", "vulkano", "tail"}, false, Exclude},
|
|
||||||
{"glob_withDomain", "middle/tail/", []string{"value", "volcano"}, []string{"value", "volcano", "middle", "tail"}, true, Exclude},
|
|
||||||
{"glob_onlyMatchInDomain_mismatch", "volcano/tail", []string{"value", "volcano"}, []string{"value", "volcano", "tail"}, false, NoMatch},
|
|
||||||
{"glob_fromRootWithoutSlash", "value/vul?ano", nil, []string{"value", "vulkano", "tail"}, false, Exclude},
|
|
||||||
{"glob_fromRoot_mismatch", "value/vulkano", nil, []string{"value", "volcano"}, false, NoMatch},
|
|
||||||
{"glob_fromRoot_tooShort_mismatch", "value/vul?ano", nil, []string{"value"}, false, NoMatch},
|
|
||||||
{"glob_fromRoot_notAtRoot_mismatch", "/value/volcano", nil, []string{"value", "value", "volcano"}, false, NoMatch},
|
|
||||||
{"glob_leadingAsterisks_atStart", "**/*lue/vol?ano", nil, []string{"value", "volcano", "tail"}, false, Exclude},
|
|
||||||
{"glob_leadingAsterisks_notAtStart", "**/*lue/vol?ano", nil, []string{"head", "value", "volcano", "tail"}, false, Exclude},
|
|
||||||
{"glob_leadingAsterisks_mismatch", "**/*lue/vol?ano", nil, []string{"head", "value", "Volcano", "tail"}, false, NoMatch},
|
|
||||||
{"glob_leadingAsterisks_isDir", "**/*lue/vol?ano/", nil, []string{"head", "value", "volcano", "tail"}, false, Exclude},
|
|
||||||
{"glob_leadingAsterisks_isDirAtEnd", "**/*lue/vol?ano/", nil, []string{"head", "value", "volcano"}, true, Exclude},
|
|
||||||
{"glob_leadingAsterisks_isDir_mismatch", "**/*lue/vol?ano/", nil, []string{"head", "value", "Colcano"}, true, NoMatch},
|
|
||||||
{"glob_leadingAsterisks_isDirNoDirAtEnd_mismatch", "**/*lue/vol?ano/", nil, []string{"head", "value", "volcano"}, false, NoMatch},
|
|
||||||
{"glob_tailingAsterisks", "/*lue/vol?ano/**", nil, []string{"value", "volcano", "tail", "moretail"}, false, Exclude},
|
|
||||||
{"glob_tailingAsterisks_exactMatch", "/*lue/vol?ano/**", nil, []string{"value", "volcano"}, false, Exclude},
|
|
||||||
{"glob_middleAsterisks_emptyMatch", "/*lue/**/vol?ano", nil, []string{"value", "volcano"}, false, Exclude},
|
|
||||||
{"glob_middleAsterisks_oneMatch", "/*lue/**/vol?ano", nil, []string{"value", "middle", "volcano"}, false, Exclude},
|
|
||||||
{"glob_middleAsterisks_multiMatch", "/*lue/**/vol?ano", nil, []string{"value", "middle1", "middle2", "volcano"}, false, Exclude},
|
|
||||||
{"glob_middleAsterisks_isDir_trailing", "/*lue/**/vol?ano/", nil, []string{"value", "middle1", "middle2", "volcano"}, true, Exclude},
|
|
||||||
{"glob_middleAsterisks_isDir_trailing_mismatch", "/*lue/**/vol?ano/", nil, []string{"value", "middle1", "middle2", "volcano"}, false, NoMatch},
|
|
||||||
{"glob_middleAsterisks_isDir", "/*lue/**/vol?ano/", nil, []string{"value", "middle1", "middle2", "volcano", "tail"}, false, Exclude},
|
|
||||||
{"glob_wrongDoubleAsterisk_mismatch", "/*lue/**foo/vol?ano", nil, []string{"value", "foo", "volcano", "tail"}, false, NoMatch},
|
|
||||||
{"glob_magicChars", "**/head/v[ou]l[kc]ano", nil, []string{"value", "head", "volcano"}, false, Exclude},
|
|
||||||
{"glob_wrongPattern_noTraversal_mismatch", "**/head/v[ou]l[", nil, []string{"value", "head", "vol["}, false, NoMatch},
|
|
||||||
{"glob_wrongPattern_onTraversal_mismatch", "/value/**/v[ou]l[", nil, []string{"value", "head", "vol["}, false, NoMatch},
|
|
||||||
{"glob_issue_923", "**/android/**/GeneratedPluginRegistrant.java", nil, []string{"packages", "flutter_tools", "lib", "src", "android", "gradle.dart"}, false, NoMatch},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
p := ParsePattern(tt.pattern, tt.domain)
|
|
||||||
if got := p.Match(tt.path, tt.isDir); got != tt.want {
|
|
||||||
t.Errorf("ParsePattern(%q, %v).Match(%v, %t) = %v, want %v",
|
|
||||||
tt.pattern, tt.domain, tt.path, tt.isDir, got, tt.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
116
internal/output/gitlab.go
Normal file
116
internal/output/gitlab.go
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
package output
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"regexp"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
|
||||||
|
"github.com/go-task/task/v3/internal/templater"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GitLab renders a task's output wrapped in [GitLab CI collapsible
|
||||||
|
// section markers]. Section IDs are generated automatically so that
|
||||||
|
// start and end markers always match and stay unique per invocation.
|
||||||
|
//
|
||||||
|
// GitLab wraps output at the task level via the [TaskWrapper] interface,
|
||||||
|
// so each task (including its command announcements and all its cmds)
|
||||||
|
// appears inside a single collapsible section. Nested task invocations
|
||||||
|
// produce nested sections.
|
||||||
|
//
|
||||||
|
// [GitLab CI collapsible section markers]: https://docs.gitlab.com/ci/jobs/job_logs/#create-custom-collapsible-sections
|
||||||
|
type GitLab struct {
|
||||||
|
Collapsed bool
|
||||||
|
ErrorOnly bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapWriter is a passthrough for GitLab: wrapping happens at the task
|
||||||
|
// level via WrapTask, not per command.
|
||||||
|
func (g GitLab) WrapWriter(stdOut, stdErr io.Writer, _ string, _ *templater.Cache) (io.Writer, io.Writer, CloseFunc) {
|
||||||
|
return stdOut, stdErr, func(error) error { return nil }
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapTask wraps an entire task's output in a single collapsible section.
|
||||||
|
func (g GitLab) WrapTask(stdOut, _ io.Writer, cache *templater.Cache) (io.Writer, io.Writer, CloseFunc) {
|
||||||
|
header := ""
|
||||||
|
if cache != nil {
|
||||||
|
header = templater.Replace("{{.TASK}}", cache)
|
||||||
|
}
|
||||||
|
if header == "" {
|
||||||
|
header = "task"
|
||||||
|
}
|
||||||
|
|
||||||
|
id := fmt.Sprintf("%s_%s", gitlabSectionSlug(header), uuid.New().String()[:8])
|
||||||
|
|
||||||
|
gw := &gitlabWriter{
|
||||||
|
writer: stdOut,
|
||||||
|
id: id,
|
||||||
|
header: header,
|
||||||
|
collapsed: g.Collapsed,
|
||||||
|
startTS: time.Now().Unix(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return gw, gw, func(err error) error {
|
||||||
|
if g.ErrorOnly && err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return gw.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type gitlabWriter struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
writer io.Writer
|
||||||
|
buff bytes.Buffer
|
||||||
|
id string
|
||||||
|
header string
|
||||||
|
collapsed bool
|
||||||
|
startTS int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gw *gitlabWriter) Write(p []byte) (int, error) {
|
||||||
|
gw.mu.Lock()
|
||||||
|
defer gw.mu.Unlock()
|
||||||
|
return gw.buff.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gw *gitlabWriter) close() error {
|
||||||
|
gw.mu.Lock()
|
||||||
|
defer gw.mu.Unlock()
|
||||||
|
|
||||||
|
if gw.buff.Len() == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
b.WriteString(gitlabSectionStart(gw.startTS, gw.id, gw.header, gw.collapsed))
|
||||||
|
if _, err := io.Copy(&b, &gw.buff); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
b.WriteString(gitlabSectionEnd(time.Now().Unix(), gw.id))
|
||||||
|
|
||||||
|
_, err := io.Copy(gw.writer, &b)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func gitlabSectionStart(ts int64, id, header string, collapsed bool) string {
|
||||||
|
options := ""
|
||||||
|
if collapsed {
|
||||||
|
options = "[collapsed=true]"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("\x1b[0Ksection_start:%d:%s%s\r\x1b[0K%s\n", ts, id, options, header)
|
||||||
|
}
|
||||||
|
|
||||||
|
func gitlabSectionEnd(ts int64, id string) string {
|
||||||
|
return fmt.Sprintf("\x1b[0Ksection_end:%d:%s\r\x1b[0K\n", ts, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
var gitlabSlugDisallowed = regexp.MustCompile(`[^a-zA-Z0-9_.-]`)
|
||||||
|
|
||||||
|
func gitlabSectionSlug(s string) string {
|
||||||
|
return gitlabSlugDisallowed.ReplaceAllString(s, "_")
|
||||||
|
}
|
||||||
@@ -13,6 +13,14 @@ type Output interface {
|
|||||||
WrapWriter(stdOut, stdErr io.Writer, prefix string, cache *templater.Cache) (io.Writer, io.Writer, CloseFunc)
|
WrapWriter(stdOut, stdErr io.Writer, prefix string, cache *templater.Cache) (io.Writer, io.Writer, CloseFunc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TaskWrapper is an optional interface that Output implementations can satisfy
|
||||||
|
// to wrap an entire task's execution in a single enclosing block — including
|
||||||
|
// the task's command announcements and all its commands' output — instead of
|
||||||
|
// wrapping each command individually via WrapWriter.
|
||||||
|
type TaskWrapper interface {
|
||||||
|
WrapTask(stdOut, stdErr io.Writer, cache *templater.Cache) (io.Writer, io.Writer, CloseFunc)
|
||||||
|
}
|
||||||
|
|
||||||
type CloseFunc func(err error) error
|
type CloseFunc func(err error) error
|
||||||
|
|
||||||
// Build the Output for the requested ast.Output.
|
// Build the Output for the requested ast.Output.
|
||||||
@@ -34,6 +42,14 @@ func BuildFor(o *ast.Output, logger *logger.Logger) (Output, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return NewPrefixed(logger), nil
|
return NewPrefixed(logger), nil
|
||||||
|
case "gitlab":
|
||||||
|
if err := checkOutputGroupUnset(o); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return GitLab{
|
||||||
|
Collapsed: o.GitLab.Collapsed,
|
||||||
|
ErrorOnly: o.GitLab.ErrorOnly,
|
||||||
|
}, nil
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf(`task: output style %q not recognized`, o.Name)
|
return nil, fmt.Errorf(`task: output style %q not recognized`, o.Name)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,12 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -121,6 +126,238 @@ func TestGroupErrorOnlyShowsOutputOnError(t *testing.T) {
|
|||||||
assert.Equal(t, "std-out\nstd-err\n", b.String())
|
assert.Equal(t, "std-out\nstd-err\n", b.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func gitlabTaskCache(taskName string) *templater.Cache {
|
||||||
|
return &templater.Cache{
|
||||||
|
Vars: ast.NewVars(
|
||||||
|
&ast.VarElement{
|
||||||
|
Key: "TASK",
|
||||||
|
Value: ast.Var{Value: taskName},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var gitlabMarkerPattern = regexp.MustCompile(
|
||||||
|
`\x1b\[0Ksection_start:(\d+):(\S+?)(\[[^\]]+\])?\r\x1b\[0K(.*)\n` +
|
||||||
|
`(?s)(.*)` +
|
||||||
|
`\x1b\[0Ksection_end:(\d+):(\S+)\r\x1b\[0K\n$`,
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGitLab(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
o := output.GitLab{}
|
||||||
|
w, _, cleanup := o.WrapTask(&b, io.Discard, gitlabTaskCache("build"))
|
||||||
|
|
||||||
|
fmt.Fprintln(w, "hello")
|
||||||
|
assert.Equal(t, "", b.String(), "output must be buffered until close")
|
||||||
|
require.NoError(t, cleanup(nil))
|
||||||
|
|
||||||
|
m := gitlabMarkerPattern.FindStringSubmatch(b.String())
|
||||||
|
require.NotNil(t, m, "output should match GitLab section markers, got: %q", b.String())
|
||||||
|
assert.Equal(t, m[2], m[7], "start and end section IDs must match")
|
||||||
|
assert.Empty(t, m[3], "collapsed option should not be present by default")
|
||||||
|
assert.Equal(t, "build", m[4], "section header should be the task name")
|
||||||
|
assert.Equal(t, "hello\n", m[5], "wrapped content must be preserved")
|
||||||
|
assert.Contains(t, m[2], "build_", "section ID should be prefixed with slugged task name")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGitLabUniqueSectionIDs(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
o := output.GitLab{}
|
||||||
|
|
||||||
|
ids := make([]string, 3)
|
||||||
|
for i := range ids {
|
||||||
|
var b bytes.Buffer
|
||||||
|
w, _, cleanup := o.WrapTask(&b, io.Discard, gitlabTaskCache("build"))
|
||||||
|
fmt.Fprintln(w, "x")
|
||||||
|
require.NoError(t, cleanup(nil))
|
||||||
|
m := gitlabMarkerPattern.FindStringSubmatch(b.String())
|
||||||
|
require.NotNil(t, m)
|
||||||
|
ids[i] = m[2]
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.NotEqual(t, ids[0], ids[1])
|
||||||
|
assert.NotEqual(t, ids[1], ids[2])
|
||||||
|
assert.NotEqual(t, ids[0], ids[2])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGitLabCollapsed(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
o := output.GitLab{Collapsed: true}
|
||||||
|
w, _, cleanup := o.WrapTask(&b, io.Discard, gitlabTaskCache("build"))
|
||||||
|
fmt.Fprintln(w, "x")
|
||||||
|
require.NoError(t, cleanup(nil))
|
||||||
|
|
||||||
|
m := gitlabMarkerPattern.FindStringSubmatch(b.String())
|
||||||
|
require.NotNil(t, m)
|
||||||
|
assert.Equal(t, "[collapsed=true]", m[3])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGitLabErrorOnlySwallowsOutputOnNoError(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
o := output.GitLab{ErrorOnly: true}
|
||||||
|
w, _, cleanup := o.WrapTask(&b, io.Discard, gitlabTaskCache("build"))
|
||||||
|
fmt.Fprintln(w, "hello")
|
||||||
|
require.NoError(t, cleanup(nil))
|
||||||
|
assert.Empty(t, b.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGitLabErrorOnlyShowsOutputOnError(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
o := output.GitLab{ErrorOnly: true}
|
||||||
|
w, _, cleanup := o.WrapTask(&b, io.Discard, gitlabTaskCache("build"))
|
||||||
|
fmt.Fprintln(w, "hello")
|
||||||
|
require.NoError(t, cleanup(errors.New("boom")))
|
||||||
|
|
||||||
|
m := gitlabMarkerPattern.FindStringSubmatch(b.String())
|
||||||
|
require.NotNil(t, m)
|
||||||
|
assert.Equal(t, "hello\n", m[5])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGitLabSlugSanitizesTaskName(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
o := output.GitLab{}
|
||||||
|
w, _, cleanup := o.WrapTask(&b, io.Discard, gitlabTaskCache("my task:with spaces"))
|
||||||
|
fmt.Fprintln(w, "x")
|
||||||
|
require.NoError(t, cleanup(nil))
|
||||||
|
|
||||||
|
m := gitlabMarkerPattern.FindStringSubmatch(b.String())
|
||||||
|
require.NotNil(t, m)
|
||||||
|
assert.Regexp(t, `^[a-zA-Z0-9_.-]+$`, m[2], "section ID must only contain GitLab-allowed chars")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGitLabWrapWriterIsPassthrough(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
o := output.GitLab{}
|
||||||
|
w, _, cleanup := o.WrapWriter(&b, io.Discard, "", nil)
|
||||||
|
|
||||||
|
fmt.Fprintln(w, "hello")
|
||||||
|
assert.Equal(t, "hello\n", b.String(), "WrapWriter must be a passthrough for GitLab")
|
||||||
|
assert.NoError(t, cleanup(nil))
|
||||||
|
assert.Equal(t, "hello\n", b.String(), "closer must be a no-op")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGitLabWrapTaskSingleSection(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
o := output.GitLab{}
|
||||||
|
w, _, cleanup := o.WrapTask(&b, io.Discard, gitlabTaskCache("build"))
|
||||||
|
|
||||||
|
// Simulate multiple cmd outputs being written during a task's execution.
|
||||||
|
fmt.Fprintln(w, "cmd 1 output")
|
||||||
|
fmt.Fprintln(w, "cmd 2 output")
|
||||||
|
fmt.Fprintln(w, "cmd 3 output")
|
||||||
|
require.NoError(t, cleanup(nil))
|
||||||
|
|
||||||
|
// There must be exactly one section_start and one section_end.
|
||||||
|
assert.Equal(t, 1, strings.Count(b.String(), "section_start:"))
|
||||||
|
assert.Equal(t, 1, strings.Count(b.String(), "section_end:"))
|
||||||
|
|
||||||
|
m := gitlabMarkerPattern.FindStringSubmatch(b.String())
|
||||||
|
require.NotNil(t, m)
|
||||||
|
assert.Equal(t, "cmd 1 output\ncmd 2 output\ncmd 3 output\n", m[5])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGitLabWrapTaskDurationElapsed(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
o := output.GitLab{}
|
||||||
|
w, _, cleanup := o.WrapTask(&b, io.Discard, gitlabTaskCache("slow"))
|
||||||
|
|
||||||
|
fmt.Fprintln(w, "started")
|
||||||
|
time.Sleep(1100 * time.Millisecond)
|
||||||
|
fmt.Fprintln(w, "done")
|
||||||
|
require.NoError(t, cleanup(nil))
|
||||||
|
|
||||||
|
m := gitlabMarkerPattern.FindStringSubmatch(b.String())
|
||||||
|
require.NotNil(t, m)
|
||||||
|
startTS, err := strconv.ParseInt(m[1], 10, 64)
|
||||||
|
require.NoError(t, err)
|
||||||
|
endTS, err := strconv.ParseInt(m[6], 10, 64)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.GreaterOrEqual(t, endTS-startTS, int64(1),
|
||||||
|
"end TS must be at least 1 second after start TS when task takes >1s")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGitLabWrapTaskNested(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var root bytes.Buffer
|
||||||
|
parent := output.GitLab{}
|
||||||
|
parentW, _, parentClose := parent.WrapTask(&root, io.Discard, gitlabTaskCache("parent"))
|
||||||
|
|
||||||
|
fmt.Fprintln(parentW, "before child")
|
||||||
|
|
||||||
|
child := output.GitLab{}
|
||||||
|
childW, _, childClose := child.WrapTask(parentW, io.Discard, gitlabTaskCache("child"))
|
||||||
|
fmt.Fprintln(childW, "inside child")
|
||||||
|
require.NoError(t, childClose(nil))
|
||||||
|
|
||||||
|
fmt.Fprintln(parentW, "after child")
|
||||||
|
require.NoError(t, parentClose(nil))
|
||||||
|
|
||||||
|
out := root.String()
|
||||||
|
// Two section_start and two section_end
|
||||||
|
assert.Equal(t, 2, strings.Count(out, "section_start:"))
|
||||||
|
assert.Equal(t, 2, strings.Count(out, "section_end:"))
|
||||||
|
|
||||||
|
// Order: parent start → child start → child end → parent end
|
||||||
|
parentStart := strings.Index(out, "section_start:") // first
|
||||||
|
childStart := strings.Index(out[parentStart+1:], "section_start:") + parentStart + 1
|
||||||
|
childEnd := strings.Index(out, "section_end:")
|
||||||
|
parentEnd := strings.LastIndex(out, "section_end:")
|
||||||
|
assert.Less(t, parentStart, childStart, "child_start must come after parent_start")
|
||||||
|
assert.Less(t, childStart, childEnd, "child_end must come after child_start")
|
||||||
|
assert.Less(t, childEnd, parentEnd, "parent_end must come after child_end")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGitLabWrapTaskConcurrentWrites(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var root bytes.Buffer
|
||||||
|
parent := output.GitLab{}
|
||||||
|
parentW, _, parentClose := parent.WrapTask(&root, io.Discard, gitlabTaskCache("parent"))
|
||||||
|
|
||||||
|
const numChildren = 10
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for i := 0; i < numChildren; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(i int) {
|
||||||
|
defer wg.Done()
|
||||||
|
child := output.GitLab{}
|
||||||
|
childW, _, childClose := child.WrapTask(parentW, io.Discard, gitlabTaskCache(fmt.Sprintf("child%d", i)))
|
||||||
|
fmt.Fprintf(childW, "child %d output\n", i)
|
||||||
|
_ = childClose(nil)
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
require.NoError(t, parentClose(nil))
|
||||||
|
|
||||||
|
out := root.String()
|
||||||
|
// 1 parent + 10 children = 11 section_start and 11 section_end
|
||||||
|
assert.Equal(t, 11, strings.Count(out, "section_start:"))
|
||||||
|
assert.Equal(t, 11, strings.Count(out, "section_end:"))
|
||||||
|
// All 10 child outputs present
|
||||||
|
for i := 0; i < numChildren; i++ {
|
||||||
|
assert.Contains(t, out, fmt.Sprintf("child %d output", i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestPrefixed(t *testing.T) { //nolint:paralleltest // cannot run in parallel
|
func TestPrefixed(t *testing.T) { //nolint:paralleltest // cannot run in parallel
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
l := &logger.Logger{
|
l := &logger.Logger{
|
||||||
|
|||||||
@@ -117,12 +117,7 @@ func printTaskCommands(l *logger.Logger, t *ast.Task) {
|
|||||||
isCommand := c.Cmd != ""
|
isCommand := c.Cmd != ""
|
||||||
l.Outf(logger.Default, " - ")
|
l.Outf(logger.Default, " - ")
|
||||||
if isCommand {
|
if isCommand {
|
||||||
// Use the masked command so secret values are not leaked in summaries
|
l.Outf(logger.Yellow, "%s\n", c.Cmd)
|
||||||
logCmd := c.LogCmd
|
|
||||||
if logCmd == "" {
|
|
||||||
logCmd = c.Cmd
|
|
||||||
}
|
|
||||||
l.Outf(logger.Yellow, "%s\n", logCmd)
|
|
||||||
} else {
|
} else {
|
||||||
l.Outf(logger.Green, "Task: %s\n", c.Task)
|
l.Outf(logger.Green, "Task: %s\n", c.Task)
|
||||||
}
|
}
|
||||||
@@ -201,11 +196,6 @@ func printTaskEnv(l *logger.Logger, t *ast.Task) {
|
|||||||
// formatVarValue formats a variable value based on its type.
|
// formatVarValue formats a variable value based on its type.
|
||||||
// Handles static values, shell commands (sh:), references (ref:), and maps.
|
// Handles static values, shell commands (sh:), references (ref:), and maps.
|
||||||
func formatVarValue(v ast.Var) string {
|
func formatVarValue(v ast.Var) string {
|
||||||
// Never expose secret variables in the summary, whatever their type
|
|
||||||
if v.Secret {
|
|
||||||
return "*****"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shell command - check this first before Value
|
// Shell command - check this first before Value
|
||||||
// because dynamic vars may have both Sh and an empty Value
|
// because dynamic vars may have both Sh and an empty Value
|
||||||
if v.Sh != nil {
|
if v.Sh != nil {
|
||||||
@@ -295,9 +285,7 @@ func isEnvVar(key string, envVars map[string]bool) bool {
|
|||||||
key == "TASKFILE_DIR" ||
|
key == "TASKFILE_DIR" ||
|
||||||
key == "USER_WORKING_DIR" ||
|
key == "USER_WORKING_DIR" ||
|
||||||
key == "ALIAS" ||
|
key == "ALIAS" ||
|
||||||
key == "MATCH" ||
|
key == "MATCH" {
|
||||||
key == "PATH_LIST_SEPARATOR" ||
|
|
||||||
key == "FILE_PATH_SEPARATOR" {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return envVars[key]
|
return envVars[key]
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ package templater
|
|||||||
import (
|
import (
|
||||||
"maps"
|
"maps"
|
||||||
"math/rand/v2"
|
"math/rand/v2"
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -23,8 +21,8 @@ var templateFuncs template.FuncMap
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
taskFuncs := template.FuncMap{
|
taskFuncs := template.FuncMap{
|
||||||
"OS": goos,
|
"OS": os,
|
||||||
"ARCH": goarch,
|
"ARCH": arch,
|
||||||
"numCPU": runtime.NumCPU,
|
"numCPU": runtime.NumCPU,
|
||||||
"catLines": catLines,
|
"catLines": catLines,
|
||||||
"splitLines": splitLines,
|
"splitLines": splitLines,
|
||||||
@@ -35,8 +33,6 @@ func init() {
|
|||||||
"splitArgs": splitArgs,
|
"splitArgs": splitArgs,
|
||||||
"IsSH": IsSH, // Deprecated
|
"IsSH": IsSH, // Deprecated
|
||||||
"joinPath": filepath.Join,
|
"joinPath": filepath.Join,
|
||||||
"joinEnv": joinEnv,
|
|
||||||
"joinUrl": joinUrl,
|
|
||||||
"relPath": filepath.Rel,
|
"relPath": filepath.Rel,
|
||||||
"absPath": filepath.Abs,
|
"absPath": filepath.Abs,
|
||||||
"merge": merge,
|
"merge": merge,
|
||||||
@@ -61,11 +57,11 @@ func init() {
|
|||||||
maps.Copy(templateFuncs, taskFuncs)
|
maps.Copy(templateFuncs, taskFuncs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func goos() string {
|
func os() string {
|
||||||
return runtime.GOOS
|
return runtime.GOOS
|
||||||
}
|
}
|
||||||
|
|
||||||
func goarch() string {
|
func arch() string {
|
||||||
return runtime.GOARCH
|
return runtime.GOARCH
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,14 +95,6 @@ func IsSH() bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func joinEnv(elem ...string) string {
|
|
||||||
return strings.Join(elem, string(os.PathListSeparator))
|
|
||||||
}
|
|
||||||
|
|
||||||
func joinUrl(elem ...string) string {
|
|
||||||
return path.Join(elem...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func merge(base map[string]any, v ...map[string]any) map[string]any {
|
func merge(base map[string]any, v ...map[string]any) map[string]any {
|
||||||
cap := len(v)
|
cap := len(v)
|
||||||
for _, m := range v {
|
for _, m := range v {
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
package templater
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/go-task/task/v3/taskfile/ast"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MaskSecrets replaces template placeholders with their values, masking secrets.
|
|
||||||
// This function uses the Go templater to resolve all variables ({{.VAR}}) while
|
|
||||||
// masking secret ones as "*****".
|
|
||||||
func MaskSecrets(cmdTemplate string, vars *ast.Vars) string {
|
|
||||||
return MaskSecretsWithExtra(cmdTemplate, vars, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MaskSecretsWithExtra is like MaskSecrets but also resolves extra variables (e.g., loop vars).
|
|
||||||
func MaskSecretsWithExtra(cmdTemplate string, vars *ast.Vars, extra map[string]any) string {
|
|
||||||
if vars == nil {
|
|
||||||
vars = ast.NewVars()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fast path: if there are no secrets to mask, resolve the template directly
|
|
||||||
// without the extra DeepCopy + masking pass.
|
|
||||||
if !hasSecrets(vars) {
|
|
||||||
cache := &Cache{Vars: vars}
|
|
||||||
result := ReplaceWithExtra(cmdTemplate, cache, extra)
|
|
||||||
if cache.Err() != nil {
|
|
||||||
return cmdTemplate
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a copy with secret values masked, leaving the originals untouched.
|
|
||||||
maskedVars := vars.DeepCopy()
|
|
||||||
for name, v := range maskedVars.All() {
|
|
||||||
if v.Secret {
|
|
||||||
maskedVars.Set(name, ast.Var{
|
|
||||||
Value: "*****",
|
|
||||||
Secret: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cache := &Cache{Vars: maskedVars}
|
|
||||||
result := ReplaceWithExtra(cmdTemplate, cache, extra)
|
|
||||||
|
|
||||||
// If there was an error, return the original template
|
|
||||||
if cache.Err() != nil {
|
|
||||||
return cmdTemplate
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// hasSecrets reports whether any variable is marked as secret.
|
|
||||||
func hasSecrets(vars *ast.Vars) bool {
|
|
||||||
for _, v := range vars.All() {
|
|
||||||
if v.Secret {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
@@ -68,13 +68,6 @@ func ReplaceWithExtra[T any](v T, cache *Cache, extra map[string]any) T {
|
|||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optimization: skip if string is not a template
|
|
||||||
if s, ok := any(v).(string); ok {
|
|
||||||
if !strings.Contains(s, "{{") {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize the cache map if it's not already initialized
|
// Initialize the cache map if it's not already initialized
|
||||||
if cache.cacheMap == nil {
|
if cache.cacheMap == nil {
|
||||||
cache.cacheMap = cache.Vars.ToCacheMap()
|
cache.cacheMap = cache.Vars.ToCacheMap()
|
||||||
@@ -89,10 +82,6 @@ func ReplaceWithExtra[T any](v T, cache *Cache, extra map[string]any) T {
|
|||||||
|
|
||||||
// Traverse the value and parse any template variables
|
// Traverse the value and parse any template variables
|
||||||
copy, err := deepcopy.TraverseStringsFunc(v, func(v string) (string, error) {
|
copy, err := deepcopy.TraverseStringsFunc(v, func(v string) (string, error) {
|
||||||
// Optimization: skip if string is not a template
|
|
||||||
if !strings.Contains(v, "{{") {
|
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
tpl, err := template.New("").Funcs(templateFuncs).Parse(v)
|
tpl, err := template.New("").Funcs(templateFuncs).Parse(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return v, err
|
return v, err
|
||||||
@@ -132,7 +121,7 @@ func ReplaceVar(v ast.Var, cache *Cache) ast.Var {
|
|||||||
|
|
||||||
func ReplaceVarWithExtra(v ast.Var, cache *Cache, extra map[string]any) ast.Var {
|
func ReplaceVarWithExtra(v ast.Var, cache *Cache, extra map[string]any) ast.Var {
|
||||||
if v.Ref != "" {
|
if v.Ref != "" {
|
||||||
return ast.Var{Value: ResolveRef(v.Ref, cache), Secret: v.Secret}
|
return ast.Var{Value: ResolveRef(v.Ref, cache)}
|
||||||
}
|
}
|
||||||
return ast.Var{
|
return ast.Var{
|
||||||
Value: ReplaceWithExtra(v.Value, cache, extra),
|
Value: ReplaceWithExtra(v.Value, cache, extra),
|
||||||
@@ -140,7 +129,6 @@ func ReplaceVarWithExtra(v ast.Var, cache *Cache, extra map[string]any) ast.Var
|
|||||||
Live: v.Live,
|
Live: v.Live,
|
||||||
Ref: v.Ref,
|
Ref: v.Ref,
|
||||||
Dir: v.Dir,
|
Dir: v.Dir,
|
||||||
Secret: v.Secret,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
3.51.1
|
3.50.0
|
||||||
|
|||||||
12
mise.toml
12
mise.toml
@@ -1,12 +0,0 @@
|
|||||||
[tools]
|
|
||||||
# Runtimes
|
|
||||||
go = "1.26.4"
|
|
||||||
node = "24"
|
|
||||||
pnpm = "11.9.0"
|
|
||||||
|
|
||||||
# Dev tools
|
|
||||||
golangci-lint = "2.12.2"
|
|
||||||
mockery = "3.7.1"
|
|
||||||
gotestsum = "latest"
|
|
||||||
goreleaser = "2"
|
|
||||||
"go:golang.org/x/exp/cmd/gorelease" = "latest"
|
|
||||||
12
requires.go
12
requires.go
@@ -3,8 +3,6 @@ package task
|
|||||||
import (
|
import (
|
||||||
"slices"
|
"slices"
|
||||||
|
|
||||||
"github.com/elliotchance/orderedmap/v3"
|
|
||||||
|
|
||||||
"github.com/go-task/task/v3/errors"
|
"github.com/go-task/task/v3/errors"
|
||||||
"github.com/go-task/task/v3/internal/input"
|
"github.com/go-task/task/v3/internal/input"
|
||||||
"github.com/go-task/task/v3/internal/term"
|
"github.com/go-task/task/v3/internal/term"
|
||||||
@@ -34,7 +32,7 @@ func (e *Executor) promptDepsVars(calls []*Call) error {
|
|||||||
|
|
||||||
// Collect all missing vars from the dependency tree
|
// Collect all missing vars from the dependency tree
|
||||||
visited := make(map[string]bool)
|
visited := make(map[string]bool)
|
||||||
varsMap := orderedmap.NewOrderedMap[string, *ast.VarsWithValidation]()
|
varsMap := make(map[string]*ast.VarsWithValidation)
|
||||||
|
|
||||||
var collect func(call *Call) error
|
var collect func(call *Call) error
|
||||||
collect = func(call *Call) error {
|
collect = func(call *Call) error {
|
||||||
@@ -44,8 +42,8 @@ func (e *Executor) promptDepsVars(calls []*Call) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, v := range getMissingRequiredVars(compiledTask) {
|
for _, v := range getMissingRequiredVars(compiledTask) {
|
||||||
if !varsMap.Has(v.Name) {
|
if _, exists := varsMap[v.Name]; !exists {
|
||||||
varsMap.Set(v.Name, v)
|
varsMap[v.Name] = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,14 +73,14 @@ func (e *Executor) promptDepsVars(calls []*Call) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if varsMap.Len() == 0 {
|
if len(varsMap) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
prompter := e.newPrompter()
|
prompter := e.newPrompter()
|
||||||
e.promptedVars = ast.NewVars()
|
e.promptedVars = ast.NewVars()
|
||||||
|
|
||||||
for v := range varsMap.Values() {
|
for _, v := range varsMap {
|
||||||
value, err := prompter.Prompt(v.Name, getEnumValues(v.Enum))
|
value, err := prompter.Prompt(v.Name, getEnumValues(v.Enum))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, input.ErrCancelled) {
|
if errors.Is(err, input.ErrCancelled) {
|
||||||
|
|||||||
28
setup.go
28
setup.go
@@ -1,12 +1,12 @@
|
|||||||
package task
|
package task
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"cmp"
|
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"slices"
|
"slices"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/sajari/fuzzy"
|
"github.com/sajari/fuzzy"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/errors"
|
"github.com/go-task/task/v3/errors"
|
||||||
|
"github.com/go-task/task/v3/internal/env"
|
||||||
"github.com/go-task/task/v3/internal/execext"
|
"github.com/go-task/task/v3/internal/execext"
|
||||||
"github.com/go-task/task/v3/internal/filepathext"
|
"github.com/go-task/task/v3/internal/filepathext"
|
||||||
"github.com/go-task/task/v3/internal/logger"
|
"github.com/go-task/task/v3/internal/logger"
|
||||||
@@ -133,9 +134,13 @@ func (e *Executor) setupTempDir() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// e.TempDirPath carries the resolved CLI precedence (flag > TASK_TEMP_DIR > taskrc).
|
tempDir := env.GetTaskEnv("TEMP_DIR")
|
||||||
tempDir := cmp.Or(e.TempDirPath, ".task")
|
if tempDir == "" {
|
||||||
if filepath.IsAbs(tempDir) || strings.HasPrefix(tempDir, "~") {
|
e.TempDir = TempDir{
|
||||||
|
Remote: filepathext.SmartJoin(e.Dir, ".task"),
|
||||||
|
Fingerprint: filepathext.SmartJoin(e.Dir, ".task"),
|
||||||
|
}
|
||||||
|
} else if filepath.IsAbs(tempDir) || strings.HasPrefix(tempDir, "~") {
|
||||||
tempDir, err := execext.ExpandLiteral(tempDir)
|
tempDir, err := execext.ExpandLiteral(tempDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -198,12 +203,27 @@ func (e *Executor) setupOutput() error {
|
|||||||
if !e.OutputStyle.IsSet() {
|
if !e.OutputStyle.IsSet() {
|
||||||
e.OutputStyle = e.Taskfile.Output
|
e.OutputStyle = e.Taskfile.Output
|
||||||
}
|
}
|
||||||
|
if !e.OutputStyle.IsSet() && e.OutputCIAuto {
|
||||||
|
if name := detectCIOutput(); name != "" {
|
||||||
|
e.OutputStyle.Name = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
e.Output, err = output.BuildFor(&e.OutputStyle, e.Logger)
|
e.Output, err = output.BuildFor(&e.OutputStyle, e.Logger)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// detectCIOutput returns the name of a CI-aware output style to use based
|
||||||
|
// on environment variables set by common CI runners. Returns an empty string
|
||||||
|
// when no supported CI environment is detected.
|
||||||
|
func detectCIOutput() string {
|
||||||
|
if isGitLab, _ := strconv.ParseBool(os.Getenv("GITLAB_CI")); isGitLab {
|
||||||
|
return "gitlab"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func (e *Executor) setupCompiler() error {
|
func (e *Executor) setupCompiler() error {
|
||||||
if e.UserWorkingDir == "" {
|
if e.UserWorkingDir == "" {
|
||||||
var err error
|
var err error
|
||||||
|
|||||||
97
setup_test.go
Normal file
97
setup_test.go
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
package task
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/go-task/task/v3/internal/logger"
|
||||||
|
"github.com/go-task/task/v3/taskfile/ast"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDetectCIOutput(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
env map[string]string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{name: "no CI detected", env: nil, want: ""},
|
||||||
|
{name: "GITLAB_CI=true", env: map[string]string{"GITLAB_CI": "true"}, want: "gitlab"},
|
||||||
|
{name: "GITLAB_CI=1", env: map[string]string{"GITLAB_CI": "1"}, want: "gitlab"},
|
||||||
|
{name: "GITLAB_CI=false", env: map[string]string{"GITLAB_CI": "false"}, want: ""},
|
||||||
|
{name: "GITLAB_CI empty", env: map[string]string{"GITLAB_CI": ""}, want: ""},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Setenv("GITLAB_CI", "") // reset
|
||||||
|
for k, v := range tc.env {
|
||||||
|
t.Setenv(k, v)
|
||||||
|
}
|
||||||
|
assert.Equal(t, tc.want, detectCIOutput())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetupOutputPriority(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
cliStyle ast.Output
|
||||||
|
taskfileStyle ast.Output
|
||||||
|
ciAuto bool
|
||||||
|
gitlabEnv string
|
||||||
|
wantName string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "CLI wins over everything",
|
||||||
|
cliStyle: ast.Output{Name: "prefixed"},
|
||||||
|
taskfileStyle: ast.Output{Name: "group", Group: ast.OutputGroup{
|
||||||
|
Begin: "b", End: "e",
|
||||||
|
}},
|
||||||
|
ciAuto: true,
|
||||||
|
gitlabEnv: "true",
|
||||||
|
wantName: "prefixed",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Taskfile wins over auto-detect",
|
||||||
|
taskfileStyle: ast.Output{Name: "prefixed"},
|
||||||
|
ciAuto: true,
|
||||||
|
gitlabEnv: "true",
|
||||||
|
wantName: "prefixed",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "auto-detect activates when nothing explicit",
|
||||||
|
ciAuto: true,
|
||||||
|
gitlabEnv: "true",
|
||||||
|
wantName: "gitlab",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "auto-detect disabled does nothing",
|
||||||
|
ciAuto: false,
|
||||||
|
gitlabEnv: "true",
|
||||||
|
wantName: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "auto-detect without CI env does nothing",
|
||||||
|
ciAuto: true,
|
||||||
|
gitlabEnv: "",
|
||||||
|
wantName: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Setenv("GITLAB_CI", tc.gitlabEnv)
|
||||||
|
|
||||||
|
e := &Executor{
|
||||||
|
OutputStyle: tc.cliStyle,
|
||||||
|
OutputCIAuto: tc.ciAuto,
|
||||||
|
Taskfile: &ast.Taskfile{Output: tc.taskfileStyle},
|
||||||
|
Logger: &logger.Logger{},
|
||||||
|
}
|
||||||
|
require.NoError(t, e.setupOutput())
|
||||||
|
assert.Equal(t, tc.wantName, e.OutputStyle.Name)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
16
task.go
16
task.go
@@ -204,9 +204,9 @@ func (e *Executor) RunTask(ctx context.Context, call *Call) error {
|
|||||||
release := e.acquireConcurrencyLimit()
|
release := e.acquireConcurrencyLimit()
|
||||||
defer release()
|
defer release()
|
||||||
|
|
||||||
if err = e.startExecution(ctx, t, func(ctx context.Context) error {
|
if err = e.startExecution(ctx, t, func(ctx context.Context) (err 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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,6 +266,9 @@ func (e *Executor) RunTask(ctx context.Context, call *Call) error {
|
|||||||
|
|
||||||
var deferredExitCode uint8
|
var deferredExitCode uint8
|
||||||
|
|
||||||
|
ctx, taskOutCloser := e.wrapTaskOutput(ctx, t, call)
|
||||||
|
defer func() { taskOutCloser(err) }()
|
||||||
|
|
||||||
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, t.Vars, &deferredExitCode)
|
defer e.runDeferred(t, call, i, t.Vars, &deferredExitCode)
|
||||||
@@ -349,8 +352,6 @@ func (e *Executor) runDeferred(t *ast.Task, call *Call, i int, vars *ast.Vars, d
|
|||||||
extra["EXIT_CODE"] = fmt.Sprintf("%d", *deferredExitCode)
|
extra["EXIT_CODE"] = fmt.Sprintf("%d", *deferredExitCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve template with secrets masked for logging
|
|
||||||
cmd.LogCmd = templater.MaskSecretsWithExtra(cmd.Cmd, vars, extra)
|
|
||||||
cmd.Cmd = templater.ReplaceWithExtra(cmd.Cmd, cache, extra)
|
cmd.Cmd = templater.ReplaceWithExtra(cmd.Cmd, cache, extra)
|
||||||
cmd.Task = templater.ReplaceWithExtra(cmd.Task, cache, extra)
|
cmd.Task = templater.ReplaceWithExtra(cmd.Task, cache, extra)
|
||||||
cmd.If = templater.ReplaceWithExtra(cmd.If, cache, extra)
|
cmd.If = templater.ReplaceWithExtra(cmd.If, cache, extra)
|
||||||
@@ -390,12 +391,12 @@ func (e *Executor) runCommand(ctx context.Context, t *ast.Task, call *Call, i in
|
|||||||
return err
|
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.LogCmd)
|
e.Logger.VerboseOutf(logger.Yellow, "task: [%s] %s not for current platform - ignored\n", t.Name(), cmd.Cmd)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.Verbose || (!call.Silent && !cmd.Silent && !t.IsSilent() && !e.Taskfile.Silent && !e.Silent) {
|
if e.Verbose || (!call.Silent && !cmd.Silent && !t.IsSilent() && !e.Taskfile.Silent && !e.Silent) {
|
||||||
e.Logger.Errf(logger.Green, "task: [%s] %s\n", t.Name(), cmd.LogCmd)
|
e.printCmdAnnouncement(ctx, t, cmd.Cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.Dry {
|
if e.Dry {
|
||||||
@@ -411,7 +412,8 @@ func (e *Executor) runCommand(ctx context.Context, t *ast.Task, call *Call, i in
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("task: failed to get variables: %w", err)
|
return fmt.Errorf("task: failed to get variables: %w", err)
|
||||||
}
|
}
|
||||||
stdOut, stdErr, closer := outputWrapper.WrapWriter(e.Stdout, e.Stderr, t.Prefix, outputTemplater)
|
taskStdOut, taskStdErr := e.writersFromCtx(ctx)
|
||||||
|
stdOut, stdErr, closer := outputWrapper.WrapWriter(taskStdOut, taskStdErr, t.Prefix, outputTemplater)
|
||||||
|
|
||||||
err = execext.RunCommand(ctx, &execext.RunCommandOptions{
|
err = execext.RunCommand(ctx, &execext.RunCommandOptions{
|
||||||
Command: cmd.Cmd,
|
Command: cmd.Cmd,
|
||||||
|
|||||||
70
task_output.go
Normal file
70
task_output.go
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
package task
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/fatih/color"
|
||||||
|
|
||||||
|
"github.com/go-task/task/v3/internal/logger"
|
||||||
|
"github.com/go-task/task/v3/internal/output"
|
||||||
|
"github.com/go-task/task/v3/internal/templater"
|
||||||
|
"github.com/go-task/task/v3/taskfile/ast"
|
||||||
|
)
|
||||||
|
|
||||||
|
type taskWritersKey struct{}
|
||||||
|
|
||||||
|
type taskWriters struct {
|
||||||
|
stdout, stderr io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
// writersFromCtx returns the task-scoped writers if set, otherwise the
|
||||||
|
// Executor's own stdout/stderr.
|
||||||
|
func (e *Executor) writersFromCtx(ctx context.Context) (io.Writer, io.Writer) {
|
||||||
|
if tw, ok := ctx.Value(taskWritersKey{}).(*taskWriters); ok && tw != nil {
|
||||||
|
return tw.stdout, tw.stderr
|
||||||
|
}
|
||||||
|
return e.Stdout, e.Stderr
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrapTaskOutput wraps a task's output in a task-scoped block if e.Output
|
||||||
|
// implements [output.TaskWrapper] and the task is not interactive. Returns
|
||||||
|
// the (possibly updated) ctx and a closer that flushes the block. The closer
|
||||||
|
// is always safe to call — it is a no-op when no wrapping took place.
|
||||||
|
func (e *Executor) wrapTaskOutput(ctx context.Context, t *ast.Task, call *Call) (context.Context, func(error)) {
|
||||||
|
noop := func(error) {}
|
||||||
|
if t.Interactive {
|
||||||
|
return ctx, noop
|
||||||
|
}
|
||||||
|
tw, ok := e.Output.(output.TaskWrapper)
|
||||||
|
if !ok {
|
||||||
|
return ctx, noop
|
||||||
|
}
|
||||||
|
stdOut, stdErr := e.writersFromCtx(ctx)
|
||||||
|
vars, err := e.Compiler.FastGetVariables(t, call)
|
||||||
|
if err != nil {
|
||||||
|
e.Logger.VerboseErrf(logger.Yellow, "task: output setup: %v\n", err)
|
||||||
|
return ctx, noop
|
||||||
|
}
|
||||||
|
wOut, wErr, closer := tw.WrapTask(stdOut, stdErr, &templater.Cache{Vars: vars})
|
||||||
|
ctx = context.WithValue(ctx, taskWritersKey{}, &taskWriters{stdout: wOut, stderr: wErr})
|
||||||
|
return ctx, func(loopErr error) {
|
||||||
|
if err := closer(loopErr); err != nil {
|
||||||
|
e.Logger.Errf(logger.Red, "task: output close: %v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// printCmdAnnouncement prints the "task: [NAME] CMD" line using the
|
||||||
|
// task-scoped stderr if available, so the announcement ends up inside the
|
||||||
|
// task's output block.
|
||||||
|
func (e *Executor) printCmdAnnouncement(ctx context.Context, t *ast.Task, cmdStr string) {
|
||||||
|
_, stdErr := e.writersFromCtx(ctx)
|
||||||
|
if stdErr == e.Stderr {
|
||||||
|
// No task-scoped writer — fall back to the Logger to preserve existing
|
||||||
|
// behavior (respects Logger's color config, etc.).
|
||||||
|
e.Logger.Errf(logger.Green, "task: [%s] %s\n", t.Name(), cmdStr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, _ = color.New(color.FgGreen).Fprintf(stdErr, "task: [%s] %s\n", t.Name(), cmdStr)
|
||||||
|
}
|
||||||
158
task_test.go
158
task_test.go
@@ -653,164 +653,6 @@ func TestStatusChecksumMissingGenerated(t *testing.T) { // nolint:paralleltest /
|
|||||||
require.NoError(t, err, "generated.txt should be recreated after third run")
|
require.NoError(t, err, "generated.txt should be recreated after third run")
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeFile(t *testing.T, dir, name, content string) {
|
|
||||||
t.Helper()
|
|
||||||
require.NoError(t, os.WriteFile(filepathext.SmartJoin(dir, name), []byte(content), 0o644))
|
|
||||||
}
|
|
||||||
|
|
||||||
// gitignoreStep writes a set of files then runs the task once, capturing its
|
|
||||||
// output as a golden fixture named run.
|
|
||||||
type gitignoreStep struct {
|
|
||||||
write map[string]string
|
|
||||||
run string
|
|
||||||
}
|
|
||||||
|
|
||||||
// gitignoreSeq drives a checksum task through a sequence of runs against a
|
|
||||||
// fixture dir. create seeds runtime files (removed on cleanup); restore resets
|
|
||||||
// tracked files to their committed content on cleanup; artifacts are
|
|
||||||
// task-produced files to delete on cleanup.
|
|
||||||
type gitignoreSeq struct {
|
|
||||||
dir string
|
|
||||||
task string
|
|
||||||
create map[string]string
|
|
||||||
restore map[string]string
|
|
||||||
artifacts []string
|
|
||||||
steps []gitignoreStep
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s gitignoreSeq) run(t *testing.T) {
|
|
||||||
t.Helper()
|
|
||||||
cleanup := func() {
|
|
||||||
_ = os.RemoveAll(filepathext.SmartJoin(s.dir, ".task"))
|
|
||||||
for name := range s.create {
|
|
||||||
_ = os.Remove(filepathext.SmartJoin(s.dir, name))
|
|
||||||
}
|
|
||||||
for _, name := range s.artifacts {
|
|
||||||
_ = os.Remove(filepathext.SmartJoin(s.dir, name))
|
|
||||||
}
|
|
||||||
for name, content := range s.restore {
|
|
||||||
writeFile(t, s.dir, name, content)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cleanup()
|
|
||||||
t.Cleanup(cleanup)
|
|
||||||
for name, content := range s.create {
|
|
||||||
writeFile(t, s.dir, name, content)
|
|
||||||
}
|
|
||||||
for _, step := range s.steps {
|
|
||||||
for name, content := range step.write {
|
|
||||||
writeFile(t, s.dir, name, content)
|
|
||||||
}
|
|
||||||
NewExecutorTest(t,
|
|
||||||
WithName(step.run),
|
|
||||||
WithExecutorOptions(task.WithDir(s.dir)),
|
|
||||||
WithTask(s.task),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGitignoreChecksum(t *testing.T) { //nolint:paralleltest // shares testdata/gitignore and mutates fixture files
|
|
||||||
gitignoreSeq{
|
|
||||||
dir: "testdata/gitignore",
|
|
||||||
task: "build",
|
|
||||||
create: map[string]string{"ignored.txt": "ignored\n"},
|
|
||||||
restore: map[string]string{"source.txt": "source content\n"},
|
|
||||||
artifacts: []string{"generated.txt"},
|
|
||||||
steps: []gitignoreStep{
|
|
||||||
{run: "first run"},
|
|
||||||
{run: "up to date"},
|
|
||||||
{run: "ignored file modified", write: map[string]string{"ignored.txt": "ignored modified\n"}},
|
|
||||||
{run: "source file modified", write: map[string]string{"source.txt": "source modified\n"}},
|
|
||||||
},
|
|
||||||
}.run(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestGitignoreNegation checks that a `!pattern` in a nested .gitignore
|
|
||||||
// re-includes a file excluded by a parent .gitignore.
|
|
||||||
func TestGitignoreNegation(t *testing.T) { //nolint:paralleltest // mutates fixture files
|
|
||||||
gitignoreSeq{
|
|
||||||
dir: "testdata/gitignore_negation",
|
|
||||||
task: "build",
|
|
||||||
create: map[string]string{"sub/debug.log": "debug\n", "sub/other.log": "other\n"},
|
|
||||||
steps: []gitignoreStep{
|
|
||||||
{run: "first run"},
|
|
||||||
{run: "up to date"},
|
|
||||||
{run: "ignored file modified", write: map[string]string{"sub/other.log": "other modified\n"}},
|
|
||||||
{run: "reincluded file modified", write: map[string]string{"sub/debug.log": "debug modified\n"}},
|
|
||||||
},
|
|
||||||
}.run(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestGitignoreNested checks that a .gitignore in a subdirectory below the task
|
|
||||||
// dir is honored when its files are reached by a deep glob.
|
|
||||||
func TestGitignoreNested(t *testing.T) { //nolint:paralleltest // mutates fixture files
|
|
||||||
gitignoreSeq{
|
|
||||||
dir: "testdata/gitignore_nested",
|
|
||||||
task: "build",
|
|
||||||
create: map[string]string{"sub/secret.dat": "secret\n"},
|
|
||||||
restore: map[string]string{"sub/keep.txt": "keep\n"},
|
|
||||||
steps: []gitignoreStep{
|
|
||||||
{run: "first run"},
|
|
||||||
{run: "up to date"},
|
|
||||||
{run: "ignored file modified", write: map[string]string{"sub/secret.dat": "secret modified\n"}},
|
|
||||||
{run: "source file modified", write: map[string]string{"sub/keep.txt": "keep modified\n"}},
|
|
||||||
},
|
|
||||||
}.run(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestGitignoreIncluded checks that a top-level use_gitignore in an included
|
|
||||||
// Taskfile is propagated onto its tasks during merge.
|
|
||||||
func TestGitignoreIncluded(t *testing.T) { //nolint:paralleltest // mutates fixture files
|
|
||||||
gitignoreSeq{
|
|
||||||
dir: "testdata/gitignore_included",
|
|
||||||
task: "included:build",
|
|
||||||
create: map[string]string{"ignored.txt": "ignored\n"},
|
|
||||||
steps: []gitignoreStep{
|
|
||||||
{run: "first run"},
|
|
||||||
{run: "up to date"},
|
|
||||||
{run: "ignored file modified", write: map[string]string{"ignored.txt": "ignored modified\n"}},
|
|
||||||
},
|
|
||||||
}.run(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestGitignoreIncludedOverride checks that an explicit use_gitignore: false in
|
|
||||||
// an included Taskfile is preserved even when the root Taskfile sets it to true.
|
|
||||||
func TestGitignoreIncludedOverride(t *testing.T) { //nolint:paralleltest // mutates fixture files
|
|
||||||
gitignoreSeq{
|
|
||||||
dir: "testdata/gitignore_included_override",
|
|
||||||
task: "included:build",
|
|
||||||
create: map[string]string{"ignored.txt": "ignored\n"},
|
|
||||||
steps: []gitignoreStep{
|
|
||||||
{run: "first run"},
|
|
||||||
{run: "up to date"},
|
|
||||||
{run: "ignored file modified", write: map[string]string{"ignored.txt": "ignored modified\n"}},
|
|
||||||
},
|
|
||||||
}.run(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGitignoreTaskListFallback(t *testing.T) { //nolint:paralleltest // shares testdata/gitignore with TestGitignoreChecksum
|
|
||||||
const dir = "testdata/gitignore"
|
|
||||||
|
|
||||||
var buff bytes.Buffer
|
|
||||||
e := task.NewExecutor(
|
|
||||||
task.WithDir(dir),
|
|
||||||
task.WithStdout(&buff),
|
|
||||||
task.WithStderr(&buff),
|
|
||||||
)
|
|
||||||
require.NoError(t, e.Setup())
|
|
||||||
|
|
||||||
listed, err := e.CompiledTaskForTaskList(&task.Call{Task: "build"})
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.True(t, listed.ShouldUseGitignore(),
|
|
||||||
"task list should reflect the global use_gitignore fallback")
|
|
||||||
|
|
||||||
// "build-no-use_gitignore" explicitly disables it.
|
|
||||||
listedOff, err := e.CompiledTaskForTaskList(&task.Call{Task: "build-no-use_gitignore"})
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.False(t, listedOff.ShouldUseGitignore(),
|
|
||||||
"explicit use_gitignore: false must be preserved in the list path")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStatusVariables(t *testing.T) {
|
func TestStatusVariables(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,7 @@ import (
|
|||||||
|
|
||||||
// Cmd is a task command
|
// Cmd is a task command
|
||||||
type Cmd struct {
|
type Cmd struct {
|
||||||
Cmd string // Resolved command (used for execution and fingerprinting)
|
Cmd string
|
||||||
LogCmd string // Command with secrets masked (used for logging)
|
|
||||||
Task string
|
Task string
|
||||||
For *For
|
For *For
|
||||||
If string
|
If string
|
||||||
@@ -29,7 +28,6 @@ func (c *Cmd) DeepCopy() *Cmd {
|
|||||||
}
|
}
|
||||||
return &Cmd{
|
return &Cmd{
|
||||||
Cmd: c.Cmd,
|
Cmd: c.Cmd,
|
||||||
LogCmd: c.LogCmd,
|
|
||||||
Task: c.Task,
|
Task: c.Task,
|
||||||
For: c.For.DeepCopy(),
|
For: c.For.DeepCopy(),
|
||||||
If: c.If,
|
If: c.If,
|
||||||
|
|||||||
@@ -93,21 +93,6 @@ func (matrix *Matrix) DeepCopy() *Matrix {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopy returns a copy of the MatrixRow. Without this, deepcopy.OrderedMap
|
|
||||||
// falls back to copying the *MatrixRow pointer as-is, so every "copy" of a
|
|
||||||
// Matrix would still share the same underlying rows - see #2890, where
|
|
||||||
// concurrent invocations of a task with a `ref:` matrix row raced on
|
|
||||||
// resolveMatrixRefs mutating that shared row.
|
|
||||||
func (row *MatrixRow) DeepCopy() *MatrixRow {
|
|
||||||
if row == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return &MatrixRow{
|
|
||||||
Ref: row.Ref,
|
|
||||||
Value: deepcopy.Slice(row.Value),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (matrix *Matrix) UnmarshalYAML(node *yaml.Node) error {
|
func (matrix *Matrix) UnmarshalYAML(node *yaml.Node) error {
|
||||||
switch node.Kind {
|
switch node.Kind {
|
||||||
case yaml.MappingNode:
|
case yaml.MappingNode:
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ type Output struct {
|
|||||||
Name string `yaml:"-"`
|
Name string `yaml:"-"`
|
||||||
// Group specific style
|
// Group specific style
|
||||||
Group OutputGroup
|
Group OutputGroup
|
||||||
|
// GitLab specific style
|
||||||
|
GitLab OutputGitLab
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsSet returns true if and only if a custom output style is set.
|
// IsSet returns true if and only if a custom output style is set.
|
||||||
@@ -33,18 +35,29 @@ func (s *Output) UnmarshalYAML(node *yaml.Node) error {
|
|||||||
case yaml.MappingNode:
|
case yaml.MappingNode:
|
||||||
var tmp struct {
|
var tmp struct {
|
||||||
Group *OutputGroup
|
Group *OutputGroup
|
||||||
|
GitLab *OutputGitLab `yaml:"gitlab"`
|
||||||
}
|
}
|
||||||
if err := node.Decode(&tmp); err != nil {
|
if err := node.Decode(&tmp); err != nil {
|
||||||
return errors.NewTaskfileDecodeError(err, node)
|
return errors.NewTaskfileDecodeError(err, node)
|
||||||
}
|
}
|
||||||
if tmp.Group == nil {
|
switch {
|
||||||
return errors.NewTaskfileDecodeError(nil, node).WithMessage(`output style must have the "group" key when in mapping form`)
|
case tmp.Group != nil && tmp.GitLab != nil:
|
||||||
}
|
return errors.NewTaskfileDecodeError(nil, node).WithMessage(`output style cannot set both "group" and "gitlab"`)
|
||||||
|
case tmp.Group != nil:
|
||||||
*s = Output{
|
*s = Output{
|
||||||
Name: "group",
|
Name: "group",
|
||||||
Group: *tmp.Group,
|
Group: *tmp.Group,
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
case tmp.GitLab != nil:
|
||||||
|
*s = Output{
|
||||||
|
Name: "gitlab",
|
||||||
|
GitLab: *tmp.GitLab,
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return errors.NewTaskfileDecodeError(nil, node).WithMessage(`output style must have the "group" or "gitlab" key when in mapping form`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors.NewTaskfileDecodeError(nil, node).WithTypeMessage("output")
|
return errors.NewTaskfileDecodeError(nil, node).WithTypeMessage("output")
|
||||||
@@ -63,3 +76,9 @@ func (g *OutputGroup) IsSet() bool {
|
|||||||
}
|
}
|
||||||
return g.Begin != "" || g.End != ""
|
return g.Begin != "" || g.End != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OutputGitLab is the style options specific to the GitLab style.
|
||||||
|
type OutputGitLab struct {
|
||||||
|
Collapsed bool
|
||||||
|
ErrorOnly bool `yaml:"error_only"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ type Task struct {
|
|||||||
Method string
|
Method string
|
||||||
Prefix string `hash:"ignore"`
|
Prefix string `hash:"ignore"`
|
||||||
IgnoreError bool
|
IgnoreError bool
|
||||||
UseGitignore *bool
|
|
||||||
Run string
|
Run string
|
||||||
Platforms []*Platform
|
Platforms []*Platform
|
||||||
If string
|
If string
|
||||||
@@ -76,12 +75,6 @@ func (t *Task) IsSilent() bool {
|
|||||||
return t.Silent != nil && *t.Silent
|
return t.Silent != nil && *t.Silent
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShouldUseGitignore returns true if gitignore filtering is enabled for the task.
|
|
||||||
// Returns false if UseGitignore is nil or set to false.
|
|
||||||
func (t *Task) ShouldUseGitignore() bool {
|
|
||||||
return t.UseGitignore != nil && *t.UseGitignore
|
|
||||||
}
|
|
||||||
|
|
||||||
// WildcardMatch will check if the given string matches the name of the Task and returns any wildcard values.
|
// WildcardMatch will check if the given string matches the name of the Task and returns any wildcard values.
|
||||||
func (t *Task) WildcardMatch(name string) (bool, []string) {
|
func (t *Task) WildcardMatch(name string) (bool, []string) {
|
||||||
names := append([]string{t.Task}, t.Aliases...)
|
names := append([]string{t.Task}, t.Aliases...)
|
||||||
@@ -157,7 +150,6 @@ func (t *Task) UnmarshalYAML(node *yaml.Node) error {
|
|||||||
Method string
|
Method string
|
||||||
Prefix string
|
Prefix string
|
||||||
IgnoreError bool `yaml:"ignore_error"`
|
IgnoreError bool `yaml:"ignore_error"`
|
||||||
UseGitignore *bool `yaml:"use_gitignore,omitempty"`
|
|
||||||
Run string
|
Run string
|
||||||
Platforms []*Platform
|
Platforms []*Platform
|
||||||
If string
|
If string
|
||||||
@@ -198,7 +190,6 @@ func (t *Task) UnmarshalYAML(node *yaml.Node) error {
|
|||||||
t.Method = task.Method
|
t.Method = task.Method
|
||||||
t.Prefix = task.Prefix
|
t.Prefix = task.Prefix
|
||||||
t.IgnoreError = task.IgnoreError
|
t.IgnoreError = task.IgnoreError
|
||||||
t.UseGitignore = deepcopy.Scalar(task.UseGitignore)
|
|
||||||
t.Run = task.Run
|
t.Run = task.Run
|
||||||
t.Platforms = task.Platforms
|
t.Platforms = task.Platforms
|
||||||
t.If = task.If
|
t.If = task.If
|
||||||
@@ -242,7 +233,6 @@ func (t *Task) DeepCopy() *Task {
|
|||||||
Method: t.Method,
|
Method: t.Method,
|
||||||
Prefix: t.Prefix,
|
Prefix: t.Prefix,
|
||||||
IgnoreError: t.IgnoreError,
|
IgnoreError: t.IgnoreError,
|
||||||
UseGitignore: deepcopy.Scalar(t.UseGitignore),
|
|
||||||
Run: t.Run,
|
Run: t.Run,
|
||||||
IncludeVars: t.IncludeVars.DeepCopy(),
|
IncludeVars: t.IncludeVars.DeepCopy(),
|
||||||
IncludedTaskfileVars: t.IncludedTaskfileVars.DeepCopy(),
|
IncludedTaskfileVars: t.IncludedTaskfileVars.DeepCopy(),
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ type Taskfile struct {
|
|||||||
Dotenv []string
|
Dotenv []string
|
||||||
Run string
|
Run string
|
||||||
Interval time.Duration
|
Interval time.Duration
|
||||||
UseGitignore *bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge merges the second Taskfile into the first
|
// Merge merges the second Taskfile into the first
|
||||||
@@ -68,14 +67,6 @@ func (t1 *Taskfile) Merge(t2 *Taskfile, include *Include) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if t2.UseGitignore != nil {
|
|
||||||
for _, t := range t2.Tasks.All(nil) {
|
|
||||||
if t.UseGitignore == nil {
|
|
||||||
v := *t2.UseGitignore
|
|
||||||
t.UseGitignore = &v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t1.Vars.Merge(t2.Vars, include)
|
t1.Vars.Merge(t2.Vars, include)
|
||||||
t1.Env.Merge(t2.Env, include)
|
t1.Env.Merge(t2.Env, include)
|
||||||
return t1.Tasks.Merge(t2.Tasks, include, t1.Vars)
|
return t1.Tasks.Merge(t2.Tasks, include, t1.Vars)
|
||||||
@@ -98,7 +89,6 @@ func (tf *Taskfile) UnmarshalYAML(node *yaml.Node) error {
|
|||||||
Dotenv []string
|
Dotenv []string
|
||||||
Run string
|
Run string
|
||||||
Interval time.Duration
|
Interval time.Duration
|
||||||
UseGitignore *bool `yaml:"use_gitignore"`
|
|
||||||
}
|
}
|
||||||
if err := node.Decode(&taskfile); err != nil {
|
if err := node.Decode(&taskfile); err != nil {
|
||||||
return errors.NewTaskfileDecodeError(err, node)
|
return errors.NewTaskfileDecodeError(err, node)
|
||||||
@@ -116,7 +106,6 @@ func (tf *Taskfile) UnmarshalYAML(node *yaml.Node) error {
|
|||||||
tf.Dotenv = taskfile.Dotenv
|
tf.Dotenv = taskfile.Dotenv
|
||||||
tf.Run = taskfile.Run
|
tf.Run = taskfile.Run
|
||||||
tf.Interval = taskfile.Interval
|
tf.Interval = taskfile.Interval
|
||||||
tf.UseGitignore = taskfile.UseGitignore
|
|
||||||
if tf.Includes == nil {
|
if tf.Includes == nil {
|
||||||
tf.Includes = NewIncludes()
|
tf.Includes = NewIncludes()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,49 +13,32 @@ type Var struct {
|
|||||||
Sh *string
|
Sh *string
|
||||||
Ref string
|
Ref string
|
||||||
Dir string
|
Dir string
|
||||||
Secret bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Var) UnmarshalYAML(node *yaml.Node) error {
|
func (v *Var) UnmarshalYAML(node *yaml.Node) error {
|
||||||
switch node.Kind {
|
switch node.Kind {
|
||||||
case yaml.MappingNode:
|
case yaml.MappingNode:
|
||||||
|
key := "<none>"
|
||||||
|
if len(node.Content) > 0 {
|
||||||
|
key = node.Content[0].Value
|
||||||
|
}
|
||||||
|
switch key {
|
||||||
|
case "sh", "ref", "map":
|
||||||
var m struct {
|
var m struct {
|
||||||
Sh *string
|
Sh *string
|
||||||
Ref string
|
Ref string
|
||||||
Map any
|
Map any
|
||||||
Value any
|
|
||||||
Secret bool
|
|
||||||
}
|
}
|
||||||
if err := node.Decode(&m); err != nil {
|
if err := node.Decode(&m); err != nil {
|
||||||
return errors.NewTaskfileDecodeError(err, node)
|
return errors.NewTaskfileDecodeError(err, node)
|
||||||
}
|
}
|
||||||
// Validate the keys regardless of their order: every key must be known
|
|
||||||
// and at least one type-defining key must be present. "secret" is a
|
|
||||||
// modifier, not a type, so it can appear in any position.
|
|
||||||
hasType := false
|
|
||||||
for i := 0; i+1 < len(node.Content); i += 2 {
|
|
||||||
switch node.Content[i].Value {
|
|
||||||
case "sh", "ref", "map", "value":
|
|
||||||
hasType = true
|
|
||||||
case "secret":
|
|
||||||
// modifier, not a type
|
|
||||||
default:
|
|
||||||
return errors.NewTaskfileDecodeError(nil, node).WithMessage(`%q is not a valid variable type. Try "sh", "ref", "map", "value" or using a scalar value`, node.Content[i].Value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !hasType {
|
|
||||||
return errors.NewTaskfileDecodeError(nil, node).WithMessage(`a variable must define one of "sh", "ref", "map", "value" or be a scalar value`)
|
|
||||||
}
|
|
||||||
v.Sh = m.Sh
|
v.Sh = m.Sh
|
||||||
v.Ref = m.Ref
|
v.Ref = m.Ref
|
||||||
v.Secret = m.Secret
|
|
||||||
// Handle both "map" and "value" keys
|
|
||||||
if m.Map != nil {
|
|
||||||
v.Value = m.Map
|
v.Value = m.Map
|
||||||
} else if m.Value != nil {
|
|
||||||
v.Value = m.Value
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
|
default:
|
||||||
|
return errors.NewTaskfileDecodeError(nil, node).WithMessage(`%q is not a valid variable type. Try "sh", "ref", "map" or using a scalar value`, key)
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
var value any
|
var value any
|
||||||
if err := node.Decode(&value); err != nil {
|
if err := node.Decode(&value); err != nil {
|
||||||
|
|||||||
@@ -98,9 +98,6 @@ func (vars *Vars) Values() iter.Seq[Var] {
|
|||||||
// ToCacheMap converts Vars to an unordered map containing only the static
|
// ToCacheMap converts Vars to an unordered map containing only the static
|
||||||
// variables
|
// variables
|
||||||
func (vars *Vars) ToCacheMap() (m map[string]any) {
|
func (vars *Vars) ToCacheMap() (m map[string]any) {
|
||||||
if vars == nil || vars.om == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
defer vars.mutex.RUnlock()
|
defer vars.mutex.RUnlock()
|
||||||
vars.mutex.RLock()
|
vars.mutex.RLock()
|
||||||
m = make(map[string]any, vars.Len())
|
m = make(map[string]any, vars.Len())
|
||||||
|
|||||||
@@ -1,55 +0,0 @@
|
|||||||
package ast
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestVars_ToCacheMap(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
t.Run("nil receiver returns nil", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
var vars *Vars
|
|
||||||
assert.Nil(t, vars.ToCacheMap())
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("empty vars returns empty map", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
vars := NewVars()
|
|
||||||
m := vars.ToCacheMap()
|
|
||||||
assert.NotNil(t, m)
|
|
||||||
assert.Empty(t, m)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("static values are included", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
vars := NewVars(
|
|
||||||
&VarElement{Key: "FOO", Value: Var{Value: "bar"}},
|
|
||||||
&VarElement{Key: "NUM", Value: Var{Value: 42}},
|
|
||||||
)
|
|
||||||
m := vars.ToCacheMap()
|
|
||||||
assert.Equal(t, map[string]any{"FOO": "bar", "NUM": 42}, m)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("live values take precedence over static values", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
vars := NewVars(
|
|
||||||
&VarElement{Key: "FOO", Value: Var{Value: "bar", Live: "live-bar"}},
|
|
||||||
)
|
|
||||||
m := vars.ToCacheMap()
|
|
||||||
assert.Equal(t, map[string]any{"FOO": "live-bar"}, m)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("dynamic variables are excluded", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
sh := "echo hello"
|
|
||||||
vars := NewVars(
|
|
||||||
&VarElement{Key: "STATIC", Value: Var{Value: "ok"}},
|
|
||||||
&VarElement{Key: "DYNAMIC", Value: Var{Sh: &sh}},
|
|
||||||
)
|
|
||||||
m := vars.ToCacheMap()
|
|
||||||
assert.Equal(t, map[string]any{"STATIC": "ok"}, m)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -73,7 +73,7 @@ func NewNode(
|
|||||||
return node, err
|
return node, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsRemoteEntrypoint(entrypoint string) bool {
|
func isRemoteEntrypoint(entrypoint string) bool {
|
||||||
scheme, _ := getScheme(entrypoint)
|
scheme, _ := getScheme(entrypoint)
|
||||||
switch scheme {
|
switch scheme {
|
||||||
case "git", "http", "https":
|
case "git", "http", "https":
|
||||||
@@ -93,8 +93,8 @@ func getScheme(uri string) (string, error) {
|
|||||||
return "git", nil
|
return "git", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if before, _, ok := strings.Cut(uri, "://"); ok {
|
if i := strings.Index(uri, "://"); i != -1 {
|
||||||
return before, nil
|
return uri[:i], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", nil
|
return "", nil
|
||||||
|
|||||||
@@ -60,7 +60,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 IsRemoteEntrypoint(entrypoint) {
|
if isRemoteEntrypoint(entrypoint) {
|
||||||
return entrypoint, nil
|
return entrypoint, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ func (node *GitNode) ReadContext(ctx 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 the file is remote, we don't need to resolve the path
|
||||||
if IsRemoteEntrypoint(entrypoint) {
|
if isRemoteEntrypoint(entrypoint) {
|
||||||
return entrypoint, nil
|
return entrypoint, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,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 IsRemoteEntrypoint(entrypoint) {
|
if isRemoteEntrypoint(entrypoint) {
|
||||||
return entrypoint, nil
|
return entrypoint, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"slices"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -286,7 +285,12 @@ func (r *Reader) isTrusted(uri string) bool {
|
|||||||
host := parsedURL.Host
|
host := parsedURL.Host
|
||||||
|
|
||||||
// Check against each trusted pattern (exact match including port if provided)
|
// Check against each trusted pattern (exact match including port if provided)
|
||||||
return slices.Contains(r.trustedHosts, host)
|
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 {
|
||||||
|
|||||||
@@ -17,9 +17,9 @@ type TaskRC struct {
|
|||||||
DisableFuzzy *bool `yaml:"disable-fuzzy"`
|
DisableFuzzy *bool `yaml:"disable-fuzzy"`
|
||||||
Concurrency *int `yaml:"concurrency"`
|
Concurrency *int `yaml:"concurrency"`
|
||||||
Interactive *bool `yaml:"interactive"`
|
Interactive *bool `yaml:"interactive"`
|
||||||
|
OutputCIAuto *bool `yaml:"output-ci-auto"`
|
||||||
Remote Remote `yaml:"remote"`
|
Remote Remote `yaml:"remote"`
|
||||||
Failfast bool `yaml:"failfast"`
|
Failfast bool `yaml:"failfast"`
|
||||||
TempDir *string `yaml:"temp-dir"`
|
|
||||||
Experiments map[string]int `yaml:"experiments"`
|
Experiments map[string]int `yaml:"experiments"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,6 +70,6 @@ func (t *TaskRC) Merge(other *TaskRC) {
|
|||||||
t.DisableFuzzy = cmp.Or(other.DisableFuzzy, t.DisableFuzzy)
|
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.Interactive = cmp.Or(other.Interactive, t.Interactive)
|
t.Interactive = cmp.Or(other.Interactive, t.Interactive)
|
||||||
|
t.OutputCIAuto = cmp.Or(other.OutputCIAuto, t.OutputCIAuto)
|
||||||
t.Failfast = cmp.Or(other.Failfast, t.Failfast)
|
t.Failfast = cmp.Or(other.Failfast, t.Failfast)
|
||||||
t.TempDir = cmp.Or(other.TempDir, t.TempDir)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,40 +112,6 @@ func TestGetConfig_OnlyLocal(t *testing.T) { //nolint:paralleltest // cannot run
|
|||||||
}, cfg)
|
}, cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetConfig_TempDir(t *testing.T) { //nolint:paralleltest // cannot run in parallel
|
|
||||||
_, _, localDir := setupDirs(t)
|
|
||||||
|
|
||||||
writeFile(t, localDir, ".taskrc.yml", `
|
|
||||||
temp-dir: .task-cache
|
|
||||||
`)
|
|
||||||
|
|
||||||
cfg, err := GetConfig(localDir)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NotNil(t, cfg)
|
|
||||||
require.NotNil(t, cfg.TempDir)
|
|
||||||
assert.Equal(t, ".task-cache", *cfg.TempDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetConfig_TempDirMergePrecedence(t *testing.T) { //nolint:paralleltest // cannot run in parallel
|
|
||||||
xdgConfigDir, homeDir, localDir := setupDirs(t)
|
|
||||||
|
|
||||||
writeFile(t, xdgConfigDir, "taskrc.yml", `
|
|
||||||
temp-dir: xdg-cache
|
|
||||||
`)
|
|
||||||
writeFile(t, homeDir, ".taskrc.yml", `
|
|
||||||
temp-dir: home-cache
|
|
||||||
`)
|
|
||||||
writeFile(t, localDir, ".taskrc.yml", `
|
|
||||||
temp-dir: local-cache
|
|
||||||
`)
|
|
||||||
|
|
||||||
cfg, err := GetConfig(localDir)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NotNil(t, cfg)
|
|
||||||
require.NotNil(t, cfg.TempDir)
|
|
||||||
assert.Equal(t, "local-cache", *cfg.TempDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetConfig_All(t *testing.T) { //nolint:paralleltest // cannot run in parallel
|
func TestGetConfig_All(t *testing.T) { //nolint:paralleltest // cannot run in parallel
|
||||||
xdgConfigDir, homeDir, localDir := setupDirs(t)
|
xdgConfigDir, homeDir, localDir := setupDirs(t)
|
||||||
|
|
||||||
@@ -340,4 +306,27 @@ remote:
|
|||||||
assert.Equal(t, &cacheExpiry, base.Remote.CacheExpiry)
|
assert.Equal(t, &cacheExpiry, base.Remote.CacheExpiry)
|
||||||
assert.Equal(t, []string{"github.com", "gitlab.com"}, base.Remote.TrustedHosts)
|
assert.Equal(t, []string{"github.com", "gitlab.com"}, base.Remote.TrustedHosts)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("output-ci-auto merge", func(t *testing.T) { //nolint:paralleltest // parent test cannot run in parallel
|
||||||
|
trueVal := true
|
||||||
|
falseVal := false
|
||||||
|
|
||||||
|
t.Run("other overrides nil base", func(t *testing.T) { //nolint:paralleltest
|
||||||
|
base := &ast.TaskRC{}
|
||||||
|
base.Merge(&ast.TaskRC{OutputCIAuto: &trueVal})
|
||||||
|
assert.Equal(t, &trueVal, base.OutputCIAuto)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("other overrides base", func(t *testing.T) { //nolint:paralleltest
|
||||||
|
base := &ast.TaskRC{OutputCIAuto: &falseVal}
|
||||||
|
base.Merge(&ast.TaskRC{OutputCIAuto: &trueVal})
|
||||||
|
assert.Equal(t, &trueVal, base.OutputCIAuto)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("nil other does not override base", func(t *testing.T) { //nolint:paralleltest
|
||||||
|
base := &ast.TaskRC{OutputCIAuto: &trueVal}
|
||||||
|
base.Merge(&ast.TaskRC{})
|
||||||
|
assert.Equal(t, &trueVal, base.OutputCIAuto)
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
1
testdata/gitignore/.gitignore
vendored
1
testdata/gitignore/.gitignore
vendored
@@ -1 +0,0 @@
|
|||||||
ignored.txt
|
|
||||||
25
testdata/gitignore/Taskfile.yml
vendored
25
testdata/gitignore/Taskfile.yml
vendored
@@ -1,25 +0,0 @@
|
|||||||
version: '3'
|
|
||||||
|
|
||||||
use_gitignore: true
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
build:
|
|
||||||
cmds:
|
|
||||||
- cp ./source.txt ./generated.txt
|
|
||||||
sources:
|
|
||||||
- ./*.txt
|
|
||||||
- exclude: ./generated.txt
|
|
||||||
generates:
|
|
||||||
- ./generated.txt
|
|
||||||
method: checksum
|
|
||||||
|
|
||||||
build-no-use_gitignore:
|
|
||||||
use_gitignore: false
|
|
||||||
cmds:
|
|
||||||
- cp ./source.txt ./generated.txt
|
|
||||||
sources:
|
|
||||||
- ./*.txt
|
|
||||||
- exclude: ./generated.txt
|
|
||||||
generates:
|
|
||||||
- ./generated.txt
|
|
||||||
method: checksum
|
|
||||||
1
testdata/gitignore/source.txt
vendored
1
testdata/gitignore/source.txt
vendored
@@ -1 +0,0 @@
|
|||||||
source content
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
task: [build] cp ./source.txt ./generated.txt
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
task: Task "build" is up to date
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
task: [build] cp ./source.txt ./generated.txt
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
task: Task "build" is up to date
|
|
||||||
1
testdata/gitignore_included/.gitignore
vendored
1
testdata/gitignore_included/.gitignore
vendored
@@ -1 +0,0 @@
|
|||||||
ignored.txt
|
|
||||||
9
testdata/gitignore_included/Taskfile.yml
vendored
9
testdata/gitignore_included/Taskfile.yml
vendored
@@ -1,9 +0,0 @@
|
|||||||
version: '3'
|
|
||||||
|
|
||||||
includes:
|
|
||||||
included: ./included/Taskfile.yml
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
default:
|
|
||||||
cmds:
|
|
||||||
- echo "root"
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
version: '3'
|
|
||||||
|
|
||||||
use_gitignore: true
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
build:
|
|
||||||
cmds:
|
|
||||||
- echo "build executed"
|
|
||||||
sources:
|
|
||||||
- ./*.txt
|
|
||||||
method: checksum
|
|
||||||
1
testdata/gitignore_included/source.txt
vendored
1
testdata/gitignore_included/source.txt
vendored
@@ -1 +0,0 @@
|
|||||||
source
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
task: [included:build] echo "build executed"
|
|
||||||
build executed
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
task: Task "included:build" is up to date
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
task: Task "included:build" is up to date
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
ignored.txt
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
version: '3'
|
|
||||||
|
|
||||||
use_gitignore: true
|
|
||||||
|
|
||||||
includes:
|
|
||||||
included: ./included/Taskfile.yml
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
default:
|
|
||||||
cmds:
|
|
||||||
- echo "root"
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
version: '3'
|
|
||||||
|
|
||||||
use_gitignore: false
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
build:
|
|
||||||
cmds:
|
|
||||||
- echo "build executed"
|
|
||||||
sources:
|
|
||||||
- ./*.txt
|
|
||||||
method: checksum
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
source
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
task: [included:build] echo "build executed"
|
|
||||||
build executed
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
task: [included:build] echo "build executed"
|
|
||||||
build executed
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
task: Task "included:build" is up to date
|
|
||||||
1
testdata/gitignore_negation/.gitignore
vendored
1
testdata/gitignore_negation/.gitignore
vendored
@@ -1 +0,0 @@
|
|||||||
*.log
|
|
||||||
11
testdata/gitignore_negation/Taskfile.yml
vendored
11
testdata/gitignore_negation/Taskfile.yml
vendored
@@ -1,11 +0,0 @@
|
|||||||
version: '3'
|
|
||||||
|
|
||||||
use_gitignore: true
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
build:
|
|
||||||
cmds:
|
|
||||||
- echo "build executed"
|
|
||||||
sources:
|
|
||||||
- ./sub/*.log
|
|
||||||
method: checksum
|
|
||||||
1
testdata/gitignore_negation/sub/.gitignore
vendored
1
testdata/gitignore_negation/sub/.gitignore
vendored
@@ -1 +0,0 @@
|
|||||||
!debug.log
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
task: [build] echo "build executed"
|
|
||||||
build executed
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
task: Task "build" is up to date
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
task: [build] echo "build executed"
|
|
||||||
build executed
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
task: Task "build" is up to date
|
|
||||||
11
testdata/gitignore_nested/Taskfile.yml
vendored
11
testdata/gitignore_nested/Taskfile.yml
vendored
@@ -1,11 +0,0 @@
|
|||||||
version: '3'
|
|
||||||
|
|
||||||
use_gitignore: true
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
build:
|
|
||||||
cmds:
|
|
||||||
- echo "build executed"
|
|
||||||
sources:
|
|
||||||
- ./sub/*
|
|
||||||
method: checksum
|
|
||||||
1
testdata/gitignore_nested/sub/.gitignore
vendored
1
testdata/gitignore_nested/sub/.gitignore
vendored
@@ -1 +0,0 @@
|
|||||||
secret.dat
|
|
||||||
1
testdata/gitignore_nested/sub/keep.txt
vendored
1
testdata/gitignore_nested/sub/keep.txt
vendored
@@ -1 +0,0 @@
|
|||||||
keep
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
task: [build] echo "build executed"
|
|
||||||
build executed
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
task: Task "build" is up to date
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
task: [build] echo "build executed"
|
|
||||||
build executed
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
task: Task "build" is up to date
|
|
||||||
83
testdata/secrets/Taskfile.yml
vendored
83
testdata/secrets/Taskfile.yml
vendored
@@ -1,83 +0,0 @@
|
|||||||
version: '3'
|
|
||||||
|
|
||||||
vars:
|
|
||||||
# Public variable
|
|
||||||
APP_NAME: myapp
|
|
||||||
|
|
||||||
# Secret variable with value
|
|
||||||
API_KEY:
|
|
||||||
value: "secret-api-key-123"
|
|
||||||
secret: true
|
|
||||||
|
|
||||||
# Secret variable from shell command
|
|
||||||
PASSWORD:
|
|
||||||
sh: "echo 'my-super-secret-password'"
|
|
||||||
secret: true
|
|
||||||
|
|
||||||
# Non-secret variable
|
|
||||||
PUBLIC_URL: https://example.com
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
test-secret-masking:
|
|
||||||
desc: Test that secret variables are masked in logs
|
|
||||||
cmds:
|
|
||||||
- echo "Deploying {{.APP_NAME}} to {{.PUBLIC_URL}}"
|
|
||||||
- echo "Using API key {{.API_KEY}}"
|
|
||||||
- echo "Password is {{.PASSWORD}}"
|
|
||||||
- echo "Public app name is {{.APP_NAME}}"
|
|
||||||
|
|
||||||
test-multiple-secrets:
|
|
||||||
desc: Test multiple secrets in one command
|
|
||||||
cmds:
|
|
||||||
- echo "API={{.API_KEY}} PWD={{.PASSWORD}}"
|
|
||||||
|
|
||||||
test-mixed:
|
|
||||||
desc: Test mix of secret and public vars
|
|
||||||
vars:
|
|
||||||
LOCAL_SECRET:
|
|
||||||
value: "task-level-secret"
|
|
||||||
secret: true
|
|
||||||
cmds:
|
|
||||||
- echo "App={{.APP_NAME}} Secret={{.LOCAL_SECRET}} URL={{.PUBLIC_URL}}"
|
|
||||||
|
|
||||||
test-deferred-secret:
|
|
||||||
desc: Test that deferred commands mask secrets
|
|
||||||
vars:
|
|
||||||
DEFERRED_SECRET:
|
|
||||||
value: "deferred-secret-value"
|
|
||||||
secret: true
|
|
||||||
cmds:
|
|
||||||
- echo "Starting task"
|
|
||||||
- defer: echo "Cleanup with secret={{.DEFERRED_SECRET}} and app={{.APP_NAME}}"
|
|
||||||
- echo "Main command executed"
|
|
||||||
|
|
||||||
test-dynamic-secret-verbose:
|
|
||||||
desc: Test that dynamic (sh) secrets are masked even in verbose logs
|
|
||||||
cmds:
|
|
||||||
- echo "Password is {{.PASSWORD}}"
|
|
||||||
|
|
||||||
test-secret-key-order:
|
|
||||||
desc: Test that "secret" may be declared before the value/sh key
|
|
||||||
vars:
|
|
||||||
SECRET_FIRST:
|
|
||||||
secret: true
|
|
||||||
value: "order-independent-secret"
|
|
||||||
SH_SECRET_FIRST:
|
|
||||||
secret: true
|
|
||||||
sh: "echo 'sh-order-independent-secret'"
|
|
||||||
cmds:
|
|
||||||
- echo "Value={{.SECRET_FIRST}} Sh={{.SH_SECRET_FIRST}}"
|
|
||||||
|
|
||||||
test-env-secret-limitation:
|
|
||||||
desc: Test showing that env vars with secret flag are NOT masked (limitation)
|
|
||||||
env:
|
|
||||||
SECRET_TOKEN:
|
|
||||||
value: "env-secret-token-123"
|
|
||||||
secret: true
|
|
||||||
PUBLIC_ENV: "public-value"
|
|
||||||
cmds:
|
|
||||||
# Templates {{.VAR}} don't work with env - they're empty
|
|
||||||
- echo "Token via template is {{.SECRET_TOKEN}}"
|
|
||||||
# Shell $VAR works but is NOT masked (env vars not in template system)
|
|
||||||
- echo "Token via shell is $SECRET_TOKEN"
|
|
||||||
- echo "Public env is {{.PUBLIC_ENV}}"
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
task: [test-deferred-secret] echo "Starting task"
|
|
||||||
Starting task
|
|
||||||
task: [test-deferred-secret] echo "Main command executed"
|
|
||||||
Main command executed
|
|
||||||
task: [test-deferred-secret] echo "Cleanup with secret=***** and app=myapp"
|
|
||||||
Cleanup with secret=deferred-secret-value and app=myapp
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user