Compare commits

..

20 Commits

Author SHA1 Message Date
Valentin Maerten
e639dfae32 refactor: VeryFastCompile for Task list 2025-02-09 20:01:35 +01:00
Pete Davison
bc85be2c47 chore: changelog for #2049 2025-02-09 19:52:15 +01:00
Pete Davison
6ce798e16c feat: experiments logging improvements (#2049)
* feat: warn when enabling inactive experiments

* feat: TASK_ environment prefix

* feat: calculate experiment enabled/active instead of storing

* refactor: rename GetTaskVar to GetTaskEnv

* feat: experiments tests
2025-02-08 23:02:51 +00:00
Pete Davison
be81885835 feat: stop task test installing task (#2050) 2025-02-08 23:02:22 +00:00
Valentin Maerten
69ac06170a chore: changelog for #2031 2025-02-08 17:34:43 +01:00
Valentin Maerten
c995fe6d11 fix(checker): use only one checker at the same time to improve perf (#2031)
* fix(checker): use only one checker at the same time to improve performance

* refactor

* fix test
2025-02-08 17:34:04 +01:00
Valentin Maerten
9009124192 chore: changelog for #2033 2025-02-08 17:31:01 +01:00
Valentin Maerten
80f96d67da fix: requires allowed values works with dynamic var (#2033) 2025-02-08 17:29:36 +01:00
Valentin Maerten
002b8c929a docs: fix a typo in dotenv section 2025-02-08 16:13:34 +01:00
Pete Davison
b5b1524d3a feat: variable inheritance tests (#2038) 2025-02-05 19:51:52 +00:00
renovate[bot]
3aee0a0519 chore(deps): update react monorepo to v19 (#2028)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-31 10:32:44 +01:00
Pete Davison
23df1f0c61 chore: changelog for #2007 2025-01-29 22:49:14 +00:00
Ukjae Jeong
edbb83f6de fix: HTTPNode.Location when building graph (#2007)
* Fix HTTPNode.Location when building graph

* Add test and fix cache
2025-01-29 22:46:43 +00:00
Pete Davison
c903d5c6f4 chore: changelog for #2011 2025-01-29 22:43:51 +00:00
Henrique Corrêa
88c4ba1740 feat: make Taskfile initialization less verbose by default (#2011)
* change what is printed when creating Taskfile

When using --init to create a new Taskfile, it used to print the whole contents of the file to the terminal, which was unnecessarily verbose (and honestly felt unintentional).

Now only the filename is printed by default and the --silent and --verbose flags can be used to control the behavior (print nothing or content + filename, respectively).

* include additional new line with -i -v

it looks slightly better in the terminal.

* print init success text in green

* fix TestInit, create and pass in a logger

* move logging outside of InitTaskfile

- revert API changes made to InitTaskfile
- make consts in init.go public so they can be accessed from task.go
- rename variable "logger" to "log" in task.go to fix conflict with logger package

* move TestInit into init_test.go file

as requested by pd93.
2025-01-29 22:41:17 +00:00
Valentin Maerten
7d4c52546a chore: add label to renovate's PRs 2025-01-29 21:46:37 +01:00
Pete Davison
f5121de468 docs: broken links 2025-01-26 00:56:25 +00:00
renovate[bot]
b5d573fbd9 chore(deps): update golang's deps (#2020)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-25 09:22:10 +01:00
renovate[bot]
888de0f8ef chore(deps): update website (#2021)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-25 09:14:26 +01:00
Valentin Maerten
09b11d343b chore: remove dependabot and put Renovate weekly (#2017) 2025-01-25 09:06:53 +01:00
84 changed files with 1951 additions and 1241 deletions

View File

@@ -1,24 +0,0 @@
version: 2
updates:
- package-ecosystem: gomod
directory: /
schedule:
interval: weekly
day: saturday
time: '08:00'
timezone: America/Sao_Paulo
labels:
- "area: dependencies"
- "lang: go"
- package-ecosystem: npm
directory: /
schedule:
interval: weekly
day: saturday
time: '08:00'
timezone: America/Sao_Paulo
labels:
- "area: dependencies"
- "lang: javascript"

39
.github/renovate.json vendored
View File

@@ -3,46 +3,23 @@
"extends": [
"config:recommended",
"group:allNonMajor",
"schedule:monthly"
"schedule:weekly",
":semanticCommitTypeAll(chore)"
],
"mode": "full",
"reviewers": ["team:developer"],
"addLabels":["area: dependencies"],
"packageRules": [
{
"matchManagers": ["github-actions"],
"groupName": "Github Action",
"labels": ["area: github actions", "area: dependencies"],
"matchPackageNames": [
"*"
],
"matchUpdateTypes": [
"minor",
"patch"
]
"addLabels": ["area: github actions"]
},
{
"matchManagers": ["npm", "nvm"],
"groupName": "Website",
"labels": ["lang: javascript", "area: dependencies"],
"matchPackageNames": [
"*"
],
"matchUpdateTypes": [
"minor",
"patch"
]
"matchCategories": ["js", "node"],
"addLabels": ["lang: javascript"]
},
{
"matchManagers": ["gomod"],
"groupName": "golang",
"labels": ["lang: go", "area: dependencies"],
"matchPackageNames": [
"*"
],
"matchUpdateTypes": [
"minor",
"patch"
]
"matchCategories": ["golang"],
"addLabels": ["lang: go"]
}
]
}

2
.nvmrc
View File

@@ -1 +1 @@
22.12.0
22.13.1

View File

@@ -1,5 +1,19 @@
# Changelog
## Unreleased
- Made `--init` less verbose by default and respect `--silent` and `--verbose`
flags (#2009, #2011 by @HeCorr).
- Fix a bug where an HTTP node's location was being mutated incorrectly (#2007
by @jeongukjae).
- Fixed a bug where allowed values didn't work with dynamic var (#2032, #2033 by
@vmaerten).
- Use only the relevant checker (timestamp or checksum) to improve performance
(#2029, #2031 by @vmaerten).
- Print warnings when attempting to enable an inactive experiment or an active
experiment with an invalid value (#1979, #2049 by @pd93).
- Refactored the experiments package and added tests (#2049 by @pd93).
## v3.41.0 - 2025-01-18
- Fixed an issue where dynamic variables were not properly logged in verbose

View File

@@ -98,21 +98,17 @@ tasks:
test:
desc: Runs test suite
aliases: [t]
deps: [install]
sources:
- "**/*.go"
- "testdata/**/*"
cmds:
- go test {{catLines .GO_PACKAGES}}
vars:
GO_PACKAGES:
sh: go list ./...
- go test ./...
test:all:
desc: Runs test suite with signals and watch tests included
deps: [install, sleepit:build]
deps: [sleepit:build]
cmds:
- go test {{catLines .GO_PACKAGES}} -tags 'signals watch'
vars:
GO_PACKAGES:
sh: go list ./...
- go test -tags 'signals watch' ./...
goreleaser:test:
desc: Tests release process without publishing
@@ -176,11 +172,3 @@ tasks:
desc: Publish release to npm
cmds:
- npm publish --access=public
packages:
cmds:
- echo '{{.GO_PACKAGES}}'
vars:
GO_PACKAGES:
sh: go list ./...
silent: true

View File

@@ -44,7 +44,7 @@ func main() {
}
func run() error {
logger := &logger.Logger{
log := &logger.Logger{
Stdout: os.Stdout,
Stderr: os.Stderr,
Verbose: flags.Verbose,
@@ -69,7 +69,7 @@ func run() error {
}
if flags.Experiments {
return experiments.List(logger)
return log.PrintExperiments()
}
if flags.Init {
@@ -77,9 +77,18 @@ func run() error {
if err != nil {
return err
}
if err := task.InitTaskfile(os.Stdout, wd); err != nil {
return err
}
if !flags.Silent {
if flags.Verbose {
log.Outf(logger.Default, "%s\n", task.DefaultTaskfile)
}
log.Outf(logger.Green, "%s created in the current directory\n", task.DefaultTaskFilename)
}
return nil
}
@@ -100,6 +109,10 @@ func run() error {
dir = home
}
if err := experiments.Validate(); err != nil {
log.Warnf("%s\n", err.Error())
}
var taskSorter sort.TaskSorter
switch flags.TaskSort {
case "none":
@@ -145,9 +158,6 @@ func run() error {
if err != nil {
return err
}
if experiments.AnyVariables.Enabled {
logger.Warnf("The 'Any Variables' experiment flag is no longer required to use non-map variable types. If you wish to use map variables, please use 'TASK_X_MAP_VARIABLES' instead. See https://github.com/go-task/task/issues/1585\n")
}
// If the download flag is specified, we should stop execution as soon as
// taskfile is downloaded

23
go.mod
View File

@@ -5,27 +5,27 @@ go 1.22.0
require (
github.com/Ladicle/tabwriter v1.0.0
github.com/Masterminds/semver/v3 v3.3.1
github.com/alecthomas/chroma/v2 v2.14.0
github.com/alecthomas/chroma/v2 v2.15.0
github.com/chainguard-dev/git-urls v1.0.2
github.com/davecgh/go-spew v1.1.1
github.com/dominikbraun/graph v0.23.0
github.com/elliotchance/orderedmap/v2 v2.7.0
github.com/fatih/color v1.18.0
github.com/go-git/go-billy/v5 v5.6.1
github.com/go-git/go-git/v5 v5.13.1
github.com/go-git/go-billy/v5 v5.6.2
github.com/go-git/go-git/v5 v5.13.2
github.com/go-task/slim-sprig/v3 v3.0.0
github.com/go-task/template v0.1.0
github.com/joho/godotenv v1.5.1
github.com/mattn/go-zglob v0.0.6
github.com/mitchellh/hashstructure/v2 v2.0.2
github.com/otiai10/copy v1.14.0
github.com/otiai10/copy v1.14.1
github.com/radovskyb/watcher v1.0.7
github.com/sajari/fuzzy v1.0.0
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.10.0
github.com/zeebo/xxh3 v1.0.2
golang.org/x/sync v0.10.0
golang.org/x/term v0.27.0
golang.org/x/term v0.28.0
gopkg.in/yaml.v3 v3.0.1
mvdan.cc/sh/v3 v3.10.0
)
@@ -33,10 +33,10 @@ require (
require (
dario.cat/mergo v1.0.0 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/ProtonMail/go-crypto v1.1.3 // indirect
github.com/ProtonMail/go-crypto v1.1.5 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/cyphar/filepath-securejoin v0.3.6 // indirect
github.com/dlclark/regexp2 v1.11.0 // indirect
github.com/dlclark/regexp2 v1.11.4 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
@@ -46,16 +46,17 @@ require (
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/otiai10/mint v1.6.3 // indirect
github.com/pjbgf/sha1cd v0.3.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/skeema/knownhosts v1.3.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/mod v0.18.0 // indirect
golang.org/x/net v0.33.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/tools v0.22.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
)

24
go.sum
View File

@@ -9,10 +9,14 @@ github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migc
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/ProtonMail/go-crypto v1.1.3 h1:nRBOetoydLeUb4nHajyO2bKqMLfWQ/ZPwkXqXxPxCFk=
github.com/ProtonMail/go-crypto v1.1.3/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
github.com/ProtonMail/go-crypto v1.1.5 h1:eoAQfK2dwL+tFSFpr7TbOaPNUbPiJj4fLYwwGE1FQO4=
github.com/ProtonMail/go-crypto v1.1.5/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE=
github.com/alecthomas/assert/v2 v2.7.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E=
github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4EbYP4i6n68SG4I=
github.com/alecthomas/chroma/v2 v2.15.0 h1:LxXTQHFoYrstG2nnV9y2X5O94sOBzf0CIUpSTbpxvMc=
github.com/alecthomas/chroma/v2 v2.15.0/go.mod h1:gUhVLrPDXPtp/f+L1jo9xepo9gL4eLwRuGAunSZMkio=
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
@@ -32,6 +36,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo=
github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dominikbraun/graph v0.23.0 h1:TdZB4pPqCLFxYhdyMFb1TBdFxp8XLcJfTTBQucVPgCo=
github.com/dominikbraun/graph v0.23.0/go.mod h1:yOjYyogZLY1LSG9E33JWZJiq5k83Qy2C6POAuiViluc=
github.com/elazarl/goproxy v1.2.3 h1:xwIyKHbaP5yfT6O9KIeYJR5549MXRQkoQMRXGztz8YQ=
@@ -48,10 +54,14 @@ github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66D
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
github.com/go-git/go-billy/v5 v5.6.1 h1:u+dcrgaguSSkbjzHwelEjc0Yj300NUevrrPphk/SoRA=
github.com/go-git/go-billy/v5 v5.6.1/go.mod h1:0AsLr1z2+Uksi4NlElmMblP5rPcDZNRCD8ujZCRR2BE=
github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM=
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
github.com/go-git/go-git/v5 v5.13.1 h1:DAQ9APonnlvSWpvolXWIuV6Q6zXy2wHbN4cVlNR5Q+M=
github.com/go-git/go-git/v5 v5.13.1/go.mod h1:qryJB4cSBoq3FRoBRf5A77joojuBcmPJ0qu3XXXVixc=
github.com/go-git/go-git/v5 v5.13.2 h1:7O7xvsK7K+rZPKW6AQR1YyNhfywkv7B8/FsP3ki6Zv0=
github.com/go-git/go-git/v5 v5.13.2/go.mod h1:hWdW5P4YZRjmpGHwRH2v3zkWcNl6HeXaXQEMGb3NJ9A=
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
@@ -94,10 +104,16 @@ github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU=
github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w=
github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8=
github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I=
github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks=
github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs=
github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -131,6 +147,8 @@ github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaD
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
@@ -138,6 +156,8 @@ golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -151,9 +171,13 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=

13
init.go
View File

@@ -1,7 +1,6 @@
package task
import (
"fmt"
"io"
"os"
@@ -9,7 +8,7 @@ import (
"github.com/go-task/task/v3/internal/filepathext"
)
const defaultTaskfile = `# https://taskfile.dev
const DefaultTaskfile = `# https://taskfile.dev
version: '3'
@@ -23,19 +22,19 @@ tasks:
silent: true
`
const defaultTaskfileName = "Taskfile.yml"
const DefaultTaskFilename = "Taskfile.yml"
// InitTaskfile Taskfile creates a new Taskfile
// InitTaskfile creates a new Taskfile
func InitTaskfile(w io.Writer, dir string) error {
f := filepathext.SmartJoin(dir, defaultTaskfileName)
f := filepathext.SmartJoin(dir, DefaultTaskFilename)
if _, err := os.Stat(f); err == nil {
return errors.TaskfileAlreadyExistsError{}
}
if err := os.WriteFile(f, []byte(defaultTaskfile), 0o644); err != nil {
if err := os.WriteFile(f, []byte(DefaultTaskfile), 0o644); err != nil {
return err
}
fmt.Fprintf(w, "%s created in the current directory\n", defaultTaskfile)
return nil
}

31
init_test.go Normal file
View File

@@ -0,0 +1,31 @@
package task_test
import (
"io"
"os"
"testing"
"github.com/go-task/task/v3"
"github.com/go-task/task/v3/internal/filepathext"
)
func TestInit(t *testing.T) {
t.Parallel()
const dir = "testdata/init"
file := filepathext.SmartJoin(dir, "Taskfile.yml")
_ = os.Remove(file)
if _, err := os.Stat(file); err == nil {
t.Errorf("Taskfile.yml should not exist")
}
if err := task.InitTaskfile(io.Discard, dir); err != nil {
t.Error(err)
}
if _, err := os.Stat(file); err != nil {
t.Errorf("Taskfile.yml should exist")
}
_ = os.Remove(file)
}

8
internal/env/env.go vendored
View File

@@ -8,6 +8,8 @@ import (
"github.com/go-task/task/v3/taskfile/ast"
)
const taskVarPrefix = "TASK_"
func Get(t *ast.Task) []string {
if t.Env == nil {
return nil
@@ -23,7 +25,7 @@ func GetFromVars(env *ast.Vars) []string {
if !isTypeAllowed(v) {
continue
}
if !experiments.EnvPrecedence.Enabled {
if !experiments.EnvPrecedence.Enabled() {
if _, alreadySet := os.LookupEnv(k); alreadySet {
continue
}
@@ -42,3 +44,7 @@ func isTypeAllowed(v any) bool {
return false
}
}
func GetTaskEnv(key string) string {
return os.Getenv(taskVarPrefix + key)
}

View File

@@ -0,0 +1,32 @@
package experiments
import (
"fmt"
"strings"
)
type InvalidValueError struct {
Name string
AllowedValues []string
Value string
}
func (err InvalidValueError) Error() string {
return fmt.Sprintf(
"task: Experiment %q has an invalid value %q (allowed values: %s)",
err.Name,
err.Value,
strings.Join(err.AllowedValues, ", "),
)
}
type InactiveError struct {
Name string
}
func (err InactiveError) Error() string {
return fmt.Sprintf(
"task: Experiment %q is inactive and cannot be enabled",
err.Name,
)
}

View File

@@ -0,0 +1,56 @@
package experiments
import (
"fmt"
"slices"
)
type Experiment struct {
Name string // The name of the experiment.
AllowedValues []string // The values that can enable this experiment.
Value string // The version of the experiment that is enabled.
}
// New creates a new experiment with the given name and sets the values that can
// enable it.
func New(xName string, allowedValues ...string) Experiment {
value := getEnv(xName)
x := Experiment{
Name: xName,
AllowedValues: allowedValues,
Value: value,
}
xList = append(xList, x)
return x
}
func (x *Experiment) Enabled() bool {
return slices.Contains(x.AllowedValues, x.Value)
}
func (x *Experiment) Active() bool {
return len(x.AllowedValues) > 0
}
func (x Experiment) Valid() error {
if !x.Active() && x.Value != "" {
return &InactiveError{
Name: x.Name,
}
}
if !x.Enabled() && x.Value != "" {
return &InvalidValueError{
Name: x.Name,
AllowedValues: x.AllowedValues,
Value: x.Value,
}
}
return nil
}
func (x Experiment) String() string {
if x.Enabled() {
return fmt.Sprintf("on (%s)", x.Value)
}
return "off"
}

View File

@@ -0,0 +1,74 @@
package experiments_test
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/go-task/task/v3/internal/experiments"
)
func TestNew(t *testing.T) {
const (
exampleExperiment = "EXAMPLE"
exampleExperimentEnv = "TASK_X_EXAMPLE"
)
tests := []struct {
name string
allowedValues []string
value string
wantEnabled bool
wantActive bool
wantValid error
}{
{
name: `[] allowed, value=""`,
wantEnabled: false,
wantActive: false,
},
{
name: `[] allowed, value="1"`,
value: "1",
wantEnabled: false,
wantActive: false,
wantValid: &experiments.InactiveError{
Name: exampleExperiment,
},
},
{
name: `[1] allowed, value=""`,
allowedValues: []string{"1"},
wantEnabled: false,
wantActive: true,
},
{
name: `[1] allowed, value="1"`,
allowedValues: []string{"1"},
value: "1",
wantEnabled: true,
wantActive: true,
},
{
name: `[1] allowed, value="2"`,
allowedValues: []string{"1"},
value: "2",
wantEnabled: false,
wantActive: true,
wantValid: &experiments.InvalidValueError{
Name: exampleExperiment,
AllowedValues: []string{"1"},
Value: "2",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Setenv(exampleExperimentEnv, tt.value)
x := experiments.New(exampleExperiment, tt.allowedValues...)
assert.Equal(t, exampleExperiment, x.Name)
assert.Equal(t, tt.wantEnabled, x.Enabled())
assert.Equal(t, tt.wantActive, x.Active())
assert.Equal(t, tt.wantValid, x.Valid())
})
}
}

View File

@@ -2,28 +2,17 @@ package experiments
import (
"fmt"
"io"
"os"
"path/filepath"
"slices"
"strings"
"github.com/Ladicle/tabwriter"
"github.com/joho/godotenv"
"github.com/spf13/pflag"
"github.com/go-task/task/v3/internal/logger"
)
const envPrefix = "TASK_X_"
type Experiment struct {
Name string
Enabled bool
Value string
}
// A list of experiments.
// A set of experiments that can be enabled or disabled.
var (
GentleForce Experiment
RemoteTaskfiles Experiment
@@ -32,32 +21,31 @@ var (
EnvPrecedence Experiment
)
// An internal list of all the initialized experiments used for iterating.
var xList []Experiment
func init() {
readDotEnv()
GentleForce = New("GENTLE_FORCE")
RemoteTaskfiles = New("REMOTE_TASKFILES")
AnyVariables = New("ANY_VARIABLES", "1", "2")
GentleForce = New("GENTLE_FORCE", "1")
RemoteTaskfiles = New("REMOTE_TASKFILES", "1")
AnyVariables = New("ANY_VARIABLES")
MapVariables = New("MAP_VARIABLES", "1", "2")
EnvPrecedence = New("ENV_PRECEDENCE")
EnvPrecedence = New("ENV_PRECEDENCE", "1")
}
func New(xName string, enabledValues ...string) Experiment {
if len(enabledValues) == 0 {
enabledValues = []string{"1"}
}
value := getEnv(xName)
return Experiment{
Name: xName,
Enabled: slices.Contains(enabledValues, value),
Value: value,
// Validate checks if any experiments have been enabled while being inactive.
// If one is found, the function returns an error.
func Validate() error {
for _, x := range List() {
if err := x.Valid(); err != nil {
return err
}
}
return nil
}
func (x Experiment) String() string {
if x.Enabled {
return fmt.Sprintf("on (%s)", x.Value)
}
return "off"
func List() []Experiment {
return xList
}
func getEnv(xName string) string {
@@ -95,18 +83,3 @@ func readDotEnv() {
}
}
}
func printExperiment(w io.Writer, l *logger.Logger, x Experiment) {
l.FOutf(w, logger.Yellow, "* ")
l.FOutf(w, logger.Green, x.Name)
l.FOutf(w, logger.Default, ": \t%s\n", x.String())
}
func List(l *logger.Logger) error {
w := tabwriter.NewWriter(os.Stdout, 0, 8, 0, ' ', 0)
printExperiment(w, l, GentleForce)
printExperiment(w, l, RemoteTaskfiles)
printExperiment(w, l, MapVariables)
printExperiment(w, l, EnvPrecedence)
return w.Flush()
}

View File

@@ -10,6 +10,7 @@ import (
"github.com/spf13/pflag"
"github.com/go-task/task/v3/errors"
"github.com/go-task/task/v3/internal/env"
"github.com/go-task/task/v3/internal/experiments"
"github.com/go-task/task/v3/taskfile/ast"
)
@@ -79,7 +80,7 @@ func init() {
log.Print(usage)
pflag.PrintDefaults()
}
offline, err := strconv.ParseBool(cmp.Or(os.Getenv("TASK_OFFLINE"), "false"))
offline, err := strconv.ParseBool(cmp.Or(env.GetTaskEnv("OFFLINE"), "false"))
if err != nil {
offline = false
}
@@ -115,7 +116,7 @@ func init() {
pflag.BoolVar(&Experiments, "experiments", false, "Lists all the available experiments and whether or not they are enabled.")
// Gentle force experiment will override the force flag and add a new force-all flag
if experiments.GentleForce.Enabled {
if experiments.GentleForce.Enabled() {
pflag.BoolVarP(&Force, "force", "f", false, "Forces execution of the directly called task.")
pflag.BoolVar(&ForceAll, "force-all", false, "Forces execution of the called task and all its dependant tasks.")
} else {
@@ -123,7 +124,7 @@ func init() {
}
// Remote Taskfiles experiment will adds the "download" and "offline" flags
if experiments.RemoteTaskfiles.Enabled {
if experiments.RemoteTaskfiles.Enabled() {
pflag.BoolVar(&Download, "download", false, "Downloads a cached version of a remote Taskfile.")
pflag.BoolVar(&Offline, "offline", offline, "Forces Task to only use local or cached Taskfiles.")
pflag.DurationVar(&Timeout, "timeout", time.Second*10, "Timeout for downloading remote Taskfiles.")

View File

@@ -8,9 +8,12 @@ import (
"strconv"
"strings"
"github.com/Ladicle/tabwriter"
"github.com/fatih/color"
"github.com/go-task/task/v3/errors"
"github.com/go-task/task/v3/internal/env"
"github.com/go-task/task/v3/internal/experiments"
"github.com/go-task/task/v3/internal/term"
)
@@ -19,70 +22,86 @@ var (
ErrNoTerminal = errors.New("no terminal")
)
var (
attrsReset = envColor("COLOR_RESET", color.Reset)
attrsFgBlue = envColor("COLOR_BLUE", color.FgBlue)
attrsFgGreen = envColor("COLOR_GREEN", color.FgGreen)
attrsFgCyan = envColor("COLOR_CYAN", color.FgCyan)
attrsFgYellow = envColor("COLOR_YELLOW", color.FgYellow)
attrsFgMagenta = envColor("COLOR_MAGENTA", color.FgMagenta)
attrsFgRed = envColor("COLOR_RED", color.FgRed)
attrsFgHiBlue = envColor("COLOR_BRIGHT_BLUE", color.FgHiBlue)
attrsFgHiGreen = envColor("COLOR_BRIGHT_GREEN", color.FgHiGreen)
attrsFgHiCyan = envColor("COLOR_BRIGHT_CYAN", color.FgHiCyan)
attrsFgHiYellow = envColor("COLOR_BRIGHT_YELLOW", color.FgHiYellow)
attrsFgHiMagenta = envColor("COLOR_BRIGHT_MAGENTA", color.FgHiMagenta)
attrsFgHiRed = envColor("COLOR_BRIGHT_RED", color.FgHiRed)
)
type (
Color func() PrintFunc
PrintFunc func(io.Writer, string, ...any)
)
func Default() PrintFunc {
return color.New(envColor("TASK_COLOR_RESET", color.Reset)...).FprintfFunc()
return color.New(attrsReset...).FprintfFunc()
}
func Blue() PrintFunc {
return color.New(envColor("TASK_COLOR_BLUE", color.FgBlue)...).FprintfFunc()
return color.New(attrsFgBlue...).FprintfFunc()
}
func Green() PrintFunc {
return color.New(envColor("TASK_COLOR_GREEN", color.FgGreen)...).FprintfFunc()
return color.New(attrsFgGreen...).FprintfFunc()
}
func Cyan() PrintFunc {
return color.New(envColor("TASK_COLOR_CYAN", color.FgCyan)...).FprintfFunc()
return color.New(attrsFgCyan...).FprintfFunc()
}
func Yellow() PrintFunc {
return color.New(envColor("TASK_COLOR_YELLOW", color.FgYellow)...).FprintfFunc()
return color.New(attrsFgYellow...).FprintfFunc()
}
func Magenta() PrintFunc {
return color.New(envColor("TASK_COLOR_MAGENTA", color.FgMagenta)...).FprintfFunc()
return color.New(attrsFgMagenta...).FprintfFunc()
}
func Red() PrintFunc {
return color.New(envColor("TASK_COLOR_RED", color.FgRed)...).FprintfFunc()
return color.New(attrsFgRed...).FprintfFunc()
}
func BrightBlue() PrintFunc {
return color.New(envColor("TASK_COLOR_BRIGHT_BLUE", color.FgHiBlue)...).FprintfFunc()
return color.New(attrsFgHiBlue...).FprintfFunc()
}
func BrightGreen() PrintFunc {
return color.New(envColor("TASK_COLOR_BRIGHT_GREEN", color.FgHiGreen)...).FprintfFunc()
return color.New(attrsFgHiGreen...).FprintfFunc()
}
func BrightCyan() PrintFunc {
return color.New(envColor("TASK_COLOR_BRIGHT_CYAN", color.FgHiCyan)...).FprintfFunc()
return color.New(attrsFgHiCyan...).FprintfFunc()
}
func BrightYellow() PrintFunc {
return color.New(envColor("TASK_COLOR_BRIGHT_YELLOW", color.FgHiYellow)...).FprintfFunc()
return color.New(attrsFgHiYellow...).FprintfFunc()
}
func BrightMagenta() PrintFunc {
return color.New(envColor("TASK_COLOR_BRIGHT_MAGENTA", color.FgHiMagenta)...).FprintfFunc()
return color.New(attrsFgHiMagenta...).FprintfFunc()
}
func BrightRed() PrintFunc {
return color.New(envColor("TASK_COLOR_BRIGHT_RED", color.FgHiRed)...).FprintfFunc()
return color.New(attrsFgHiRed...).FprintfFunc()
}
func envColor(env string, defaultColor color.Attribute) []color.Attribute {
func envColor(name string, defaultColor color.Attribute) []color.Attribute {
if os.Getenv("FORCE_COLOR") != "" {
color.NoColor = false
}
// Fetch the environment variable
override := os.Getenv(env)
override := env.GetTaskEnv(name)
// First, try splitting the string by commas (RGB shortcut syntax) and if it
// matches, then prepend the 256-color foreground escape sequence.
@@ -195,3 +214,16 @@ func (l *Logger) Prompt(color Color, prompt string, defaultValue string, continu
return nil
}
func (l *Logger) PrintExperiments() error {
w := tabwriter.NewWriter(l.Stdout, 0, 8, 0, ' ', 0)
for _, x := range experiments.List() {
if !x.Active() {
continue
}
l.FOutf(w, Yellow, "* ")
l.FOutf(w, Green, x.Name)
l.FOutf(w, Default, ": \t%s\n", x.String())
}
return w.Flush()
}

View File

@@ -13,20 +13,10 @@ func (e *Executor) areTaskRequiredVarsSet(t *ast.Task) error {
}
var missingVars []string
var notAllowedValuesVars []errors.NotAllowedVar
for _, requiredVar := range t.Requires.Vars {
value, ok := t.Vars.Get(requiredVar.Name)
_, ok := t.Vars.Get(requiredVar.Name)
if !ok {
missingVars = append(missingVars, requiredVar.Name)
} else {
value, isString := value.Value.(string)
if isString && requiredVar.Enum != nil && !slices.Contains(requiredVar.Enum, value) {
notAllowedValuesVars = append(notAllowedValuesVars, errors.NotAllowedVar{
Value: value,
Enum: requiredVar.Enum,
Name: requiredVar.Name,
})
}
}
}
@@ -37,6 +27,29 @@ func (e *Executor) areTaskRequiredVarsSet(t *ast.Task) error {
}
}
return nil
}
func (e *Executor) areTaskRequiredVarsAllowedValuesSet(t *ast.Task) error {
if t.Requires == nil || len(t.Requires.Vars) == 0 {
return nil
}
var notAllowedValuesVars []errors.NotAllowedVar
for _, requiredVar := range t.Requires.Vars {
varValue, _ := t.Vars.Get(requiredVar.Name)
value, isString := varValue.Value.(string)
if isString && requiredVar.Enum != nil && !slices.Contains(requiredVar.Enum, value) {
notAllowedValuesVars = append(notAllowedValuesVars, errors.NotAllowedVar{
Value: value,
Enum: requiredVar.Enum,
Name: requiredVar.Name,
})
}
}
if len(notAllowedValuesVars) > 0 {
return &errors.TaskNotAllowedVars{
TaskName: t.Name(),

View File

@@ -14,6 +14,7 @@ import (
"github.com/go-task/task/v3/errors"
"github.com/go-task/task/v3/internal/compiler"
"github.com/go-task/task/v3/internal/env"
"github.com/go-task/task/v3/internal/execext"
"github.com/go-task/task/v3/internal/filepathext"
"github.com/go-task/task/v3/internal/logger"
@@ -109,13 +110,14 @@ func (e *Executor) setupTempDir() error {
return nil
}
if os.Getenv("TASK_TEMP_DIR") == "" {
tempDir := env.GetTaskEnv("TEMP_DIR")
if tempDir == "" {
e.TempDir = TempDir{
Remote: filepathext.SmartJoin(e.Dir, ".task"),
Fingerprint: filepathext.SmartJoin(e.Dir, ".task"),
}
} else if filepath.IsAbs(os.Getenv("TASK_TEMP_DIR")) || strings.HasPrefix(os.Getenv("TASK_TEMP_DIR"), "~") {
tempDir, err := execext.Expand(os.Getenv("TASK_TEMP_DIR"))
} else if filepath.IsAbs(tempDir) || strings.HasPrefix(tempDir, "~") {
tempDir, err := execext.Expand(tempDir)
if err != nil {
return err
}
@@ -128,14 +130,15 @@ func (e *Executor) setupTempDir() error {
} else {
e.TempDir = TempDir{
Remote: filepathext.SmartJoin(e.Dir, os.Getenv("TASK_TEMP_DIR")),
Fingerprint: filepathext.SmartJoin(e.Dir, os.Getenv("TASK_TEMP_DIR")),
Remote: filepathext.SmartJoin(e.Dir, tempDir),
Fingerprint: filepathext.SmartJoin(e.Dir, tempDir),
}
}
if os.Getenv("TASK_REMOTE_DIR") != "" {
if filepath.IsAbs(os.Getenv("TASK_REMOTE_DIR")) || strings.HasPrefix(os.Getenv("TASK_REMOTE_DIR"), "~") {
remoteTempDir, err := execext.Expand(os.Getenv("TASK_REMOTE_DIR"))
remoteDir := env.GetTaskEnv("REMOTE_DIR")
if remoteDir != "" {
if filepath.IsAbs(remoteDir) || strings.HasPrefix(remoteDir, "~") {
remoteTempDir, err := execext.Expand(remoteDir)
if err != nil {
return err
}

View File

@@ -185,6 +185,11 @@ func (e *Executor) RunTask(ctx context.Context, call *ast.Call) error {
if err != nil {
return err
}
if err := e.areTaskRequiredVarsAllowedValuesSet(t); err != nil {
return err
}
if !e.Watch && atomic.AddInt32(e.taskCallCount[t.Task], 1) >= MaximumTaskCall {
return &errors.TaskCalledTooManyTimesError{
TaskName: t.Task,
@@ -522,7 +527,7 @@ func (e *Executor) GetTaskList(filters ...FilterFunc) ([]*ast.Task, error) {
// Compile the list of tasks
for i := range tasks {
g.Go(func() error {
compiledTask, err := e.FastCompiledTask(&ast.Call{Task: tasks[i].Task})
compiledTask, err := e.CompiledTaskForTaskList(&ast.Call{Task: tasks[i].Task})
if err != nil {
return err
}

View File

@@ -2,6 +2,7 @@ package task_test
import (
"bytes"
"cmp"
"context"
"fmt"
"io"
@@ -134,8 +135,7 @@ func TestEnv(t *testing.T) {
},
}
tt.Run(t)
t.Setenv("TASK_X_ENV_PRECEDENCE", "1")
experiments.EnvPrecedence = experiments.New("ENV_PRECEDENCE")
enableExperimentForTest(t, &experiments.EnvPrecedence, "1")
ttt := fileContentTest{
Dir: "testdata/env",
Target: "overridden",
@@ -180,12 +180,12 @@ func TestRequires(t *testing.T) {
}
require.NoError(t, e.Setup())
require.ErrorContains(t, e.Run(context.Background(), &ast.Call{Task: "missing-var"}), "task: Task \"missing-var\" cancelled because it is missing required variables: foo")
require.ErrorContains(t, e.Run(context.Background(), &ast.Call{Task: "missing-var"}), "task: Task \"missing-var\" cancelled because it is missing required variables: FOO")
buff.Reset()
require.NoError(t, e.Setup())
vars := ast.NewVars()
vars.Set("foo", ast.Var{Value: "bar"})
vars.Set("FOO", ast.Var{Value: "bar"})
require.NoError(t, e.Run(context.Background(), &ast.Call{
Task: "missing-var",
Vars: vars,
@@ -193,11 +193,15 @@ func TestRequires(t *testing.T) {
buff.Reset()
require.NoError(t, e.Setup())
require.ErrorContains(t, e.Run(context.Background(), &ast.Call{Task: "validation-var", Vars: vars}), "task: Task \"validation-var\" cancelled because it is missing required variables:\n - foo has an invalid value : 'bar' (allowed values : [one two])")
require.ErrorContains(t, e.Run(context.Background(), &ast.Call{Task: "validation-var", Vars: vars}), "task: Task \"validation-var\" cancelled because it is missing required variables:\n - FOO has an invalid value : 'bar' (allowed values : [one two])")
buff.Reset()
require.NoError(t, e.Setup())
vars.Set("foo", ast.Var{Value: "one"})
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "validation-var-dynamic", Vars: vars}))
buff.Reset()
require.NoError(t, e.Setup())
vars.Set("FOO", ast.Var{Value: "one"})
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "validation-var", Vars: vars}))
buff.Reset()
@@ -965,10 +969,13 @@ func TestStatusVariables(t *testing.T) {
Verbose: true,
}
require.NoError(t, e.Setup())
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "build"}))
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "build-checksum"}))
assert.Contains(t, buff.String(), "3e464c4b03f4b65d740e1e130d4d108a")
buff.Reset()
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "build-ts"}))
inf, err := os.Stat(filepathext.SmartJoin(dir, "source.txt"))
require.NoError(t, err)
ts := fmt.Sprintf("%d", inf.ModTime().Unix())
@@ -998,10 +1005,12 @@ func TestCmdsVariables(t *testing.T) {
Verbose: true,
}
require.NoError(t, e.Setup())
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "build"}))
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "build-checksum"}))
assert.Contains(t, buff.String(), "3e464c4b03f4b65d740e1e130d4d108a")
buff.Reset()
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "build-ts"}))
inf, err := os.Stat(filepathext.SmartJoin(dir, "source.txt"))
require.NoError(t, err)
ts := fmt.Sprintf("%d", inf.ModTime().Unix())
@@ -1011,27 +1020,6 @@ func TestCmdsVariables(t *testing.T) {
assert.Contains(t, buff.String(), tf)
}
func TestInit(t *testing.T) {
t.Parallel()
const dir = "testdata/init"
file := filepathext.SmartJoin(dir, "Taskfile.yml")
_ = os.Remove(file)
if _, err := os.Stat(file); err == nil {
t.Errorf("Taskfile.yml should not exist")
}
if err := task.InitTaskfile(io.Discard, dir); err != nil {
t.Error(err)
}
if _, err := os.Stat(file); err != nil {
t.Errorf("Taskfile.yml should exist")
}
_ = os.Remove(file)
}
func TestCyclicDep(t *testing.T) {
t.Parallel()
@@ -1239,6 +1227,10 @@ func TestIncludesRemote(t *testing.T) {
firstRemote: srv.URL + "/first/Taskfile.yml",
secondRemote: "./second/Taskfile.yml",
},
{
firstRemote: srv.URL + "/first/",
secondRemote: srv.URL + "/first/second/",
},
}
tasks := []string{
@@ -3224,6 +3216,110 @@ func TestReference(t *testing.T) {
}
}
func TestVarInheritance(t *testing.T) {
enableExperimentForTest(t, &experiments.EnvPrecedence, "1")
tests := []struct {
name string
want string
call string
}{
{
name: "shell",
want: "shell\nshell\n",
},
{
name: "entrypoint-global-dotenv",
want: "entrypoint-global-dotenv\nentrypoint-global-dotenv\n",
},
{
name: "entrypoint-global-vars",
want: "entrypoint-global-vars\nentrypoint-global-vars\n",
},
{
// We can't send env vars to a called task, so the env var is not overridden
name: "entrypoint-task-call-vars",
want: "entrypoint-task-call-vars\nentrypoint-global-vars\n",
},
{
// Dotenv doesn't set variables
name: "entrypoint-task-call-dotenv",
want: "entrypoint-task-call-vars\nentrypoint-task-call-dotenv\n",
},
{
name: "entrypoint-task-call-task-vars",
want: "entrypoint-task-call-task-vars\nentrypoint-task-call-task-vars\n",
},
{
// Dotenv doesn't set variables
name: "entrypoint-task-dotenv",
want: "entrypoint-global-vars\nentrypoint-task-dotenv\n",
},
{
name: "entrypoint-task-vars",
want: "entrypoint-task-vars\nentrypoint-task-vars\n",
},
// {
// // Dotenv not currently allowed in included taskfiles
// name: "included-global-dotenv",
// want: "included-global-dotenv\nincluded-global-dotenv\n",
// },
{
name: "included-global-vars",
want: "included-global-vars\nincluded-global-vars\n",
call: "included",
},
{
// We can't send env vars to a called task, so the env var is not overridden
name: "included-task-call-vars",
want: "included-task-call-vars\nincluded-global-vars\n",
call: "included",
},
{
// Dotenv doesn't set variables
// Dotenv not currently allowed in included taskfiles (but doesn't error in a task)
name: "included-task-call-dotenv",
want: "included-task-call-vars\nincluded-global-vars\n",
call: "included",
},
{
name: "included-task-call-task-vars",
want: "included-task-call-task-vars\nincluded-task-call-task-vars\n",
call: "included",
},
{
// Dotenv doesn't set variables
// Somehow dotenv is working here!
name: "included-task-dotenv",
want: "included-global-vars\nincluded-task-dotenv\n",
call: "included",
},
{
name: "included-task-vars",
want: "included-task-vars\nincluded-task-vars\n",
call: "included",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
var buff bytes.Buffer
t.Setenv("VAR", "shell")
t.Setenv("ENV", "shell")
e := task.Executor{
Dir: fmt.Sprintf("testdata/var_inheritance/v3/%s", test.name),
Stdout: &buff,
Stderr: &buff,
Silent: true,
Force: true,
}
call := cmp.Or(test.call, "default")
require.NoError(t, e.Setup())
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: call}))
assert.Equal(t, test.want, buff.String())
})
}
}
// enableExperimentForTest enables the experiment behind pointer e for the duration of test t and sub-tests,
// with the experiment being restored to its previous state when tests complete.
//
@@ -3231,12 +3327,11 @@ func TestReference(t *testing.T) {
// because the experiment settings are parsed during experiments.init(), before any tests run.
func enableExperimentForTest(t *testing.T, e *experiments.Experiment, val string) {
t.Helper()
prev := *e
*e = experiments.Experiment{
Name: prev.Name,
Enabled: true,
Value: val,
Name: prev.Name,
AllowedValues: []string{val},
Value: val,
}
t.Cleanup(func() { *e = prev })
}

View File

@@ -175,7 +175,7 @@ type Var struct {
}
func (v *Var) UnmarshalYAML(node *yaml.Node) error {
if experiments.MapVariables.Enabled {
if experiments.MapVariables.Enabled() {
// This implementation is not backwards-compatible and replaces the 'sh' key with map variables
if experiments.MapVariables.Value == "1" {

View File

@@ -64,7 +64,7 @@ func NewNode(
}
if node.Remote() && !experiments.RemoteTaskfiles.Enabled {
if node.Remote() && !experiments.RemoteTaskfiles.Enabled() {
return nil, errors.New("task: Remote taskfiles are not enabled. You can read more about this experiment and how to enable it at https://taskfile.dev/experiments/remote-taskfiles")
}
return node, err

View File

@@ -17,9 +17,10 @@ import (
// An HTTPNode is a node that reads a Taskfile from a remote location via HTTP.
type HTTPNode struct {
*BaseNode
URL *url.URL
logger *logger.Logger
timeout time.Duration
URL *url.URL // stores url pointing actual remote file. (e.g. with Taskfile.yml)
entrypoint string // stores entrypoint url. used for building graph vertices.
logger *logger.Logger
timeout time.Duration
}
func NewHTTPNode(
@@ -40,15 +41,16 @@ func NewHTTPNode(
}
return &HTTPNode{
BaseNode: base,
URL: url,
timeout: timeout,
logger: l,
BaseNode: base,
URL: url,
entrypoint: entrypoint,
timeout: timeout,
logger: l,
}, nil
}
func (node *HTTPNode) Location() string {
return node.URL.String()
return node.entrypoint
}
func (node *HTTPNode) Remote() bool {
@@ -119,6 +121,6 @@ func (node *HTTPNode) ResolveDir(dir string) (string, error) {
}
func (node *HTTPNode) FilenameAndLastDir() (string, string) {
dir, filename := filepath.Split(node.URL.Path)
dir, filename := filepath.Split(node.entrypoint)
return filepath.Base(dir), filename
}

View File

@@ -1,10 +1,16 @@
version: '3'
tasks:
build:
build-checksum:
sources:
- ./source.txt
cmds:
- echo "{{.CHECKSUM}}"
- echo "{{.TIMESTAMP.Unix}}"
- echo "{{.TIMESTAMP}}"
build-ts:
method: timestamp
sources:
- ./source.txt
cmds:
- echo '{{.TIMESTAMP.Unix}}'
- echo '{{.TIMESTAMP}}'

View File

@@ -7,7 +7,7 @@ tasks:
missing-var:
requires:
vars:
- foo
- FOO
cmd: echo "{{.foo}}"
var-defined-in-task:
@@ -19,10 +19,21 @@ tasks:
cmd: echo "{{.FOO}}"
validation-var-dynamic:
vars:
FOO:
sh: echo "one"
requires:
vars:
- name: FOO
enum: ['one', 'two']
validation-var:
requires:
vars:
- name: foo
- name: FOO
enum: ['one', 'two']

View File

@@ -1,10 +1,16 @@
version: '3'
tasks:
build:
build-checksum:
sources:
- ./source.txt
status:
- echo "{{.CHECKSUM}}"
build-ts:
method: timestamp
sources:
- ./source.txt
status:
- echo '{{.TIMESTAMP.Unix}}'
- echo '{{.TIMESTAMP}}'

View File

@@ -0,0 +1,11 @@
version: '3'
silent: true
dotenv:
- 'global.env'
tasks:
default:
cmds:
- 'echo "{{.VAR}}"'
- 'echo "$ENV"'

View File

@@ -0,0 +1,2 @@
VAR=entrypoint-global-dotenv
ENV=entrypoint-global-dotenv

View File

@@ -0,0 +1,15 @@
version: '3'
silent: true
dotenv:
- 'global.env'
vars:
VAR: entrypoint-global-vars
env:
ENV: entrypoint-global-vars
tasks:
default:
cmds:
- 'echo "{{.VAR}}"'
- 'echo "$ENV"'

View File

@@ -0,0 +1,2 @@
VAR=entrypoint-global-dotenv
ENV=entrypoint-global-dotenv

View File

@@ -0,0 +1,25 @@
version: '3'
silent: true
dotenv:
- 'global.env'
vars:
VAR: entrypoint-global-vars
env:
ENV: entrypoint-global-vars
tasks:
default:
dotenv:
- 'task.env'
cmds:
- task: called-task
vars:
VAR: entrypoint-task-call-vars
called-task:
dotenv:
- 'called-task.env'
cmds:
- 'echo "{{.VAR}}"'
- 'echo "$ENV"'

View File

@@ -0,0 +1,2 @@
VAR=entrypoint-task-call-dotenv
ENV=entrypoint-task-call-dotenv

View File

@@ -0,0 +1,2 @@
VAR=entrypoint-global-dotenv
ENV=entrypoint-global-dotenv

View File

@@ -0,0 +1,2 @@
VAR=entrypoint-task-dotenv
ENV=entrypoint-task-dotenv

View File

@@ -0,0 +1,27 @@
version: '3'
silent: true
dotenv:
- 'global.env'
vars:
VAR: entrypoint-global-vars
env:
ENV: entrypoint-global-vars
tasks:
default:
dotenv:
- 'task.env'
cmds:
- task: called-task
vars:
VAR: entrypoint-task-call-vars
called-task:
vars:
VAR: entrypoint-task-call-task-vars
env:
ENV: entrypoint-task-call-task-vars
cmds:
- 'echo "{{.VAR}}"'
- 'echo "$ENV"'

View File

@@ -0,0 +1,2 @@
VAR=entrypoint-global-dotenv
ENV=entrypoint-global-dotenv

View File

@@ -0,0 +1,2 @@
VAR=entrypoint-task-dotenv
ENV=entrypoint-task-dotenv

View File

@@ -0,0 +1,23 @@
version: '3'
silent: true
dotenv:
- 'global.env'
vars:
VAR: entrypoint-global-vars
env:
ENV: entrypoint-global-vars
tasks:
default:
dotenv:
- 'task.env'
cmds:
- task: called-task
vars:
VAR: entrypoint-task-call-vars
called-task:
cmds:
- 'echo "{{.VAR}}"'
- 'echo "$ENV"'

View File

@@ -0,0 +1,2 @@
VAR=entrypoint-global-dotenv
ENV=entrypoint-global-dotenv

View File

@@ -0,0 +1,2 @@
VAR=entrypoint-task-dotenv
ENV=entrypoint-task-dotenv

View File

@@ -0,0 +1,17 @@
version: '3'
silent: true
dotenv:
- 'global.env'
vars:
VAR: entrypoint-global-vars
env:
ENV: entrypoint-global-vars
tasks:
default:
dotenv:
- 'task.env'
cmds:
- 'echo "{{.VAR}}"'
- 'echo "$ENV"'

View File

@@ -0,0 +1,2 @@
VAR=entrypoint-global-dotenv
ENV=entrypoint-global-dotenv

View File

@@ -0,0 +1,2 @@
VAR=entrypoint-task-dotenv
ENV=entrypoint-task-dotenv

View File

@@ -0,0 +1,21 @@
version: '3'
silent: true
dotenv:
- 'global.env'
vars:
VAR: entrypoint-global-vars
env:
ENV: entrypoint-global-vars
tasks:
default:
dotenv:
- 'task.env'
vars:
VAR: entrypoint-task-vars
env:
ENV: entrypoint-task-vars
cmds:
- 'echo "{{.VAR}}"'
- 'echo "$ENV"'

View File

@@ -0,0 +1,2 @@
VAR=entrypoint-global-dotenv
ENV=entrypoint-global-dotenv

View File

@@ -0,0 +1,2 @@
VAR=entrypoint-task-dotenv
ENV=entrypoint-task-dotenv

View File

@@ -0,0 +1,12 @@
version: '3'
silent: true
dotenv:
- 'global.env'
vars:
VAR: entrypoint-global-vars
env:
ENV: entrypoint-global-vars
includes:
included: included.yml

View File

@@ -0,0 +1,2 @@
VAR=entrypoint-global-dotenv
ENV=entrypoint-global-dotenv

View File

@@ -0,0 +1,13 @@
version: '3'
silent: true
vars:
VAR: included-global-vars
env:
ENV: included-global-vars
tasks:
default:
cmds:
- 'echo "{{.VAR}}"'
- 'echo "$ENV"'

View File

@@ -0,0 +1,12 @@
version: '3'
silent: true
dotenv:
- 'global.env'
vars:
VAR: entrypoint-global-vars
env:
ENV: entrypoint-global-vars
includes:
included: included.yml

View File

@@ -0,0 +1,2 @@
VAR=entrypoint-global-dotenv
ENV=entrypoint-global-dotenv

View File

@@ -0,0 +1,21 @@
version: '3'
silent: true
vars:
VAR: included-global-vars
env:
ENV: included-global-vars
tasks:
default:
dotenv:
- 'task.env'
cmds:
- task: called-task
vars:
VAR: included-task-call-vars
called-task:
cmds:
- 'echo "{{.VAR}}"'
- 'echo "$ENV"'

View File

@@ -0,0 +1,2 @@
VAR=included-task-dotenv
ENV=included-task-dotenv

View File

@@ -0,0 +1,12 @@
version: '3'
silent: true
dotenv:
- 'global.env'
vars:
VAR: entrypoint-global-vars
env:
ENV: entrypoint-global-vars
includes:
included: included.yml

View File

@@ -0,0 +1,2 @@
VAR=entrypoint-global-dotenv
ENV=entrypoint-global-dotenv

View File

@@ -0,0 +1,25 @@
version: '3'
silent: true
vars:
VAR: included-global-vars
env:
ENV: included-global-vars
tasks:
default:
dotenv:
- 'task.env'
cmds:
- task: called-task
vars:
VAR: included-task-call-vars
called-task:
vars:
VAR: included-task-call-task-vars
env:
ENV: included-task-call-task-vars
cmds:
- 'echo "{{.VAR}}"'
- 'echo "$ENV"'

View File

@@ -0,0 +1,2 @@
VAR=included-task-dotenv
ENV=included-task-dotenv

View File

@@ -0,0 +1,12 @@
version: '3'
silent: true
dotenv:
- 'global.env'
vars:
VAR: entrypoint-global-vars
env:
ENV: entrypoint-global-vars
includes:
included: included.yml

View File

@@ -0,0 +1,2 @@
VAR=entrypoint-global-dotenv
ENV=entrypoint-global-dotenv

View File

@@ -0,0 +1,21 @@
version: '3'
silent: true
vars:
VAR: included-global-vars
env:
ENV: included-global-vars
tasks:
default:
dotenv:
- 'task.env'
cmds:
- task: called-task
vars:
VAR: included-task-call-vars
called-task:
cmds:
- 'echo "{{.VAR}}"'
- 'echo "$ENV"'

View File

@@ -0,0 +1,2 @@
VAR=included-task-dotenv
ENV=included-task-dotenv

View File

@@ -0,0 +1,12 @@
version: '3'
silent: true
dotenv:
- 'global.env'
vars:
VAR: entrypoint-global-vars
env:
ENV: entrypoint-global-vars
includes:
included: included.yml

View File

@@ -0,0 +1,2 @@
VAR=entrypoint-global-dotenv
ENV=entrypoint-global-dotenv

View File

@@ -0,0 +1,15 @@
version: '3'
silent: true
vars:
VAR: included-global-vars
env:
ENV: included-global-vars
tasks:
default:
dotenv:
- 'task.env'
cmds:
- 'echo "{{.VAR}}"'
- 'echo "$ENV"'

View File

@@ -0,0 +1,2 @@
VAR=included-task-dotenv
ENV=included-task-dotenv

View File

@@ -0,0 +1,12 @@
version: '3'
silent: true
dotenv:
- 'global.env'
vars:
VAR: entrypoint-global-vars
env:
ENV: entrypoint-global-vars
includes:
included: included.yml

View File

@@ -0,0 +1,2 @@
VAR=entrypoint-global-dotenv
ENV=entrypoint-global-dotenv

View File

@@ -0,0 +1,19 @@
version: '3'
silent: true
vars:
VAR: included-global-vars
env:
ENV: included-global-vars
tasks:
default:
dotenv:
- 'task.env'
vars:
VAR: included-task-vars
env:
ENV: included-task-vars
cmds:
- 'echo "{{.VAR}}"'
- 'echo "$ENV"'

View File

@@ -0,0 +1,2 @@
VAR=included-task-dotenv
ENV=included-task-dotenv

View File

@@ -0,0 +1,12 @@
version: '3'
silent: true
dotenv:
- 'global.env'
vars:
VAR: entrypoint-global-vars
env:
ENV: entrypoint-global-vars
includes:
included: included.yml

View File

@@ -0,0 +1,2 @@
VAR=entrypoint-global-dotenv
ENV=entrypoint-global-dotenv

View File

@@ -0,0 +1,15 @@
version: '3'
silent: true
vars:
VAR: included-global-vars
env:
ENV: included-global-vars
tasks:
default:
dotenv:
- 'task.env'
cmds:
- 'echo "{{.VAR}}"'
- 'echo "$ENV"'

View File

@@ -0,0 +1,2 @@
VAR=included-task-dotenv
ENV=included-task-dotenv

View File

@@ -0,0 +1,9 @@
version: '3'
silent: true
tasks:
default:
cmds:
- 'echo "{{.VAR}}"'
- 'echo "$ENV"'

View File

@@ -27,6 +27,51 @@ func (e *Executor) FastCompiledTask(call *ast.Call) (*ast.Task, error) {
return e.compiledTask(call, false)
}
func (e *Executor) CompiledTaskForTaskList(call *ast.Call) (*ast.Task, error) {
origTask, err := e.GetTask(call)
if err != nil {
return nil, err
}
vars, err := e.Compiler.FastGetVariables(origTask, call)
if err != nil {
return nil, err
}
cache := &templater.Cache{Vars: vars}
return &ast.Task{
Task: origTask.Task,
Label: templater.Replace(origTask.Label, cache),
Desc: templater.Replace(origTask.Desc, cache),
Prompt: templater.Replace(origTask.Prompt, cache),
Summary: templater.Replace(origTask.Summary, cache),
Aliases: origTask.Aliases,
Sources: origTask.Sources,
Generates: origTask.Generates,
Dir: origTask.Dir,
Set: origTask.Set,
Shopt: origTask.Shopt,
Vars: vars,
Env: nil,
Dotenv: origTask.Dotenv,
Silent: origTask.Silent,
Interactive: origTask.Interactive,
Internal: origTask.Internal,
Method: origTask.Method,
Prefix: origTask.Prefix,
IgnoreError: origTask.IgnoreError,
Run: origTask.Run,
IncludeVars: origTask.IncludeVars,
IncludedTaskfileVars: origTask.IncludedTaskfileVars,
Platforms: origTask.Platforms,
Location: origTask.Location,
Requires: origTask.Requires,
Watch: origTask.Watch,
Namespace: origTask.Namespace,
}, nil
}
func (e *Executor) compiledTask(call *ast.Call, evaluateShVars bool) (*ast.Task, error) {
origTask, err := e.GetTask(call)
if err != nil {
@@ -128,18 +173,21 @@ func (e *Executor) compiledTask(call *ast.Call, evaluateShVars bool) (*ast.Task,
}
}
if len(origTask.Sources) > 0 {
timestampChecker := fingerprint.NewTimestampChecker(e.TempDir.Fingerprint, e.Dry)
checksumChecker := fingerprint.NewChecksumChecker(e.TempDir.Fingerprint, e.Dry)
if len(origTask.Sources) > 0 && origTask.Method != "none" {
var checker fingerprint.SourcesCheckable
for _, checker := range []fingerprint.SourcesCheckable{timestampChecker, checksumChecker} {
value, err := checker.Value(&new)
if err != nil {
return nil, err
}
vars.Set(strings.ToUpper(checker.Kind()), ast.Var{Live: value})
if origTask.Method == "timestamp" {
checker = fingerprint.NewTimestampChecker(e.TempDir.Fingerprint, e.Dry)
} else {
checker = fingerprint.NewChecksumChecker(e.TempDir.Fingerprint, e.Dry)
}
value, err := checker.Value(&new)
if err != nil {
return nil, err
}
vars.Set(strings.ToUpper(checker.Kind()), ast.Var{Live: value})
// Adding new variables, requires us to refresh the templaters
// cache of the the values manually
cache.ResetCache()

View File

@@ -83,10 +83,10 @@ add a new section. If you're updating an existing feature, ensure that the
documentation and any examples are up-to-date. Ensure that any examples follow
the [Taskfile Styleguide](/styleguide).
If you added a new field, command or flag, ensure that you add it to the
[API Reference](/api). New fields also need to be added to the [JSON
Schema][json-schema]. The descriptions for fields in the API reference and the
schema should match.
If you added a new command or flag, ensure that you add it to the [CLI
Reference](/reference/cli). New fields also need to be added to the [Schema
Reference](/reference/schema) and [JSON Schema][json-schema]. The descriptions
for fields in the docs and the schema should match.
### Writing tests

View File

@@ -58,7 +58,7 @@ four groups with the following ranges:
- Success (0)
- General errors (1-99)
- Taskfile errors (100-199)
- Task errors (200-299)
- Task errors (200-255)
A full list of the exit codes and their descriptions can be found below:

View File

@@ -192,7 +192,7 @@ version: '3'
env:
ENV: testing
dotenv: ['.env', '{{.ENV}}/.env.', '{{.HOME}}/.env']
dotenv: ['.env', '{{.ENV}}/.env', '{{.HOME}}/.env']
tasks:
greet:
@@ -210,7 +210,7 @@ env:
tasks:
greet:
dotenv: ['.env', '{{.ENV}}/.env.', '{{.HOME}}/.env']
dotenv: ['.env', '{{.ENV}}/.env', '{{.HOME}}/.env']
cmds:
- echo "Using $KEYNAME and endpoint $ENDPOINT"
```
@@ -226,7 +226,7 @@ env:
tasks:
greet:
dotenv: ['.env', '{{.ENV}}/.env.', '{{.HOME}}/.env']
dotenv: ['.env', '{{.ENV}}/.env', '{{.HOME}}/.env']
env:
KEYNAME: DIFFERENT_VALUE
cmds:
@@ -938,8 +938,8 @@ information.
You can use `--force` or `-f` if you want to force a task to run even when
up-to-date.
Also, `task --status [tasks]...` will exit with a non-zero exit code if any of
the tasks are not up-to-date.
Also, `task --status [tasks]...` will exit with a non-zero [exit
code](/reference/cli#exit-codes) if any of the tasks are not up-to-date.
`status` can be combined with the
[fingerprinting](#by-fingerprinting-locally-generated-files-and-their-sources)
@@ -1759,8 +1759,8 @@ commands are executed in the reverse order if you schedule multiple of them.
:::
A special variable `.EXIT_CODE` is exposed when a command exited with a non-zero
exit code. You can check its presence to know if the task completed successfully
or not:
[exit code](/reference/cli#exit-codes). You can check its presence to know if
the task completed successfully or not:
```yaml
version: '3'
@@ -1959,8 +1959,8 @@ tasks:
```
Warning prompts are called before executing a task. If a prompt is denied Task
will exit with [exit code](/api#exit-codes) 205. If approved, Task will continue
as normal.
will exit with [exit code](/reference/cli#exit-codes) 205. If approved, Task
will continue as normal.
```shell
task example

View File

@@ -24,8 +24,8 @@
"clsx": "^2.0.0",
"prism-react-renderer": "^2.1.0",
"raw-loader": "^4.0.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"remark-gfm": "^4.0.0",
"remark-github": "^12.0.0"
},
@@ -33,7 +33,7 @@
"@docusaurus/module-type-aliases": "^3.5.2",
"@docusaurus/tsconfig": "^3.5.2",
"@docusaurus/types": "^3.5.2",
"@types/react": "^18.2.29",
"@types/react": "^19.0.0",
"typescript": "^5.3.3"
},
"engines": {

View File

@@ -83,10 +83,10 @@ add a new section. If you're updating an existing feature, ensure that the
documentation and any examples are up-to-date. Ensure that any examples follow
the [Taskfile Styleguide](/styleguide).
If you added a new field, command or flag, ensure that you add it to the
[API Reference](/api). New fields also need to be added to the [JSON
Schema][json-schema]. The descriptions for fields in the API reference and the
schema should match.
If you added a new command or flag, ensure that you add it to the [CLI
Reference](/reference/cli). New fields also need to be added to the [Schema
Reference](/reference/schema) and [JSON Schema][json-schema]. The descriptions
for fields in the docs and the schema should match.
### Writing tests

View File

@@ -58,7 +58,7 @@ four groups with the following ranges:
- Success (0)
- General errors (1-99)
- Taskfile errors (100-199)
- Task errors (200-299)
- Task errors (200-255)
A full list of the exit codes and their descriptions can be found below:

View File

@@ -192,7 +192,7 @@ version: '3'
env:
ENV: testing
dotenv: ['.env', '{{.ENV}}/.env.', '{{.HOME}}/.env']
dotenv: ['.env', '{{.ENV}}/.env', '{{.HOME}}/.env']
tasks:
greet:
@@ -210,7 +210,7 @@ env:
tasks:
greet:
dotenv: ['.env', '{{.ENV}}/.env.', '{{.HOME}}/.env']
dotenv: ['.env', '{{.ENV}}/.env', '{{.HOME}}/.env']
cmds:
- echo "Using $KEYNAME and endpoint $ENDPOINT"
```
@@ -226,7 +226,7 @@ env:
tasks:
greet:
dotenv: ['.env', '{{.ENV}}/.env.', '{{.HOME}}/.env']
dotenv: ['.env', '{{.ENV}}/.env', '{{.HOME}}/.env']
env:
KEYNAME: DIFFERENT_VALUE
cmds:
@@ -938,8 +938,8 @@ information.
You can use `--force` or `-f` if you want to force a task to run even when
up-to-date.
Also, `task --status [tasks]...` will exit with a non-zero exit code if any of
the tasks are not up-to-date.
Also, `task --status [tasks]...` will exit with a non-zero [exit
code](/reference/cli#exit-codes) if any of the tasks are not up-to-date.
`status` can be combined with the
[fingerprinting](#by-fingerprinting-locally-generated-files-and-their-sources)
@@ -1759,8 +1759,8 @@ commands are executed in the reverse order if you schedule multiple of them.
:::
A special variable `.EXIT_CODE` is exposed when a command exited with a non-zero
exit code. You can check its presence to know if the task completed successfully
or not:
[exit code](/reference/cli#exit-codes). You can check its presence to know if
the task completed successfully or not:
```yaml
version: '3'
@@ -1959,8 +1959,8 @@ tasks:
```
Warning prompts are called before executing a task. If a prompt is denied Task
will exit with [exit code](/api#exit-codes) 205. If approved, Task will continue
as normal.
will exit with [exit code](/reference/cli#exit-codes) 205. If approved, Task
will continue as normal.
```shell
task example

File diff suppressed because it is too large Load Diff