diff --git a/.github/workflows/issue-awaiting-response.yml b/.github/workflows/issue-awaiting-response.yml index d22e19b4ff..3d6ef829ae 100644 --- a/.github/workflows/issue-awaiting-response.yml +++ b/.github/workflows/issue-awaiting-response.yml @@ -8,7 +8,7 @@ jobs: issue-awaiting-response: runs-on: ubuntu-latest steps: - - uses: actions/github-script@v6 + - uses: actions/github-script@v7 with: github-token: ${{secrets.GH_PAT}} script: | diff --git a/.github/workflows/issue-closed.yml b/.github/workflows/issue-closed.yml index 49ce8fb451..e51f50d265 100644 --- a/.github/workflows/issue-closed.yml +++ b/.github/workflows/issue-closed.yml @@ -8,7 +8,7 @@ jobs: issue-closed: runs-on: ubuntu-latest steps: - - uses: actions/github-script@v6 + - uses: actions/github-script@v7 with: github-token: ${{secrets.GH_PAT}} script: | diff --git a/.github/workflows/issue-experiment.yml b/.github/workflows/issue-experiment.yml index f949f884a0..5f0f1bb639 100644 --- a/.github/workflows/issue-experiment.yml +++ b/.github/workflows/issue-experiment.yml @@ -9,7 +9,7 @@ jobs: if: github.event.label.name == format('experiment{0} proposed', ':') runs-on: ubuntu-latest steps: - - uses: actions/github-script@v6 + - uses: actions/github-script@v7 with: github-token: ${{secrets.GH_PAT}} script: | @@ -23,7 +23,7 @@ jobs: if: github.event.label.name == format('experiment{0} draft', ':') runs-on: ubuntu-latest steps: - - uses: actions/github-script@v6 + - uses: actions/github-script@v7 with: github-token: ${{secrets.GH_PAT}} script: | @@ -37,7 +37,7 @@ jobs: if: github.event.label.name == format('experiment{0} candidate', ':') runs-on: ubuntu-latest steps: - - uses: actions/github-script@v6 + - uses: actions/github-script@v7 with: github-token: ${{secrets.GH_PAT}} script: | @@ -51,7 +51,7 @@ jobs: if: github.event.label.name == format('experiment{0} stable', ':') runs-on: ubuntu-latest steps: - - uses: actions/github-script@v6 + - uses: actions/github-script@v7 with: github-token: ${{secrets.GH_PAT}} script: | @@ -65,7 +65,7 @@ jobs: if: github.event.label.name == format('experiment{0} released', ':') runs-on: ubuntu-latest steps: - - uses: actions/github-script@v6 + - uses: actions/github-script@v7 with: github-token: ${{secrets.GH_PAT}} script: | @@ -85,7 +85,7 @@ jobs: if: github.event.label.name == format('experiment{0} abandoned', ':') runs-on: ubuntu-latest steps: - - uses: actions/github-script@v6 + - uses: actions/github-script@v7 with: github-token: ${{secrets.GH_PAT}} script: | @@ -105,7 +105,7 @@ jobs: if: github.event.label.name == format('experiment{0} superseded', ':') runs-on: ubuntu-latest steps: - - uses: actions/github-script@v6 + - uses: actions/github-script@v7 with: github-token: ${{secrets.GH_PAT}} script: | diff --git a/.github/workflows/issue-needs-triage.yml b/.github/workflows/issue-needs-triage.yml index 5193b17af7..1fc4fb6ee9 100644 --- a/.github/workflows/issue-needs-triage.yml +++ b/.github/workflows/issue-needs-triage.yml @@ -8,7 +8,7 @@ jobs: issue-needs-triage: runs-on: ubuntu-latest steps: - - uses: actions/github-script@v6 + - uses: actions/github-script@v7 with: github-token: ${{secrets.GH_PAT}} script: | diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index d7361f602b..60fd1bdfe2 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -47,7 +47,7 @@ jobs: - uses: actions/checkout@v4 - name: Get changed files in the docs folder id: changed-files-specific - uses: tj-actions/changed-files@v44 + uses: tj-actions/changed-files@v45 with: files: website/versioned_docs/** diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ff8ab1a271..7d92660daf 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ jobs: go-version: 1.22.x - name: Run GoReleaser - uses: goreleaser/goreleaser-action@v2 + uses: goreleaser/goreleaser-action@v6 with: version: latest args: release --clean diff --git a/.goreleaser.yml b/.goreleaser.yml index 0bb49ebe1e..ebcd920b80 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -49,7 +49,7 @@ release: draft: true snapshot: - version_template: "{{.Tag}}" + version_template: "{{.Version}}" checksum: name_template: "task_checksums.txt" diff --git a/.nvmrc b/.nvmrc index b460d6f2de..17719ce25a 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -18.12.1 +18.20.4 diff --git a/CHANGELOG.md b/CHANGELOG.md index 49190fed4c..b8d0a3ffac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,26 @@ # Changelog +## Unreleased + +- Fixed output of some functions (e.g. `splitArgs`/`splitLines`) not working in + for loops (#1822, #1823 by @stawii). +- Added a new `TASK_OFFLINE` environment variable to configure the `--offline` + flag and expose it as a special variable in the templating system (#1470, + #1716 by @vmaerten and @pd93). +- Fixed a bug where multiple remote includes caused all prompts to display + without waiting for user input (#1832, #1833 by @vmaerten and @pd93). +- When using the "[Remote Taskfiles](https://taskfile.dev/experiments/remote-taskfiles/)". + experiment, you can now include Taskfiles from Git repositories (#1652 by @vmaerten). +- Improved the error message when a dotenv file cannot be parsed (#1842 by @pbitty). +- Fix issue with directory when using the remote experiment (#1757 by @pbitty). +- Fixed an issue where a special variable was used in combination with a dotenv file (#1232, #1810 by @vmaerten). +- Refactor the way Task reads Taskfiles to improve readability (#1771 by @pbitty). + +## v3.39.2 - 2024-09-19 + +- Fix dynamic variables not working properly for a defer: statement (#1803, + #1818 by @vmaerten). + ## v3.39.1 - 2024-09-18 - Added Renovate configuration to automatically create PRs to keep dependencies diff --git a/cmd/task/task.go b/cmd/task/task.go index 439ca9c5a7..b33d688e4e 100644 --- a/cmd/task/task.go +++ b/cmd/task/task.go @@ -198,6 +198,7 @@ func run() error { globals.Set("CLI_FORCE", ast.Var{Value: flags.Force || flags.ForceAll}) globals.Set("CLI_SILENT", ast.Var{Value: flags.Silent}) globals.Set("CLI_VERBOSE", ast.Var{Value: flags.Verbose}) + globals.Set("CLI_OFFLINE", ast.Var{Value: flags.Offline}) e.Taskfile.Vars.Merge(globals, nil) if !flags.Watch { diff --git a/go.mod b/go.mod index 1c323236c6..73a5a0a108 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,8 @@ require ( github.com/davecgh/go-spew v1.1.1 github.com/dominikbraun/graph v0.23.0 github.com/fatih/color v1.17.0 + github.com/go-git/go-billy/v5 v5.5.0 + github.com/go-git/go-git/v5 v5.12.0 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 @@ -19,21 +21,40 @@ require ( github.com/sajari/fuzzy v1.0.0 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.9.0 + github.com/whilp/git-urls v1.0.0 github.com/zeebo/xxh3 v1.0.2 golang.org/x/sync v0.8.0 - golang.org/x/term v0.24.0 + golang.org/x/term v0.25.0 gopkg.in/yaml.v3 v3.0.1 mvdan.cc/sh/v3 v3.9.0 ) require ( + dario.cat/mergo v1.0.0 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/ProtonMail/go-crypto v1.0.0 // indirect + github.com/cloudflare/circl v1.3.7 // indirect + github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/dlclark/regexp2 v1.11.0 // indirect - github.com/klauspost/cpuid/v2 v2.0.9 // 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 + github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect + github.com/kevinburke/ssh_config v1.2.0 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect 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/pmezard/go-difflib v1.0.0 // indirect + github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect + github.com/skeema/knownhosts v1.2.2 // indirect github.com/stretchr/objx v0.5.2 // indirect - golang.org/x/sys v0.25.0 // indirect - gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect + github.com/xanzy/ssh-agent v0.3.3 // indirect + golang.org/x/crypto v0.23.0 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/tools v0.21.0 // indirect + gopkg.in/warnings.v0 v0.1.2 // indirect ) diff --git a/go.sum b/go.sum index 232baa2794..a44e311937 100644 --- a/go.sum +++ b/go.sum @@ -1,39 +1,80 @@ +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= github.com/Ladicle/tabwriter v1.0.0 h1:DZQqPvMumBDwVNElso13afjYLNp0Z7pHqHnu0r4t9Dg= github.com/Ladicle/tabwriter v1.0.0/go.mod h1:c4MdCjxQyTbGuQO/gvqJ+IA/89UEwrsD6hUCW98dyp4= github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78= +github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= 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/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= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= +github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= +github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= +github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0= github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= +github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/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/dominikbraun/graph v0.23.0 h1:TdZB4pPqCLFxYhdyMFb1TBdFxp8XLcJfTTBQucVPgCo= github.com/dominikbraun/graph v0.23.0/go.mod h1:yOjYyogZLY1LSG9E33JWZJiq5k83Qy2C6POAuiViluc= +github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= +github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= +github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE= +github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= +github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= +github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= +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.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys= +github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY= github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-task/template v0.1.0 h1:ym/r2G937RZA1bsgiWedNnY9e5kxDT+3YcoAnuIetTE= github.com/go-task/template v0.1.0/go.mod h1:RgwRaZK+kni/hJJ7/AaOE2lPQFPbAdji/DyhC6pxo4k= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= +github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +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.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -47,10 +88,16 @@ github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4 github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= +github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= +github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= 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/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks= github.com/otiai10/mint v1.5.1/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/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/radovskyb/watcher v1.0.7 h1:AYePLih6dpmS32vlHfhCeli8127LzkIgwJGcwwe8tUE= @@ -59,27 +106,101 @@ github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/sajari/fuzzy v1.0.0 h1:+FmwVvJErsd0d0hAPlj4CxqxUtQY/fOoY0DwX4ykpRY= github.com/sajari/fuzzy v1.0.0/go.mod h1:OjYR6KxoWOe9+dOlXeiCJd4dIbED4Oo8wpS89o0pwOo= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A= +github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/whilp/git-urls v1.0.0 h1:95f6UMWN5FKW71ECsXRUd3FVYiXdrE7aX4NZKcPmIjU= +github.com/whilp/git-urls v1.0.0/go.mod h1:J16SAmobsqc3Qcy98brfl5f5+e0clUvg1krgwk/qCfE= +github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= +github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= -golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.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.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= +golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= mvdan.cc/sh/v3 v3.9.0 h1:it14fyjCdQUk4jf/aYxLO3FG8jFarR9GzMCtnlvvD7c= diff --git a/internal/compiler/compiler.go b/internal/compiler/compiler.go index 8627909a7a..67ea07e456 100644 --- a/internal/compiler/compiler.go +++ b/internal/compiler/compiler.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "fmt" + "maps" "os" "path/filepath" "strings" @@ -45,14 +46,12 @@ func (c *Compiler) FastGetVariables(t *ast.Task, call *ast.Call) (*ast.Vars, err func (c *Compiler) getVariables(t *ast.Task, call *ast.Call, evaluateShVars bool) (*ast.Vars, error) { result := GetEnviron() - if t != nil { - specialVars, err := c.getSpecialVars(t, call) - if err != nil { - return nil, err - } - for k, v := range specialVars { - result.Set(k, ast.Var{Value: v}) - } + specialVars, err := c.getSpecialVars(t, call) + if err != nil { + return nil, err + } + for k, v := range specialVars { + result.Set(k, ast.Var{Value: v}) } getRangeFunc := func(dir string) func(k string, v ast.Var) error { @@ -180,15 +179,18 @@ func (c *Compiler) ResetCache() { } func (c *Compiler) getSpecialVars(t *ast.Task, call *ast.Call) (map[string]string, error) { - return map[string]string{ - "TASK": t.Task, - "ALIAS": call.Task, + allVars := map[string]string{ "TASK_EXE": filepath.ToSlash(os.Args[0]), "ROOT_TASKFILE": filepathext.SmartJoin(c.Dir, c.Entrypoint), "ROOT_DIR": c.Dir, - "TASKFILE": t.Location.Taskfile, - "TASKFILE_DIR": filepath.Dir(t.Location.Taskfile), "USER_WORKING_DIR": c.UserWorkingDir, "TASK_VERSION": version.GetVersion(), - }, nil + } + if t != nil { + maps.Copy(allVars, map[string]string{"TASK": t.Task, "TASKFILE": t.Location.Taskfile, "TASKFILE_DIR": filepath.Dir(t.Location.Taskfile)}) + } + if call != nil { + maps.Copy(allVars, map[string]string{"ALIAS": call.Task}) + } + return allVars, nil } diff --git a/internal/flags/flags.go b/internal/flags/flags.go index 4ae50c2efc..8ce7783504 100644 --- a/internal/flags/flags.go +++ b/internal/flags/flags.go @@ -1,9 +1,11 @@ package flags import ( + "cmp" "errors" "log" "os" + "strconv" "time" "github.com/spf13/pflag" @@ -77,7 +79,10 @@ func init() { log.Print(usage) pflag.PrintDefaults() } - + offline, err := strconv.ParseBool(cmp.Or(os.Getenv("TASK_OFFLINE"), "false")) + if err != nil { + offline = false + } pflag.BoolVar(&Version, "version", false, "Show Task version.") pflag.BoolVarP(&Help, "help", "h", false, "Shows Task usage.") pflag.BoolVarP(&Init, "init", "i", false, "Creates a new Taskfile.yml in the current folder.") @@ -120,7 +125,7 @@ func init() { // Remote Taskfiles experiment will adds the "download" and "offline" flags if experiments.RemoteTaskfiles.Enabled { pflag.BoolVar(&Download, "download", false, "Downloads a cached version of a remote Taskfile.") - pflag.BoolVar(&Offline, "offline", false, "Forces Task to only use local or cached Taskfiles.") + pflag.BoolVar(&Offline, "offline", offline, "Forces Task to only use local or cached Taskfiles.") pflag.DurationVar(&Timeout, "timeout", time.Second*10, "Timeout for downloading remote Taskfiles.") pflag.BoolVar(&ClearCache, "clear-cache", false, "Clear the remote cache.") } diff --git a/internal/version/version.go b/internal/version/version.go index 89a241a368..9685b75c85 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -18,7 +18,9 @@ func init() { if version == "" { version = info.Main.Version } - sum = info.Main.Sum + if sum == "" { + sum = info.Main.Sum + } } } diff --git a/package-lock.json b/package-lock.json index 25687e60bc..9d7efe1a52 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@go-task/cli", - "version": "3.39.1", + "version": "3.39.2", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 5eb3a72252..00f849e7a4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@go-task/cli", - "version": "3.39.1", + "version": "3.39.2", "description": "A task runner / simpler Make alternative written in Go", "scripts": { "postinstall": "go-npm install", diff --git a/task.go b/task.go index 0b24da0729..5c63de346b 100644 --- a/task.go +++ b/task.go @@ -330,7 +330,7 @@ func (e *Executor) runDeferred(t *ast.Task, call *ast.Call, i int, deferredExitC } cmd := t.Cmds[i] - vars, _ := e.Compiler.FastGetVariables(origTask, call) + vars, _ := e.Compiler.GetVariables(origTask, call) cache := &templater.Cache{Vars: vars} extra := map[string]any{} diff --git a/task_test.go b/task_test.go index 7915ff41bb..599277c8c5 100644 --- a/task_test.go +++ b/task_test.go @@ -5,6 +5,10 @@ import ( "context" "fmt" "io" + "io/fs" + rand "math/rand/v2" + "net/http" + "net/http/httptest" "os" "path/filepath" "regexp" @@ -12,6 +16,7 @@ import ( "strings" "sync" "testing" + "time" "github.com/Masterminds/semver/v3" "github.com/stretchr/testify/assert" @@ -21,6 +26,7 @@ import ( "github.com/go-task/task/v3/errors" "github.com/go-task/task/v3/internal/experiments" "github.com/go-task/task/v3/internal/filepathext" + "github.com/go-task/task/v3/internal/logger" "github.com/go-task/task/v3/taskfile/ast" ) @@ -1075,6 +1081,107 @@ func TestIncludesMultiLevel(t *testing.T) { tt.Run(t) } +func TestIncludesRemote(t *testing.T) { + enableExperimentForTest(t, &experiments.RemoteTaskfiles, "1") + + dir := "testdata/includes_remote" + + srv := httptest.NewServer(http.FileServer(http.Dir(dir))) + defer srv.Close() + + tcs := []struct { + firstRemote string + secondRemote string + }{ + { + firstRemote: srv.URL + "/first/Taskfile.yml", + secondRemote: srv.URL + "/first/second/Taskfile.yml", + }, + { + firstRemote: srv.URL + "/first/Taskfile.yml", + secondRemote: "./second/Taskfile.yml", + }, + } + + tasks := []string{ + "first:write-file", + "first:second:write-file", + } + + for i, tc := range tcs { + t.Run(fmt.Sprint(i), func(t *testing.T) { + t.Setenv("FIRST_REMOTE_URL", tc.firstRemote) + t.Setenv("SECOND_REMOTE_URL", tc.secondRemote) + + var buff SyncBuffer + + executors := []struct { + name string + executor *task.Executor + }{ + { + name: "online, always download", + executor: &task.Executor{ + Dir: dir, + Stdout: &buff, + Stderr: &buff, + Timeout: time.Minute, + Insecure: true, + Logger: &logger.Logger{Stdout: &buff, Stderr: &buff, Verbose: true}, + + // Without caching + AssumeYes: true, + Download: true, + }, + }, + { + name: "offline, use cache", + executor: &task.Executor{ + Dir: dir, + Stdout: &buff, + Stderr: &buff, + Timeout: time.Minute, + Insecure: true, + Logger: &logger.Logger{Stdout: &buff, Stderr: &buff, Verbose: true}, + + // With caching + AssumeYes: false, + Download: false, + Offline: true, + }, + }, + } + + for j, e := range executors { + t.Run(fmt.Sprint(j), func(t *testing.T) { + require.NoError(t, e.executor.Setup()) + + for k, task := range tasks { + t.Run(task, func(t *testing.T) { + expectedContent := fmt.Sprint(rand.Int64()) + t.Setenv("CONTENT", expectedContent) + + outputFile := fmt.Sprintf("%d.%d.txt", i, k) + t.Setenv("OUTPUT_FILE", outputFile) + + path := filepath.Join(dir, outputFile) + require.NoError(t, os.RemoveAll(path)) + + require.NoError(t, e.executor.Run(context.Background(), &ast.Call{Task: task})) + + actualContent, err := os.ReadFile(path) + require.NoError(t, err) + assert.Equal(t, expectedContent, strings.TrimSpace(string(actualContent))) + }) + } + }) + } + + t.Log("\noutput:\n", buff.buf.String()) + }) + } +} + func TestIncludeCycle(t *testing.T) { const dir = "testdata/includes_cycle" @@ -1119,6 +1226,88 @@ func TestIncludesEmptyMain(t *testing.T) { tt.Run(t) } +func TestIncludesHttp(t *testing.T) { + enableExperimentForTest(t, &experiments.RemoteTaskfiles, "1") + + dir, err := filepath.Abs("testdata/includes_http") + require.NoError(t, err) + + srv := httptest.NewServer(http.FileServer(http.Dir(dir))) + defer srv.Close() + + t.Cleanup(func() { + // This test fills the .task/remote directory with cache entries because the include URL + // is different on every test due to the dynamic nature of the TCP port in srv.URL + if err := os.RemoveAll(filepath.Join(dir, ".task")); err != nil { + t.Logf("error cleaning up: %s", err) + } + }) + + taskfiles, err := fs.Glob(os.DirFS(dir), "root-taskfile-*.yml") + require.NoError(t, err) + + remotes := []struct { + name string + root string + }{ + { + name: "local", + root: ".", + }, + { + name: "http-remote", + root: srv.URL, + }, + } + + for _, taskfile := range taskfiles { + t.Run(taskfile, func(t *testing.T) { + for _, remote := range remotes { + t.Run(remote.name, func(t *testing.T) { + t.Setenv("INCLUDE_ROOT", remote.root) + entrypoint := filepath.Join(dir, taskfile) + + var buff SyncBuffer + e := task.Executor{ + Entrypoint: entrypoint, + Dir: dir, + Stdout: &buff, + Stderr: &buff, + Insecure: true, + Download: true, + AssumeYes: true, + Logger: &logger.Logger{Stdout: &buff, Stderr: &buff, Verbose: true}, + Timeout: time.Minute, + } + require.NoError(t, e.Setup()) + defer func() { t.Log("output:", buff.buf.String()) }() + + tcs := []struct { + name, dir string + }{ + { + name: "second-with-dir-1:third-with-dir-1:default", + dir: filepath.Join(dir, "dir-1"), + }, + { + name: "second-with-dir-1:third-with-dir-2:default", + dir: filepath.Join(dir, "dir-2"), + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + task, err := e.CompiledTask(&ast.Call{Task: tc.name}) + require.NoError(t, err) + assert.Equal(t, tc.dir, task.Dir) + }) + } + }) + } + }) + } +} + func TestIncludesDependencies(t *testing.T) { tt := fileContentTest{ Dir: "testdata/includes_deps", @@ -1690,6 +1879,18 @@ func TestDotenvHasEnvVarInPath(t *testing.T) { tt.Run(t) } +func TestTaskDotenvParseErrorMessage(t *testing.T) { + e := task.Executor{ + Dir: "testdata/dotenv/parse_error", + } + + path, _ := filepath.Abs(filepath.Join(e.Dir, ".env-with-error")) + expected := fmt.Sprintf("error reading env file %s:", path) + + err := e.Setup() + require.ErrorContains(t, err, expected) +} + func TestTaskDotenv(t *testing.T) { tt := fileContentTest{ Dir: "testdata/dotenv_task/default", @@ -1821,7 +2022,7 @@ func TestExitCodeZero(t *testing.T) { require.NoError(t, e.Setup()) require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "exit-zero"})) - assert.Equal(t, "FOO=bar - EXIT_CODE=", strings.TrimSpace(buff.String())) + assert.Equal(t, "FOO=bar - DYNAMIC_FOO=bar - EXIT_CODE=", strings.TrimSpace(buff.String())) } func TestExitCodeOne(t *testing.T) { @@ -1835,7 +2036,7 @@ func TestExitCodeOne(t *testing.T) { require.NoError(t, e.Setup()) require.Error(t, e.Run(context.Background(), &ast.Call{Task: "exit-one"})) - assert.Equal(t, "FOO=bar - EXIT_CODE=1", strings.TrimSpace(buff.String())) + assert.Equal(t, "FOO=bar - DYNAMIC_FOO=bar - EXIT_CODE=1", strings.TrimSpace(buff.String())) } func TestIgnoreNilElements(t *testing.T) { @@ -2521,6 +2722,8 @@ func TestForDeps(t *testing.T) { Stderr: &buff, Silent: true, Force: true, + // Force output of each dep to be grouped together to prevent interleaving + OutputStyle: ast.Output{Name: "group"}, } require.NoError(t, e.Setup()) require.NoError(t, e.Run(context.Background(), &ast.Call{Task: test.name})) @@ -2635,3 +2838,18 @@ func TestReference(t *testing.T) { }) } } + +// 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. +// +// Typically experiments are controlled via TASK_X_ env vars, but we cannot use those in tests +// because the experiment settings are parsed during experiments.init(), before any tests run. +func enableExperimentForTest(t *testing.T, e *experiments.Experiment, val string) { + prev := *e + *e = experiments.Experiment{ + Name: prev.Name, + Enabled: true, + Value: val, + } + t.Cleanup(func() { *e = prev }) +} diff --git a/taskfile/dotenv.go b/taskfile/dotenv.go index 3971a9d233..cabb40168c 100644 --- a/taskfile/dotenv.go +++ b/taskfile/dotenv.go @@ -1,6 +1,7 @@ package taskfile import ( + "fmt" "os" "github.com/joho/godotenv" @@ -37,7 +38,7 @@ func Dotenv(c *compiler.Compiler, tf *ast.Taskfile, dir string) (*ast.Vars, erro envs, err := godotenv.Read(dotEnvPath) if err != nil { - return nil, err + return nil, fmt.Errorf("error reading env file %s: %w", dotEnvPath, err) } for key, value := range envs { if ok := env.Exists(key); !ok { diff --git a/taskfile/node.go b/taskfile/node.go index 56b25a16aa..8f09bc5e1d 100644 --- a/taskfile/node.go +++ b/taskfile/node.go @@ -7,6 +7,8 @@ import ( "strings" "time" + giturls "github.com/whilp/git-urls" + "github.com/go-task/task/v3/errors" "github.com/go-task/task/v3/internal/experiments" "github.com/go-task/task/v3/internal/logger" @@ -48,24 +50,39 @@ func NewNode( ) (Node, error) { var node Node var err error - switch getScheme(entrypoint) { + scheme, err := getScheme(entrypoint) + if err != nil { + return nil, err + } + switch scheme { + case "git": + node, err = NewGitNode(entrypoint, dir, insecure, opts...) case "http", "https": node, err = NewHTTPNode(l, entrypoint, dir, insecure, timeout, opts...) default: - // If no other scheme matches, we assume it's a file node, err = NewFileNode(l, entrypoint, dir, opts...) + } + 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 } -func getScheme(uri string) string { +func getScheme(uri string) (string, error) { + u, err := giturls.Parse(uri) + if u == nil { + return "", err + } + if strings.HasSuffix(strings.Split(u.Path, "//")[0], ".git") && (u.Scheme == "git" || u.Scheme == "ssh" || u.Scheme == "https" || u.Scheme == "http") { + return "git", nil + } + if i := strings.Index(uri, "://"); i != -1 { - return uri[:i] + return uri[:i], nil } - return "" + return "", nil } func getDefaultDir(entrypoint, dir string) string { diff --git a/taskfile/node_file.go b/taskfile/node_file.go index 3e5ec9d69e..4ea9001c43 100644 --- a/taskfile/node_file.go +++ b/taskfile/node_file.go @@ -81,6 +81,9 @@ func (node *FileNode) ResolveEntrypoint(entrypoint string) (string, error) { if strings.Contains(entrypoint, "://") { return entrypoint, nil } + if strings.HasPrefix(entrypoint, "git") { + return entrypoint, nil + } path, err := execext.Expand(entrypoint) if err != nil { diff --git a/taskfile/node_git.go b/taskfile/node_git.go new file mode 100644 index 0000000000..26d287a129 --- /dev/null +++ b/taskfile/node_git.go @@ -0,0 +1,126 @@ +package taskfile + +import ( + "context" + "fmt" + "io" + "net/url" + "path/filepath" + "strings" + + "github.com/go-git/go-billy/v5/memfs" + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing" + "github.com/go-git/go-git/v5/storage/memory" + giturls "github.com/whilp/git-urls" + + "github.com/go-task/task/v3/errors" + "github.com/go-task/task/v3/internal/execext" + "github.com/go-task/task/v3/internal/filepathext" +) + +// An GitNode is a node that reads a Taskfile from a remote location via Git. +type GitNode struct { + *BaseNode + URL *url.URL + rawUrl string + ref string + path string +} + +func NewGitNode( + entrypoint string, + dir string, + insecure bool, + opts ...NodeOption, +) (*GitNode, error) { + base := NewBaseNode(dir, opts...) + u, err := giturls.Parse(entrypoint) + if err != nil { + return nil, err + } + + basePath, path := func() (string, string) { + x := strings.Split(u.Path, "//") + return x[0], x[1] + }() + ref := u.Query().Get("ref") + + rawUrl := u.String() + + u.RawQuery = "" + u.Path = basePath + + if u.Scheme == "http" && !insecure { + return nil, &errors.TaskfileNotSecureError{URI: entrypoint} + } + return &GitNode{ + BaseNode: base, + URL: u, + rawUrl: rawUrl, + ref: ref, + path: path, + }, nil +} + +func (node *GitNode) Location() string { + return node.rawUrl +} + +func (node *GitNode) Remote() bool { + return true +} + +func (node *GitNode) Read(_ context.Context) ([]byte, error) { + fs := memfs.New() + storer := memory.NewStorage() + _, err := git.Clone(storer, fs, &git.CloneOptions{ + URL: node.URL.String(), + ReferenceName: plumbing.ReferenceName(node.ref), + SingleBranch: true, + Depth: 1, + }) + if err != nil { + return nil, err + } + file, err := fs.Open(node.path) + if err != nil { + return nil, err + } + // Read the entire response body + b, err := io.ReadAll(file) + if err != nil { + return nil, err + } + + return b, nil +} + +func (node *GitNode) ResolveEntrypoint(entrypoint string) (string, error) { + dir, _ := filepath.Split(node.path) + resolvedEntrypoint := fmt.Sprintf("%s//%s", node.URL, filepath.Join(dir, entrypoint)) + if node.ref != "" { + return fmt.Sprintf("%s?ref=%s", resolvedEntrypoint, node.ref), nil + } + return resolvedEntrypoint, nil +} + +func (node *GitNode) ResolveDir(dir string) (string, error) { + path, err := execext.Expand(dir) + if err != nil { + return "", err + } + + if filepathext.IsAbs(path) { + return path, nil + } + + // NOTE: Uses the directory of the entrypoint (Taskfile), not the current working directory + // This means that files are included relative to one another + entrypointDir := filepath.Dir(node.Dir()) + return filepathext.SmartJoin(entrypointDir, path), nil +} + +func (node *GitNode) FilenameAndLastDir() (string, string) { + return filepath.Base(node.path), filepath.Base(filepath.Dir(node.path)) +} diff --git a/taskfile/node_git_test.go b/taskfile/node_git_test.go new file mode 100644 index 0000000000..bac6862638 --- /dev/null +++ b/taskfile/node_git_test.go @@ -0,0 +1,75 @@ +package taskfile + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGitNode_ssh(t *testing.T) { + node, err := NewGitNode("git@github.com:foo/bar.git//Taskfile.yml?ref=main", "", false) + assert.NoError(t, err) + assert.Equal(t, "main", node.ref) + assert.Equal(t, "Taskfile.yml", node.path) + assert.Equal(t, "ssh://git@github.com/foo/bar.git//Taskfile.yml?ref=main", node.rawUrl) + assert.Equal(t, "ssh://git@github.com/foo/bar.git", node.URL.String()) + entrypoint, err := node.ResolveEntrypoint("common.yml") + assert.NoError(t, err) + assert.Equal(t, "ssh://git@github.com/foo/bar.git//common.yml?ref=main", entrypoint) +} + +func TestGitNode_sshWithDir(t *testing.T) { + node, err := NewGitNode("git@github.com:foo/bar.git//directory/Taskfile.yml?ref=main", "", false) + assert.NoError(t, err) + assert.Equal(t, "main", node.ref) + assert.Equal(t, "directory/Taskfile.yml", node.path) + assert.Equal(t, "ssh://git@github.com/foo/bar.git//directory/Taskfile.yml?ref=main", node.rawUrl) + assert.Equal(t, "ssh://git@github.com/foo/bar.git", node.URL.String()) + entrypoint, err := node.ResolveEntrypoint("common.yml") + assert.NoError(t, err) + assert.Equal(t, "ssh://git@github.com/foo/bar.git//directory/common.yml?ref=main", entrypoint) +} + +func TestGitNode_https(t *testing.T) { + node, err := NewGitNode("https://github.com/foo/bar.git//Taskfile.yml?ref=main", "", false) + assert.NoError(t, err) + assert.Equal(t, "main", node.ref) + assert.Equal(t, "Taskfile.yml", node.path) + assert.Equal(t, "https://github.com/foo/bar.git//Taskfile.yml?ref=main", node.rawUrl) + assert.Equal(t, "https://github.com/foo/bar.git", node.URL.String()) + entrypoint, err := node.ResolveEntrypoint("common.yml") + assert.NoError(t, err) + assert.Equal(t, "https://github.com/foo/bar.git//common.yml?ref=main", entrypoint) +} + +func TestGitNode_httpsWithDir(t *testing.T) { + node, err := NewGitNode("https://github.com/foo/bar.git//directory/Taskfile.yml?ref=main", "", false) + assert.NoError(t, err) + assert.Equal(t, "main", node.ref) + assert.Equal(t, "directory/Taskfile.yml", node.path) + assert.Equal(t, "https://github.com/foo/bar.git//directory/Taskfile.yml?ref=main", node.rawUrl) + assert.Equal(t, "https://github.com/foo/bar.git", node.URL.String()) + entrypoint, err := node.ResolveEntrypoint("common.yml") + assert.NoError(t, err) + assert.Equal(t, "https://github.com/foo/bar.git//directory/common.yml?ref=main", entrypoint) +} + +func TestGitNode_FilenameAndDir(t *testing.T) { + node, err := NewGitNode("https://github.com/foo/bar.git//directory/Taskfile.yml?ref=main", "", false) + assert.NoError(t, err) + filename, dir := node.FilenameAndLastDir() + assert.Equal(t, "Taskfile.yml", filename) + assert.Equal(t, "directory", dir) + + node, err = NewGitNode("https://github.com/foo/bar.git//Taskfile.yml?ref=main", "", false) + assert.NoError(t, err) + filename, dir = node.FilenameAndLastDir() + assert.Equal(t, "Taskfile.yml", filename) + assert.Equal(t, ".", dir) + + node, err = NewGitNode("https://github.com/foo/bar.git//multiple/directory/Taskfile.yml?ref=main", "", false) + assert.NoError(t, err) + filename, dir = node.FilenameAndLastDir() + assert.Equal(t, "Taskfile.yml", filename) + assert.Equal(t, "directory", dir) +} diff --git a/taskfile/node_http.go b/taskfile/node_http.go index fa415d3c1f..fc9058487f 100644 --- a/taskfile/node_http.go +++ b/taskfile/node_http.go @@ -74,7 +74,6 @@ func (node *HTTPNode) Read(ctx context.Context) ([]byte, error) { return nil, errors.TaskfileFetchFailedError{URI: node.URL.String()} } defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { return nil, errors.TaskfileFetchFailedError{ URI: node.URL.String(), @@ -111,8 +110,12 @@ func (node *HTTPNode) ResolveDir(dir string) (string, error) { // NOTE: Uses the directory of the entrypoint (Taskfile), not the current working directory // This means that files are included relative to one another - entrypointDir := filepath.Dir(node.Dir()) - return filepathext.SmartJoin(entrypointDir, path), nil + parent := node.Dir() + if node.Parent() != nil { + parent = node.Parent().Dir() + } + + return filepathext.SmartJoin(parent, path), nil } func (node *HTTPNode) FilenameAndLastDir() (string, string) { diff --git a/taskfile/node_test.go b/taskfile/node_test.go new file mode 100644 index 0000000000..77651981a2 --- /dev/null +++ b/taskfile/node_test.go @@ -0,0 +1,22 @@ +package taskfile + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestScheme(t *testing.T) { + scheme, err := getScheme("https://github.com/foo/bar.git") + assert.NoError(t, err) + assert.Equal(t, "git", scheme) + scheme, err = getScheme("https://github.com/foo/bar.git?ref=v1//taskfile/common.yml") + assert.NoError(t, err) + assert.Equal(t, "git", scheme) + scheme, err = getScheme("git@github.com:foo/bar.git?ref=main//Taskfile.yml") + assert.NoError(t, err) + assert.Equal(t, "git", scheme) + scheme, err = getScheme("https://github.com/foo/common.yml") + assert.NoError(t, err) + assert.Equal(t, "https", scheme) +} diff --git a/taskfile/reader.go b/taskfile/reader.go index 1f1fefe2a1..69f75b1d19 100644 --- a/taskfile/reader.go +++ b/taskfile/reader.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "os" + "sync" "time" "github.com/dominikbraun/graph" @@ -30,14 +31,15 @@ Continue?` // A Reader will recursively read Taskfiles from a given source using a directed // acyclic graph (DAG). type Reader struct { - graph *ast.TaskfileGraph - node Node - insecure bool - download bool - offline bool - timeout time.Duration - tempDir string - logger *logger.Logger + graph *ast.TaskfileGraph + node Node + insecure bool + download bool + offline bool + timeout time.Duration + tempDir string + logger *logger.Logger + promptMutex sync.Mutex } func NewReader( @@ -50,14 +52,15 @@ func NewReader( logger *logger.Logger, ) *Reader { return &Reader{ - graph: ast.NewTaskfileGraph(), - node: node, - insecure: insecure, - download: download, - offline: offline, - timeout: timeout, - tempDir: tempDir, - logger: logger, + graph: ast.NewTaskfileGraph(), + node: node, + insecure: insecure, + download: download, + offline: offline, + timeout: timeout, + tempDir: tempDir, + logger: logger, + promptMutex: sync.Mutex{}, } } @@ -181,88 +184,9 @@ func (r *Reader) include(node Node) error { } func (r *Reader) readNode(node Node) (*ast.Taskfile, error) { - var b []byte - var err error - var cache *Cache - - if node.Remote() { - cache, err = NewCache(r.tempDir) - if err != nil { - return nil, err - } - } - - // If the file is remote and we're in offline mode, check if we have a cached copy - if node.Remote() && r.offline { - if b, err = cache.read(node); errors.Is(err, os.ErrNotExist) { - return nil, &errors.TaskfileCacheNotFoundError{URI: node.Location()} - } else if err != nil { - return nil, err - } - r.logger.VerboseOutf(logger.Magenta, "task: [%s] Fetched cached copy\n", node.Location()) - } else { - - downloaded := false - ctx, cf := context.WithTimeout(context.Background(), r.timeout) - defer cf() - - // Read the file - b, err = node.Read(ctx) - var taskfileNetworkTimeoutError *errors.TaskfileNetworkTimeoutError - // If we timed out then we likely have a network issue - if node.Remote() && errors.As(err, &taskfileNetworkTimeoutError) { - // If a download was requested, then we can't use a cached copy - if r.download { - return nil, &errors.TaskfileNetworkTimeoutError{URI: node.Location(), Timeout: r.timeout} - } - // Search for any cached copies - if b, err = cache.read(node); errors.Is(err, os.ErrNotExist) { - return nil, &errors.TaskfileNetworkTimeoutError{URI: node.Location(), Timeout: r.timeout, CheckedCache: true} - } else if err != nil { - return nil, err - } - r.logger.VerboseOutf(logger.Magenta, "task: [%s] Network timeout. Fetched cached copy\n", node.Location()) - } else if err != nil { - return nil, err - } else { - downloaded = true - } - - // If the node was remote, we need to check the checksum - if node.Remote() && downloaded { - r.logger.VerboseOutf(logger.Magenta, "task: [%s] Fetched remote copy\n", node.Location()) - - // Get the checksums - checksum := checksum(b) - cachedChecksum := cache.readChecksum(node) - - var prompt string - if cachedChecksum == "" { - // If the checksum doesn't exist, prompt the user to continue - prompt = fmt.Sprintf(taskfileUntrustedPrompt, node.Location()) - } else if checksum != cachedChecksum { - // If there is a cached hash, but it doesn't match the expected hash, prompt the user to continue - prompt = fmt.Sprintf(taskfileChangedPrompt, node.Location()) - } - if prompt != "" { - if err := r.logger.Prompt(logger.Yellow, prompt, "n", "y", "yes"); err != nil { - return nil, &errors.TaskfileNotTrustedError{URI: node.Location()} - } - } - - // If the hash has changed (or is new) - if checksum != cachedChecksum { - // Store the checksum - if err := cache.writeChecksum(node, checksum); err != nil { - return nil, err - } - // Cache the file - r.logger.VerboseOutf(logger.Magenta, "task: [%s] Caching downloaded file\n", node.Location()) - if err = cache.write(node, b); err != nil { - return nil, err - } - } - } + b, err := r.loadNodeContent(node) + if err != nil { + return nil, err } var tf ast.Taskfile @@ -295,3 +219,93 @@ func (r *Reader) readNode(node Node) (*ast.Taskfile, error) { return &tf, nil } + +func (r *Reader) loadNodeContent(node Node) ([]byte, error) { + if !node.Remote() { + ctx, cf := context.WithTimeout(context.Background(), r.timeout) + defer cf() + return node.Read(ctx) + } + + cache, err := NewCache(r.tempDir) + if err != nil { + return nil, err + } + + if r.offline { + // In offline mode try to use cached copy + cached, err := cache.read(node) + if errors.Is(err, os.ErrNotExist) { + return nil, &errors.TaskfileCacheNotFoundError{URI: node.Location()} + } else if err != nil { + return nil, err + } + r.logger.VerboseOutf(logger.Magenta, "task: [%s] Fetched cached copy\n", node.Location()) + + return cached, nil + } + + ctx, cf := context.WithTimeout(context.Background(), r.timeout) + defer cf() + + b, err := node.Read(ctx) + if errors.Is(err, &errors.TaskfileNetworkTimeoutError{}) { + // If we timed out then we likely have a network issue + + // If a download was requested, then we can't use a cached copy + if r.download { + return nil, &errors.TaskfileNetworkTimeoutError{URI: node.Location(), Timeout: r.timeout} + } + + // Search for any cached copies + cached, err := cache.read(node) + if errors.Is(err, os.ErrNotExist) { + return nil, &errors.TaskfileNetworkTimeoutError{URI: node.Location(), Timeout: r.timeout, CheckedCache: true} + } else if err != nil { + return nil, err + } + r.logger.VerboseOutf(logger.Magenta, "task: [%s] Network timeout. Fetched cached copy\n", node.Location()) + + return cached, nil + + } else if err != nil { + return nil, err + } + r.logger.VerboseOutf(logger.Magenta, "task: [%s] Fetched remote copy\n", node.Location()) + + // Get the checksums + checksum := checksum(b) + cachedChecksum := cache.readChecksum(node) + + var prompt string + if cachedChecksum == "" { + // If the checksum doesn't exist, prompt the user to continue + prompt = fmt.Sprintf(taskfileUntrustedPrompt, node.Location()) + } else if checksum != cachedChecksum { + // If there is a cached hash, but it doesn't match the expected hash, prompt the user to continue + prompt = fmt.Sprintf(taskfileChangedPrompt, node.Location()) + } + + if prompt != "" { + if err := func() error { + r.promptMutex.Lock() + defer r.promptMutex.Unlock() + return r.logger.Prompt(logger.Yellow, prompt, "n", "y", "yes") + }(); err != nil { + return nil, &errors.TaskfileNotTrustedError{URI: node.Location()} + } + + // Store the checksum + if err := cache.writeChecksum(node, checksum); err != nil { + return nil, err + } + + // Cache the file + r.logger.VerboseOutf(logger.Magenta, "task: [%s] Caching downloaded file\n", node.Location()) + if err = cache.write(node, b); err != nil { + return nil, err + } + } + + return b, nil +} diff --git a/testdata/dotenv/parse_error/.env-with-error b/testdata/dotenv/parse_error/.env-with-error new file mode 100644 index 0000000000..3286e2924a --- /dev/null +++ b/testdata/dotenv/parse_error/.env-with-error @@ -0,0 +1,2 @@ +#intentional parse error +SOME_VAR diff --git a/testdata/dotenv/parse_error/Taskfile.yml b/testdata/dotenv/parse_error/Taskfile.yml new file mode 100644 index 0000000000..37623e77d8 --- /dev/null +++ b/testdata/dotenv/parse_error/Taskfile.yml @@ -0,0 +1,8 @@ +version: '3' + +dotenv: ['.env-with-error'] + +tasks: + default: + cmd: "true" + diff --git a/testdata/exit_code/Taskfile.yml b/testdata/exit_code/Taskfile.yml index e78bd2cd85..3170b5079e 100644 --- a/testdata/exit_code/Taskfile.yml +++ b/testdata/exit_code/Taskfile.yml @@ -9,13 +9,17 @@ tasks: exit-zero: vars: FOO: bar + DYNAMIC_FOO: + sh: echo 'bar' cmds: - - defer: echo FOO={{.FOO}} - {{.PREFIX}}{{.EXIT_CODE}} + - defer: echo FOO={{.FOO}} - DYNAMIC_FOO={{.DYNAMIC_FOO}} - {{.PREFIX}}{{.EXIT_CODE}} - exit 0 exit-one: vars: FOO: bar + DYNAMIC_FOO: + sh: echo 'bar' cmds: - - defer: echo FOO={{.FOO}} - {{.PREFIX}}{{.EXIT_CODE}} + - defer: echo FOO={{.FOO}} - DYNAMIC_FOO={{.DYNAMIC_FOO}} - {{.PREFIX}}{{.EXIT_CODE}} - exit 1 diff --git a/testdata/includes_http/child-taskfile2.yml b/testdata/includes_http/child-taskfile2.yml new file mode 100644 index 0000000000..84690ed588 --- /dev/null +++ b/testdata/includes_http/child-taskfile2.yml @@ -0,0 +1,9 @@ +version: '3' + +includes: + third-with-dir-1: + taskfile: "{{.INCLUDE_ROOT}}/child-taskfile3.yml" + dir: ./dir-1 + third-with-dir-2: + taskfile: "{{.INCLUDE_ROOT}}/child-taskfile3.yml" + dir: ./dir-2 diff --git a/testdata/includes_http/child-taskfile3.yml b/testdata/includes_http/child-taskfile3.yml new file mode 100644 index 0000000000..3449a19b42 --- /dev/null +++ b/testdata/includes_http/child-taskfile3.yml @@ -0,0 +1,4 @@ +version: '3' + +tasks: + default: "true" diff --git a/testdata/includes_http/root-taskfile-remotefile-empty-dir-1st.yml b/testdata/includes_http/root-taskfile-remotefile-empty-dir-1st.yml new file mode 100644 index 0000000000..ff948f3961 --- /dev/null +++ b/testdata/includes_http/root-taskfile-remotefile-empty-dir-1st.yml @@ -0,0 +1,8 @@ +version: '3' + +includes: + second-no-dir: + taskfile: "{{.INCLUDE_ROOT}}/child-taskfile2.yml" + second-with-dir-1: + taskfile: "{{.INCLUDE_ROOT}}/child-taskfile2.yml" + dir: ./dir-1 diff --git a/testdata/includes_http/root-taskfile-remotefile-empty-dir-2nd.yml b/testdata/includes_http/root-taskfile-remotefile-empty-dir-2nd.yml new file mode 100644 index 0000000000..69080d55d8 --- /dev/null +++ b/testdata/includes_http/root-taskfile-remotefile-empty-dir-2nd.yml @@ -0,0 +1,8 @@ +version: '3' + +includes: + second-with-dir-1: + taskfile: "{{.INCLUDE_ROOT}}/child-taskfile2.yml" + dir: ./dir-1 + second-no-dir: + taskfile: "{{.INCLUDE_ROOT}}/child-taskfile2.yml" diff --git a/testdata/includes_remote/.gitignore b/testdata/includes_remote/.gitignore new file mode 100644 index 0000000000..2211df63dd --- /dev/null +++ b/testdata/includes_remote/.gitignore @@ -0,0 +1 @@ +*.txt diff --git a/testdata/includes_remote/Taskfile.yml b/testdata/includes_remote/Taskfile.yml new file mode 100644 index 0000000000..b518102245 --- /dev/null +++ b/testdata/includes_remote/Taskfile.yml @@ -0,0 +1,4 @@ +version: '3' + +includes: + first: "{{.FIRST_REMOTE_URL}}" diff --git a/testdata/includes_remote/first/Taskfile.yml b/testdata/includes_remote/first/Taskfile.yml new file mode 100644 index 0000000000..d15e50e0ec --- /dev/null +++ b/testdata/includes_remote/first/Taskfile.yml @@ -0,0 +1,11 @@ +version: '3' + +includes: + second: "{{.SECOND_REMOTE_URL}}" + +tasks: + write-file: + requires: + vars: [CONTENT, OUTPUT_FILE] + cmd: | + echo "{{.CONTENT}}" > "{{.OUTPUT_FILE}}" diff --git a/testdata/includes_remote/first/second/Taskfile.yml b/testdata/includes_remote/first/second/Taskfile.yml new file mode 100644 index 0000000000..1b261e448d --- /dev/null +++ b/testdata/includes_remote/first/second/Taskfile.yml @@ -0,0 +1,8 @@ +version: '3' + +tasks: + write-file: + requires: + vars: [CONTENT, OUTPUT_FILE] + cmd: | + echo "{{.CONTENT}}" > "{{.OUTPUT_FILE}}" diff --git a/variables.go b/variables.go index cfdb70ae46..6dac3a6c3e 100644 --- a/variables.go +++ b/variables.go @@ -309,6 +309,10 @@ func itemsFromFor( } else { values = asAnySlice(strings.Fields(value)) } + case []string: + values = asAnySlice(value) + case []int: + values = asAnySlice(value) case []any: values = value case map[string]any: diff --git a/website/docs/changelog.mdx b/website/docs/changelog.mdx index 4486b4dbb6..368437c853 100644 --- a/website/docs/changelog.mdx +++ b/website/docs/changelog.mdx @@ -5,6 +5,11 @@ sidebar_position: 14 # Changelog +## v3.39.2 - 2024-09-19 + +- Fix dynamic variables not working properly for a defer: statement (#1803, + #1818 by @vmaerten). + ## v3.39.1 - 2024-09-18 - Added Renovate configuration to automatically create PRs to keep dependencies diff --git a/website/docs/experiments/remote_taskfiles.mdx b/website/docs/experiments/remote_taskfiles.mdx index 8a85ccd0b6..45e6617edf 100644 --- a/website/docs/experiments/remote_taskfiles.mdx +++ b/website/docs/experiments/remote_taskfiles.mdx @@ -62,6 +62,16 @@ includes: `TOKEN=my-token task my-remote-namespace:hello` will be resolved by Task to `https://my-token@raw.githubusercontent.com/my-org/my-repo/main/Taskfile.yml` +## Git nodes + +You can also include a Taskfile from a Git node. We currently support ssh-style and http / https addresses like `git@example.com/foo/bar.git//Taskfiles.yml?ref=v1` and `https://example.com/foo/bar.git//Taskfiles.yml?ref=v1`. + +You need to follow this pattern : `.git//?ref=`. +The `ref` parameter, optional, can be a branch name or a tag, if not provided it'll pick up the default branch. +The `path` is the path to the Taskfile in the repository. + +If you want to use the SSH protocol, you need to make sure that your ssh-agent has your private ssh keys added so that they can be used during authentication. + ## Security Running commands from sources that you do not control is always a potential diff --git a/website/docs/reference/environment.mdx b/website/docs/reference/environment.mdx index ce9b7334b3..40ccfb6344 100644 --- a/website/docs/reference/environment.mdx +++ b/website/docs/reference/environment.mdx @@ -8,16 +8,17 @@ sidebar_position: 4 Task allows you to configure some behavior using environment variables. This page lists all the environment variables that Task supports. -| ENV | Default | Description | -| ----------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | -| `TASK_TEMP_DIR` | `.task` | Location of the temp dir. Can relative to the project like `tmp/task` or absolute like `/tmp/.task` or `~/.task`. | -| `TASK_REMOTE_DIR` | `TASK_TEMP_DIR` | Location of the remote temp dir (used for caching). Can relative to the project like `tmp/task` or absolute like `/tmp/.task` or `~/.task`. | -| `FORCE_COLOR` | | Force color output usage. | +| ENV | Default | Description | +|-------------------|-----------------|----------------------------------------------------------------------------------------------------------------------------------------------------| +| `TASK_TEMP_DIR` | `.task` | Location of the temp dir. Can relative to the project like `tmp/task` or absolute like `/tmp/.task` or `~/.task`. | +| `TASK_REMOTE_DIR` | `TASK_TEMP_DIR` | Location of the remote temp dir (used for caching). Can relative to the project like `tmp/task` or absolute like `/tmp/.task` or `~/.task`. | +| `TASK_OFFLINE` | `false` | Set the `--offline` flag through the environment variable. Only for remote experiment. CLI flag `--offline` takes precedence over the env variable | +| `FORCE_COLOR` | | Force color output usage. | ## Custom Colors | ENV | Default | Description | -| --------------------------- | ------- | ----------------------- | +|-----------------------------|---------|-------------------------| | `TASK_COLOR_RESET` | `0` | Color used for white. | | `TASK_COLOR_RED` | `31` | Color used for red. | | `TASK_COLOR_GREEN` | `32` | Color used for green. | diff --git a/website/docs/reference/templating.mdx b/website/docs/reference/templating.mdx index 33df58e876..6c9f738f4a 100644 --- a/website/docs/reference/templating.mdx +++ b/website/docs/reference/templating.mdx @@ -106,6 +106,7 @@ special variable will be overridden. | `CLI_FORCE` | A boolean containing whether the `--force` or `--force-all` flags were set. | | `CLI_SILENT` | A boolean containing whether the `--silent` flag was set. | | `CLI_VERBOSE` | A boolean containing whether the `--verbose` flag was set. | +| `CLI_OFFLINE` | A boolean containing whether the `--offline` flag was set. | | `TASK` | The name of the current task. | | `ALIAS` | The alias used for the current task, otherwise matches `TASK`. | | `TASK_EXE` | The Task executable name or path. | diff --git a/website/versioned_docs/version-latest/changelog.mdx b/website/versioned_docs/version-latest/changelog.mdx index 4486b4dbb6..368437c853 100644 --- a/website/versioned_docs/version-latest/changelog.mdx +++ b/website/versioned_docs/version-latest/changelog.mdx @@ -5,6 +5,11 @@ sidebar_position: 14 # Changelog +## v3.39.2 - 2024-09-19 + +- Fix dynamic variables not working properly for a defer: statement (#1803, + #1818 by @vmaerten). + ## v3.39.1 - 2024-09-18 - Added Renovate configuration to automatically create PRs to keep dependencies diff --git a/website/yarn.lock b/website/yarn.lock index 027256d11f..d64761de5d 100644 --- a/website/yarn.lock +++ b/website/yarn.lock @@ -2591,9 +2591,9 @@ vfile "^6.0.0" "@mdx-js/react@^3.0.0": - version "3.0.0" - resolved "https://registry.npmjs.org/@mdx-js/react/-/react-3.0.0.tgz" - integrity sha512-nDctevR9KyYFyV+m+/+S4cpzCWHqj+iHDHq3QrsWezcC+B17uZdIWgCguESUkwFhM3n/56KxWVE3V6EokrmONQ== + version "3.0.1" + resolved "https://registry.yarnpkg.com/@mdx-js/react/-/react-3.0.1.tgz#997a19b3a5b783d936c75ae7c47cfe62f967f746" + integrity sha512-9ZrPIU4MGf6et1m1ov3zKf+q9+deetI51zprKB1D/z3NOb+rUxxtEl3mCjW5wTGh6VhRdwPueh1oRzi6ezkA8A== dependencies: "@types/mdx" "^2.0.0" @@ -2949,9 +2949,9 @@ "@types/unist" "*" "@types/mdx@^2.0.0": - version "2.0.10" - resolved "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.10.tgz" - integrity sha512-Rllzc5KHk0Al5/WANwgSPl1/CwjqCy+AZrGd78zuK+jO9aDM6ffblZ+zIjgPNAaEBmlO0RYDvLNh7wD0zKVgEg== + version "2.0.13" + resolved "https://registry.yarnpkg.com/@types/mdx/-/mdx-2.0.13.tgz#68f6877043d377092890ff5b298152b0a21671bd" + integrity sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw== "@types/mime@*": version "3.0.1" @@ -2988,14 +2988,14 @@ integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== "@types/prismjs@^1.26.0": - version "1.26.3" - resolved "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.3.tgz" - integrity sha512-A0D0aTXvjlqJ5ZILMz3rNfDBOx9hHxLZYv2by47Sm/pqW35zzjusrZTryatjN/Rf8Us2gZrJD+KeHbUSTux1Cw== + version "1.26.4" + resolved "https://registry.yarnpkg.com/@types/prismjs/-/prismjs-1.26.4.tgz#1a9e1074619ce1d7322669e5b46fbe823925103a" + integrity sha512-rlAnzkW2sZOjbqZ743IHUhFcvzaGbqijwOu8QZnZCjfQzBqFE3s4lOTJEsxikImav9uzz/42I+O7YUs1mWgMlg== "@types/prop-types@*": - version "15.7.12" - resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.12.tgz#12bb1e2be27293c1406acb6af1c3f3a1481d98c6" - integrity sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q== + version "15.7.13" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.13.tgz#2af91918ee12d9d32914feb13f5326658461b451" + integrity sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA== "@types/qs@*": version "6.9.7" @@ -3042,12 +3042,11 @@ csstype "^3.0.2" "@types/react@^18.2.29": - version "18.2.45" - resolved "https://registry.npmjs.org/@types/react/-/react-18.2.45.tgz" - integrity sha512-TtAxCNrlrBp8GoeEp1npd5g+d/OejJHFxS3OWmrPBMFaVQMSN0OFySozJio5BHxTuTeug00AVXVAjfDSfk+lUg== + version "18.3.11" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.11.tgz#9d530601ff843ee0d7030d4227ea4360236bd537" + integrity sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ== dependencies: "@types/prop-types" "*" - "@types/scheduler" "*" csstype "^3.0.2" "@types/retry@0.12.0": @@ -3062,11 +3061,6 @@ dependencies: "@types/node" "*" -"@types/scheduler@*": - version "0.23.0" - resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.23.0.tgz#0a6655b3e2708eaabca00b7372fafd7a792a7b09" - integrity sha512-YIoDCTH3Af6XM5VuwGG/QL/CJqga1Zm3NkU3HZ4ZHK2fRMPYP1VczsTUqtsf43PH/iJNVlPHAo2oWX7BSdB2Hw== - "@types/serve-index@^1.9.1": version "1.9.1" resolved "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz" @@ -3569,10 +3563,10 @@ binary-extensions@^2.0.0: resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== -body-parser@1.20.2: - version "1.20.2" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd" - integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA== +body-parser@1.20.3: + version "1.20.3" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6" + integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g== dependencies: bytes "3.1.2" content-type "~1.0.5" @@ -3582,7 +3576,7 @@ body-parser@1.20.2: http-errors "2.0.0" iconv-lite "0.4.24" on-finished "2.4.1" - qs "6.11.0" + qs "6.13.0" raw-body "2.5.2" type-is "~1.6.18" unpipe "1.0.0" @@ -3698,7 +3692,7 @@ cacheable-request@^10.2.8: normalize-url "^8.0.0" responselike "^3.0.0" -call-bind@^1.0.0, call-bind@^1.0.5: +call-bind@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz" integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ== @@ -3707,6 +3701,17 @@ call-bind@^1.0.0, call-bind@^1.0.5: get-intrinsic "^1.2.1" set-function-length "^1.1.1" +call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" @@ -3893,9 +3898,9 @@ clone-deep@^4.0.1: shallow-clone "^3.0.0" clsx@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz" - integrity sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q== + version "2.1.1" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999" + integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA== collapse-white-space@^2.0.0: version "2.1.0" @@ -4362,6 +4367,15 @@ define-data-property@^1.0.1, define-data-property@^1.1.1: gopd "^1.0.1" has-property-descriptors "^1.0.0" +define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + define-lazy-prop@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz" @@ -4589,6 +4603,11 @@ encodeurl@~1.0.2: resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz" integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== +encodeurl@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" + integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== + enhanced-resolve@^5.17.1: version "5.17.1" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15" @@ -4614,6 +4633,18 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + es-module-lexer@^1.2.1: version "1.5.0" resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.5.0.tgz#4878fee3789ad99e065f975fdd3c645529ff0236" @@ -4787,36 +4818,36 @@ execa@^5.0.0: strip-final-newline "^2.0.0" express@^4.17.3: - version "4.19.2" - resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465" - integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q== + version "4.21.0" + resolved "https://registry.yarnpkg.com/express/-/express-4.21.0.tgz#d57cb706d49623d4ac27833f1cbc466b668eb915" + integrity sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng== dependencies: accepts "~1.3.8" array-flatten "1.1.1" - body-parser "1.20.2" + body-parser "1.20.3" content-disposition "0.5.4" content-type "~1.0.4" cookie "0.6.0" cookie-signature "1.0.6" debug "2.6.9" depd "2.0.0" - encodeurl "~1.0.2" + encodeurl "~2.0.0" escape-html "~1.0.3" etag "~1.8.1" - finalhandler "1.2.0" + finalhandler "1.3.1" fresh "0.5.2" http-errors "2.0.0" - merge-descriptors "1.0.1" + merge-descriptors "1.0.3" methods "~1.1.2" on-finished "2.4.1" parseurl "~1.3.3" - path-to-regexp "0.1.7" + path-to-regexp "0.1.10" proxy-addr "~2.0.7" - qs "6.11.0" + qs "6.13.0" range-parser "~1.2.1" safe-buffer "5.2.1" - send "0.18.0" - serve-static "1.15.0" + send "0.19.0" + serve-static "1.16.2" setprototypeof "1.2.0" statuses "2.0.1" type-is "~1.6.18" @@ -4911,13 +4942,13 @@ fill-range@^7.1.1: dependencies: to-regex-range "^5.0.1" -finalhandler@1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz" - integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== +finalhandler@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.3.1.tgz#0c575f1d1d324ddd1da35ad7ece3df7d19088019" + integrity sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ== dependencies: debug "2.6.9" - encodeurl "~1.0.2" + encodeurl "~2.0.0" escape-html "~1.0.3" on-finished "2.4.1" parseurl "~1.3.3" @@ -5053,7 +5084,7 @@ gensync@^1.0.0-beta.2: resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: +get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: version "1.2.2" resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz" integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA== @@ -5063,6 +5094,17 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@ has-symbols "^1.0.3" hasown "^2.0.0" +get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + get-own-enumerable-property-symbols@^3.0.0: version "3.0.2" resolved "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz" @@ -5233,6 +5275,13 @@ has-property-descriptors@^1.0.0: dependencies: get-intrinsic "^1.2.2" +has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + has-proto@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz" @@ -6366,10 +6415,10 @@ memfs@^3.1.2, memfs@^3.4.3: dependencies: fs-monkey "^1.0.4" -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz" - integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== +merge-descriptors@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" + integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== merge-stream@^2.0.0: version "2.0.0" @@ -6993,10 +7042,10 @@ object-assign@^4.1.1: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== -object-inspect@^1.9.0: - version "1.13.1" - resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz" - integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== +object-inspect@^1.13.1: + version "1.13.2" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" + integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== object-keys@^1.1.1: version "1.1.1" @@ -7242,10 +7291,10 @@ path-parse@^1.0.7: resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -path-to-regexp@0.1.7: - version "0.1.7" - resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz" - integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== +path-to-regexp@0.1.10: + version "0.1.10" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.10.tgz#67e9108c5c0551b9e5326064387de4763c4d5f8b" + integrity sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w== path-to-regexp@2.2.1: version "2.2.1" @@ -7622,9 +7671,9 @@ pretty-time@^1.1.0: integrity sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA== prism-react-renderer@^2.1.0, prism-react-renderer@^2.3.0: - version "2.3.0" - resolved "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-2.3.0.tgz" - integrity sha512-UYRg2TkVIaI6tRVHC5OJ4/BxqPUxJkJvq/odLT/ykpt1zGYXooNperUxQcCvi87LyRnR4nCh81ceOA+e7nrydg== + version "2.4.0" + resolved "https://registry.yarnpkg.com/prism-react-renderer/-/prism-react-renderer-2.4.0.tgz#c5ea692029c2f8b3fd04f63662d04ffd4eaf10a0" + integrity sha512-327BsVCD/unU4CNLZTWVHyUHKnsqcvj2qbPlQ8MiBE2eq2rgctjigPA1Gp9HLF83kZ20zNN6jgizHJeEsyFYOw== dependencies: "@types/prismjs" "^1.26.0" clsx "^2.0.0" @@ -7691,12 +7740,12 @@ pupa@^3.1.0: dependencies: escape-goat "^4.0.0" -qs@6.11.0: - version "6.11.0" - resolved "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz" - integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== +qs@6.13.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" + integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== dependencies: - side-channel "^1.0.4" + side-channel "^1.0.6" queue-microtask@^1.2.2: version "1.2.3" @@ -7791,12 +7840,12 @@ react-dev-utils@^12.0.1: text-table "^0.2.0" react-dom@^18.2.0: - version "18.2.0" - resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz" - integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== + version "18.3.1" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.3.1.tgz#c2265d79511b57d479b3dd3fdfa51536494c5cb4" + integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw== dependencies: loose-envify "^1.1.0" - scheduler "^0.23.0" + scheduler "^0.23.2" react-error-overlay@^6.0.11: version "6.0.11" @@ -7879,9 +7928,9 @@ react-router@5.3.4, react-router@^5.3.4: tiny-warning "^1.0.0" react@^18.2.0: - version "18.2.0" - resolved "https://registry.npmjs.org/react/-/react-18.2.0.tgz" - integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== + version "18.3.1" + resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891" + integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ== dependencies: loose-envify "^1.1.0" @@ -8213,10 +8262,10 @@ sax@^1.2.4: resolved "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz" integrity sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA== -scheduler@^0.23.0: - version "0.23.0" - resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz" - integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== +scheduler@^0.23.2: + version "0.23.2" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3" + integrity sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ== dependencies: loose-envify "^1.1.0" @@ -8288,10 +8337,10 @@ semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.4: dependencies: lru-cache "^6.0.0" -send@0.18.0: - version "0.18.0" - resolved "https://registry.npmjs.org/send/-/send-0.18.0.tgz" - integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== +send@0.19.0: + version "0.19.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8" + integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw== dependencies: debug "2.6.9" depd "2.0.0" @@ -8348,15 +8397,15 @@ serve-index@^1.9.1: mime-types "~2.1.17" parseurl "~1.3.2" -serve-static@1.15.0: - version "1.15.0" - resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz" - integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== +serve-static@1.16.2: + version "1.16.2" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.2.tgz#b6a5343da47f6bdd2673848bf45754941e803296" + integrity sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw== dependencies: - encodeurl "~1.0.2" + encodeurl "~2.0.0" escape-html "~1.0.3" parseurl "~1.3.3" - send "0.18.0" + send "0.19.0" set-function-length@^1.1.1: version "1.1.1" @@ -8368,6 +8417,18 @@ set-function-length@^1.1.1: gopd "^1.0.1" has-property-descriptors "^1.0.0" +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + setprototypeof@1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz" @@ -8416,14 +8477,15 @@ shelljs@^0.8.5: interpret "^1.0.0" rechoir "^0.6.2" -side-channel@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz" - integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== +side-channel@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" + integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== dependencies: - call-bind "^1.0.0" - get-intrinsic "^1.0.2" - object-inspect "^1.9.0" + call-bind "^1.0.7" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" signal-exit@^3.0.2, signal-exit@^3.0.3: version "3.0.7" @@ -8865,9 +8927,9 @@ typedarray-to-buffer@^3.1.5: is-typedarray "^1.0.0" typescript@^5.3.3: - version "5.3.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37" - integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw== + version "5.6.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.2.tgz#d1de67b6bef77c41823f822df8f0b3bcff60a5a0" + integrity sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw== undici-types@~5.26.4: version "5.26.5"