diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bc9ff90..2eb5e1de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## v3.43.0 - 2025-04-21 - Significant improvements were made to the watcher. We migrated from [watcher](https://github.com/radovskyb/watcher) to diff --git a/internal/version/version.txt b/internal/version/version.txt index e339122b..a9184766 100644 --- a/internal/version/version.txt +++ b/internal/version/version.txt @@ -1 +1 @@ -3.42.1 +3.43.0 diff --git a/package-lock.json b/package-lock.json index b2448dbf..80998be1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@go-task/cli", - "version": "3.42.1", + "version": "3.43.0", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index ffd5d164..97daa4b3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@go-task/cli", - "version": "3.42.1", + "version": "3.43.0", "description": "A task runner / simpler Make alternative written in Go", "scripts": { "postinstall": "go-npm install", diff --git a/website/docs/changelog.mdx b/website/docs/changelog.mdx index 3e39dd94..5e216c8c 100644 --- a/website/docs/changelog.mdx +++ b/website/docs/changelog.mdx @@ -5,6 +5,77 @@ sidebar_position: 14 # Changelog +## v3.43.0 - 2025-04-21 + +- Significant improvements were made to the watcher. We migrated from + [watcher](https://github.com/radovskyb/watcher) to + [fsnotify](https://github.com/fsnotify/fsnotify). The former library used + polling, which means Task had a high CPU usage when watching too many files. + `fsnotify` uses proper the APIs from each operating system to watch files, + which means a much better performance. The default interval changed from 5 + seconds to 100 milliseconds, because now it configures the wait time for + duplicated events, instead of the polling time (#2048 by @andreynering, #1508, + #985, #1179). +- The [Map Variables experiment](https://github.com/go-task/task/issues/1585) + was made generally available so you can now + [define map variables in your Taskfiles!](https://taskfile.dev/usage/#variables) + (#1585, #1547, #2081 by @pd93). +- Wildcards can now + [match multiple tasks](https://taskfile.dev/usage/#wildcard-arguments) (#2072, + #2121 by @pd93). +- Added the ability to + [loop over the files specified by the `generates` keyword](https://taskfile.dev/usage/#looping-over-your-tasks-sources-or-generated-files). + This works the same way as looping over sources (#2151 by @sedyh). +- Added the ability to resolve variables when defining an include variable + (#2108, #2113 by @pd93). +- A few changes have been made to the + [Remote Taskfiles experiment](https://github.com/go-task/task/issues/1317) + (#1402, #2176 by @pd93): + - Cached files are now prioritized over remote ones. + - Added an `--expiry` flag which sets the TTL for a remote file cache. By + default the value will be 0 (caching disabled). If Task is running in + offline mode or fails to make a connection, it will fallback on the cache. +- `.taskrc` files can now be used from subdirectories and will be searched for + recursively up the file tree in the same way that Taskfiles are (#2159, #2166 + by @pd93). +- The default taskfile (output when using the `--init` flag) is now an embedded + file in the binary instead of being stored in the code (#2112 by @pd93). +- Improved the way we report the Task version when using the `--version` flag or + `{{.TASK_VERSION}}` variable. This should now be more consistent and easier + for package maintainers to use (#2131 by @pd93). +- Fixed a bug where globstar (`**`) matching in `sources` only resolved the + first result (#2073, #2075 by @pd93). +- Fixed a bug where sorting tasks by "none" would use the default sorting + instead of leaving tasks in the order they were defined (#2124, #2125 by + @trulede). +- Fixed Fish completion on newer Fish versions (#2130 by @atusy). +- Fixed a bug where undefined/null variables resolved to an empty string instead + of `nil` (#1911, #2144 by @pd93). +- The `USER_WORKING_DIR` special now will now properly account for the `--dir` + (`-d`) flag, if given (#2102, #2103 by @jaynis, #2186 by @andreynering). +- Fix Fish completions when `--global` (`-g`) is given (#2134 by @atusy). +- Fixed variables not available when using `defer:` (#1909, #2173 by @vmaerten). + +#### Package API + +- The [`Executor`](https://pkg.go.dev/github.com/go-task/task/v3#Executor) now + uses the functional options pattern (#2085, #2147, #2148 by @pd93). +- The functional options for the + [`taskfile.Reader`](https://pkg.go.dev/github.com/go-task/task/v3/taskfile#Reader) + and + [`taskfile.Snippet`](https://pkg.go.dev/github.com/go-task/task/v3/taskfile#Snippet) + types no longer have the `Reader`/`Snippet` respective prefixes (#2148 by + @pd93). +- [`taskfile.Reader`](https://pkg.go.dev/github.com/go-task/task/v3/taskfile#Reader) + no longer accepts a + [`taskfile.Node`](https://pkg.go.dev/github.com/go-task/task/v3/taskfile#Node). + Instead nodes are passed directly into the + [`Reader.Read`](https://pkg.go.dev/github.com/go-task/task/v3/taskfile#Reader.Read) + method (#2169 by @pd93). +- [`Reader.Read`](https://pkg.go.dev/github.com/go-task/task/v3/taskfile#Reader.Read) + also now accepts a [`context.Context`](https://pkg.go.dev/context#Context) + (#2176 by @pd93). + ## v3.42.1 - 2025-03-10 - Fixed a bug where some special variables caused a type error when used global diff --git a/website/versioned_docs/version-latest/changelog.mdx b/website/versioned_docs/version-latest/changelog.mdx index 3e39dd94..5e216c8c 100644 --- a/website/versioned_docs/version-latest/changelog.mdx +++ b/website/versioned_docs/version-latest/changelog.mdx @@ -5,6 +5,77 @@ sidebar_position: 14 # Changelog +## v3.43.0 - 2025-04-21 + +- Significant improvements were made to the watcher. We migrated from + [watcher](https://github.com/radovskyb/watcher) to + [fsnotify](https://github.com/fsnotify/fsnotify). The former library used + polling, which means Task had a high CPU usage when watching too many files. + `fsnotify` uses proper the APIs from each operating system to watch files, + which means a much better performance. The default interval changed from 5 + seconds to 100 milliseconds, because now it configures the wait time for + duplicated events, instead of the polling time (#2048 by @andreynering, #1508, + #985, #1179). +- The [Map Variables experiment](https://github.com/go-task/task/issues/1585) + was made generally available so you can now + [define map variables in your Taskfiles!](https://taskfile.dev/usage/#variables) + (#1585, #1547, #2081 by @pd93). +- Wildcards can now + [match multiple tasks](https://taskfile.dev/usage/#wildcard-arguments) (#2072, + #2121 by @pd93). +- Added the ability to + [loop over the files specified by the `generates` keyword](https://taskfile.dev/usage/#looping-over-your-tasks-sources-or-generated-files). + This works the same way as looping over sources (#2151 by @sedyh). +- Added the ability to resolve variables when defining an include variable + (#2108, #2113 by @pd93). +- A few changes have been made to the + [Remote Taskfiles experiment](https://github.com/go-task/task/issues/1317) + (#1402, #2176 by @pd93): + - Cached files are now prioritized over remote ones. + - Added an `--expiry` flag which sets the TTL for a remote file cache. By + default the value will be 0 (caching disabled). If Task is running in + offline mode or fails to make a connection, it will fallback on the cache. +- `.taskrc` files can now be used from subdirectories and will be searched for + recursively up the file tree in the same way that Taskfiles are (#2159, #2166 + by @pd93). +- The default taskfile (output when using the `--init` flag) is now an embedded + file in the binary instead of being stored in the code (#2112 by @pd93). +- Improved the way we report the Task version when using the `--version` flag or + `{{.TASK_VERSION}}` variable. This should now be more consistent and easier + for package maintainers to use (#2131 by @pd93). +- Fixed a bug where globstar (`**`) matching in `sources` only resolved the + first result (#2073, #2075 by @pd93). +- Fixed a bug where sorting tasks by "none" would use the default sorting + instead of leaving tasks in the order they were defined (#2124, #2125 by + @trulede). +- Fixed Fish completion on newer Fish versions (#2130 by @atusy). +- Fixed a bug where undefined/null variables resolved to an empty string instead + of `nil` (#1911, #2144 by @pd93). +- The `USER_WORKING_DIR` special now will now properly account for the `--dir` + (`-d`) flag, if given (#2102, #2103 by @jaynis, #2186 by @andreynering). +- Fix Fish completions when `--global` (`-g`) is given (#2134 by @atusy). +- Fixed variables not available when using `defer:` (#1909, #2173 by @vmaerten). + +#### Package API + +- The [`Executor`](https://pkg.go.dev/github.com/go-task/task/v3#Executor) now + uses the functional options pattern (#2085, #2147, #2148 by @pd93). +- The functional options for the + [`taskfile.Reader`](https://pkg.go.dev/github.com/go-task/task/v3/taskfile#Reader) + and + [`taskfile.Snippet`](https://pkg.go.dev/github.com/go-task/task/v3/taskfile#Snippet) + types no longer have the `Reader`/`Snippet` respective prefixes (#2148 by + @pd93). +- [`taskfile.Reader`](https://pkg.go.dev/github.com/go-task/task/v3/taskfile#Reader) + no longer accepts a + [`taskfile.Node`](https://pkg.go.dev/github.com/go-task/task/v3/taskfile#Node). + Instead nodes are passed directly into the + [`Reader.Read`](https://pkg.go.dev/github.com/go-task/task/v3/taskfile#Reader.Read) + method (#2169 by @pd93). +- [`Reader.Read`](https://pkg.go.dev/github.com/go-task/task/v3/taskfile#Reader.Read) + also now accepts a [`context.Context`](https://pkg.go.dev/context#Context) + (#2176 by @pd93). + ## v3.42.1 - 2025-03-10 - Fixed a bug where some special variables caused a type error when used global diff --git a/website/versioned_docs/version-latest/experiments/map_variables.mdx b/website/versioned_docs/version-latest/experiments/map_variables.mdx deleted file mode 100644 index 477714d0..00000000 --- a/website/versioned_docs/version-latest/experiments/map_variables.mdx +++ /dev/null @@ -1,245 +0,0 @@ ---- -slug: /experiments/map-variables/ ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -# Map Variables (#1585) - -:::caution - -All experimental features are subject to breaking changes and/or removal _at any -time_. We strongly recommend that you do not use these features in a production -environment. They are intended for testing and feedback only. - -::: - -Currently, Task supports all variable types except for maps. This experiment -adds two different proposals for map variables. Click on the tabs below to -switch between them. - - - - - -:::warning - -This experiment proposal breaks the following functionality: - -- Dynamically defined variables (using the `sh` keyword) - -::: - -:::info - -To enable this experiment, set the environment variable: -`TASK_X_MAP_VARIABLES=1`. Check out [our guide to enabling experiments -][enabling-experiments] for more information. - -::: - -This proposal removes support for the `sh` and `ref` keywords in favour of a new -syntax for dynamically defined variables and references. This allows you to -define a map directly as you would for any other type: - -```yaml -version: 3 - -tasks: - foo: - vars: - FOO: {a: 1, b: 2, c: 3} # <-- Directly defined map on the `FOO` key - cmds: - - 'echo {{.FOO.a}}' -``` - -## Migration - -Taskfiles with dynamically defined variables via the `sh` subkey or references -defined with `ref` will no longer work with this experiment enabled. In order to -keep using these features, you will need to migrate your Taskfile to use the new -syntax. - -### Dynamic Variables - -Previously, you had to define dynamic variables using the `sh` subkey. With this -experiment enabled, you will need to remove the `sh` subkey and define your -command as a string that begins with a `$`. This will instruct Task to interpret -the string as a command instead of a literal value and the variable will be -populated with the output of the command. For example: - - - - - -```yaml -version: 3 - -tasks: - foo: - vars: - CALCULATED_VAR: - sh: 'echo hello' - cmds: - - 'echo {{.CALCULATED_VAR}}' -``` - - - - -```yaml -version: 3 - -tasks: - foo: - vars: - CALCULATED_VAR: '$echo hello' # <-- Prefix dynamic variable with a `$` - cmds: - - 'echo {{.CALCULATED_VAR}}' -``` - - - -### References - - - - - -```yaml -version: 3 - -tasks: - foo: - vars: - VAR: 42 - VAR_REF: - ref: '.FOO' - cmds: - - 'echo {{.VAR_REF}}' -``` - - - - -```yaml -version: 3 - -tasks: - foo: - vars: - VAR: 42 - VAR_REF: '#.FOO' # <-- Prefix reference with a `#` - cmds: - - 'echo {{.VAR_REF}}' -``` - - - -If your current Taskfile contains a string variable that begins with a `$` or a -`#`, you will now need to escape it with a backslash (`\`) to stop Task from -interpreting it as a command or reference. - - - - -:::info - -To enable this experiment, set the environment variable: -`TASK_X_MAP_VARIABLES=2`. Check out [our guide to enabling experiments -][enabling-experiments] for more information. - -::: - -This proposal maintains backwards-compatibility and the `sh` subkey and adds -another new `map` subkey for defining map variables: - -```yaml -version: 3 - -tasks: - foo: - vars: - FOO: - map: {a: 1, b: 2, c: 3} # <-- Defined using the `map' subkey instead of directly on 'FOO' - BAR: true # <-- Other types of variables are still defined directly on the key - BAZ: - sh: 'echo Hello Task' # <-- The `sh` subkey is still supported - QUX: - ref: '.BAZ' # <-- The `ref` subkey is still supported - cmds: - - 'echo {{.FOO.a}}' -``` - - - -## Looping over maps - -This experiment also adds support for looping over maps using the `for` keyword, -just like arrays. In addition to the `{{.ITEM}}` variable being populated when -looping over a map, we also make an additional `{{.KEY}}` variable available -that holds the string value of the map key. - - - - - -```yaml -version: 3 - -tasks: - foo: - vars: - MAP: {a: 1, b: 2, c: 3} - cmds: - - for: - var: MAP - cmd: 'echo "{{.KEY}}: {{.ITEM}}"' -``` - - - - -```yaml -version: 3 - -tasks: - foo: - vars: - map: - MAP: {a: 1, b: 2, c: 3} - cmds: - - for: - var: MAP - cmd: 'echo "{{.KEY}}: {{.ITEM}}"' -``` - -:::note - -Remember that maps are unordered, so -the order in which the items are looped over is random. - -::: - - - -{/* prettier-ignore-start */} -[enabling-experiments]: ./experiments.mdx#enabling-experiments -{/* prettier-ignore-end */} diff --git a/website/versioned_docs/version-latest/experiments/remote_taskfiles.mdx b/website/versioned_docs/version-latest/experiments/remote_taskfiles.mdx index 45e6617e..b9c2d1f2 100644 --- a/website/versioned_docs/version-latest/experiments/remote_taskfiles.mdx +++ b/website/versioned_docs/version-latest/experiments/remote_taskfiles.mdx @@ -2,6 +2,9 @@ slug: /experiments/remote-taskfiles/ --- +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + # Remote Taskfiles (#1317) :::caution @@ -20,33 +23,151 @@ To enable this experiment, set the environment variable: ::: -This experiment allows you to specify a remote Taskfile URL when including a -Taskfile. For example: +:::danger +Never run remote Taskfiles from sources that you do not trust. +::: -```yaml -version: '3' +This experiment allows you to use Taskfiles which are stored in remote +locations. This applies to both the root Taskfile (aka. Entrypoint) and also +when including Taskfiles. -includes: - my-remote-namespace: https://raw.githubusercontent.com/my-org/my-repo/main/Taskfile.yml -``` +Task uses "nodes" to reference remote Taskfiles. There are a few different types +of node which you can use: -This works exactly the same way that including a local file does. Any tasks in -the remote Taskfile will be available to run from your main Taskfile via the -namespace `my-remote-namespace`. For example, if the remote file contains the -following: + + + +`https://raw.githubusercontent.com/go-task/task/main/website/static/Taskfile.yml` + +This is the most basic type of remote node and works by downloading the file +from the specified URL. The file must be a valid Taskfile and can be of any +name. If a file is not found at the specified URL, Task will append each of the +[supported file names][supported-file-names] in turn until it finds a valid +file. If it still does not find a valid Taskfile, an error is returned. + + + + +`https://github.com/go-task/task.git//website/static/Taskfile.yml?ref=main` + +This type of node works by downloading the file from a Git repository over +HTTP/HTTPS. The first part of the URL is the base URL of the Git repository. +This is the same URL that you would use to clone the repo over HTTP. + +- You can optionally add the path to the Taskfile in the repository by appending +`//` to the URL. +- You can also optionally specify a branch or tag to use by appending +`?ref=` to the end of the URL. If you omit a reference, the default branch +will be used. + + + + +`git@github.com/go-task/task.git//website/static/Taskfile.yml?ref=main` + +This type of node works by downloading the file from a Git repository over SSH. +The first part of the URL is the user and base URL of the Git repository. This +is the same URL that you would use to clone the repo over SSH. + +To use Git over SSH, you need to make sure that your SSH agent has your private +SSH keys added so that they can be used during authentication. + +- You can optionally add the path to the Taskfile in the repository by appending +`//` to the URL. +- You can also optionally specify a branch or tag to use by appending +`?ref=` to the end of the URL. If you omit a reference, the default branch +will be used. + + + + +Task has an [example remote Taskfile][example-remote-taskfile] in our repository +that you can use for testing and that we will use throughout this document: ```yaml version: '3' tasks: - hello: - silent: true + default: cmds: - - echo "Hello from the remote Taskfile!" + - task: hello + + hello: + cmds: + - echo "Hello Task!" ``` -and you run `task my-remote-namespace:hello`, it will print the text: "Hello -from the remote Taskfile!" to your console. +## Specifying a remote entrypoint + +By default, Task will look for one of the [supported file +names][supported-file-names] on your local filesystem. If you want to use a +remote file instead, you can pass its URI into the `--taskfile`/`-t` flag just +like you would to specify a different local file. For example: + + + +```shell +$ task --taskfile https://raw.githubusercontent.com/go-task/task/main/website/static/Taskfile.yml +task: [hello] echo "Hello Task!" +Hello Task! +``` + + +```shell +$ task --taskfile https://github.com/go-task/task.git//website/static/Taskfile.yml?ref=main +task: [hello] echo "Hello Task!" +Hello Task! +``` + + +```shell +$ task --taskfile git@github.com/go-task/task.git//website/static/Taskfile.yml?ref=main +task: [hello] echo "Hello Task!" +Hello Task! +``` + + + +## Including remote Taskfiles + +Including a remote file works exactly the same way that including a local file +does. You just need to replace the local path with a remote URI. Any tasks in +the remote Taskfile will be available to run from your main Taskfile. + + + +```yaml +version: '3' + +includes: + my-remote-namespace: https://raw.githubusercontent.com/go-task/task/main/website/static/Taskfile.yml +``` + + +```yaml +version: '3' + +includes: + my-remote-namespace: https://github.com/go-task/task.git//website/static/Taskfile.yml?ref=main +``` + + +```yaml +version: '3' + +includes: + my-remote-namespace: git@github.com/go-task/task.git//website/static/Taskfile.yml?ref=main +``` + + + +```shell +$ task my-remote-namespace:hello +task: [hello] echo "Hello Task!" +Hello Task! +``` + +### Authenticating using environment variables The Taskfile location is processed by the templating system, so you can reference environment variables in your URL if you need to add authentication. @@ -59,19 +180,6 @@ includes: my-remote-namespace: https://{{.TOKEN}}@raw.githubusercontent.com/my-org/my-repo/main/Taskfile.yml ``` -`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 @@ -104,20 +212,26 @@ flag. Before enabling this flag, you should: Task currently supports both `http` and `https` URLs. However, the `http` requests will not execute by default unless you run the task with the `--insecure` flag. This is to protect you from accidentally running a remote -Taskfile that is via an unencrypted connection. Sources that are not protected -by TLS are vulnerable to [man-in-the-middle attacks][man-in-the-middle-attacks] -and should be avoided unless you know what you are doing. +Taskfile that is downloaded via an unencrypted connection. Sources that are not +protected by TLS are vulnerable to [man-in-the-middle +attacks][man-in-the-middle-attacks] and should be avoided unless you know what +you are doing. ## Caching & Running Offline Whenever you run a remote Taskfile, the latest copy will be downloaded from the -internet and cached locally. If for whatever reason, you lose access to the -internet, you will still be able to run your tasks by specifying the `--offline` -flag. This will tell Task to use the latest cached version of the file instead -of trying to download it. You are able to use the `--download` flag to update -the cached version of the remote files without running any tasks. You are able -to use the `--clear-cache` flag to clear all cached version of the remote files -without running any tasks. +internet and cached locally. This cached file will be used for all future +invocations of the Taskfile until the cache expires. Once it expires, Task will +download the latest copy of the file and update the cache. By default, the cache +is set to expire immediately. This means that Task will always fetch the latest +version. However, the cache expiry duration can be modified by setting the +`--expiry` flag. + +If for any reason you lose access to the internet or you are running Task in +offline mode (via the `--offline` flag or `TASK_OFFLINE` environment variable), +Task will run the any available cached files _even if they are expired_. This +means that you should never be stuck without the ability to run your tasks as +long as you have downloaded a remote Taskfile at least once. By default, Task will timeout requests to download remote files after 10 seconds and look for a cached copy instead. This timeout can be configured by setting @@ -129,7 +243,14 @@ By default, the cache is stored in the Task temp directory, represented by the override the location of the cache by setting the `TASK_REMOTE_DIR` environment variable. This way, you can share the cache between different projects. +You can force Task to ignore the cache and download the latest version +by using the `--download` flag. + +You can use the `--clear-cache` flag to clear all cached remote files. + {/* prettier-ignore-start */} [enabling-experiments]: ./experiments.mdx#enabling-experiments [man-in-the-middle-attacks]: https://en.wikipedia.org/wiki/Man-in-the-middle_attack +[supported-file-names]: https://taskfile.dev/usage/#supported-file-names +[example-remote-taskfile]: https://raw.githubusercontent.com/go-task/task/main/website/static/Taskfile.yml {/* prettier-ignore-end */} diff --git a/website/versioned_docs/version-latest/getting_started.mdx b/website/versioned_docs/version-latest/getting_started.mdx index bef3482e..e79108d2 100644 --- a/website/versioned_docs/version-latest/getting_started.mdx +++ b/website/versioned_docs/version-latest/getting_started.mdx @@ -62,8 +62,8 @@ the commands. ## Calling a task -To call the task, you simply invoke `task` followed by the name of the task you -want to run. In this case, the name of the task is `default`, so you should run: +To call the task, invoke `task` followed by the name of the task you want to +run. In this case, the name of the task is `default`, so you should run: ```shell task default diff --git a/website/versioned_docs/version-latest/installation.mdx b/website/versioned_docs/version-latest/installation.mdx index 223f274c..b216aa3d 100644 --- a/website/versioned_docs/version-latest/installation.mdx +++ b/website/versioned_docs/version-latest/installation.mdx @@ -181,6 +181,11 @@ to install a specific version: sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d v3.36.0 ``` +Parameters are order specific, to set both installation directory and version: +```shell +sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b ~/.local/bin v3.42.1 +``` + ### GitHub Actions If you want to install Task in GitHub Actions you can try using diff --git a/website/versioned_docs/version-latest/reference/cli.mdx b/website/versioned_docs/version-latest/reference/cli.mdx index 4afe84b8..e5094a9a 100644 --- a/website/versioned_docs/version-latest/reference/cli.mdx +++ b/website/versioned_docs/version-latest/reference/cli.mdx @@ -24,7 +24,7 @@ If `--` is given, all remaining arguments will be assigned to a special | ----- | --------------------------- | -------- | -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `-c` | `--color` | `bool` | `true` | Colored output. Enabled by default. Set flag to `false` or use `NO_COLOR=1` to disable. | | `-C` | `--concurrency` | `int` | `0` | Limit number tasks to run concurrently. Zero means unlimited. | -| `-d` | `--dir` | `string` | Working directory | Sets directory of execution. | +| `-d` | `--dir` | `string` | Working directory | Sets the directory in which Task will execute and look for a Taskfile. | | `-n` | `--dry` | `bool` | `false` | Compiles and prints tasks in the order that they would be run, without executing them. | | `-x` | `--exit-code` | `bool` | `false` | Pass-through the exit code of the task command. | | `-f` | `--force` | `bool` | `false` | Forces execution even when the task is up-to-date. | diff --git a/website/versioned_docs/version-latest/reference/package.mdx b/website/versioned_docs/version-latest/reference/package.mdx index 1f753897..4777f5df 100644 --- a/website/versioned_docs/version-latest/reference/package.mdx +++ b/website/versioned_docs/version-latest/reference/package.mdx @@ -23,7 +23,145 @@ changelog entry for breaking changes to the package API. Task is primarily a CLI tool that is agnostic of any programming language. However, it is written in Go and therefore can also be used as a Go package too. This can be useful if you are already using Go in your project and you need to -extend Task's functionality in some way. +extend Task's functionality in some way. In this document, we describe the +public API surface of Task and how to use it. This may also be useful if you +want to contribute to Task or understand how it works in more detail. -The full generated documentation for the package API is available on -[pkg.go.dev](https://pkg.go.dev/github.com/go-task/task/v3). +## Key packages + +The following packages make up the most important parts of Task's package API. +Below we have listed what they are for and some of the key types available: + +### [`github.com/go-task/task/v3`] + +The core task package provides most of the main functionality for Task including +fetching and executing tasks from a Taskfile. At this time, the vast majority of +the this package's functionality is exposed via the [`task.Executor`] which +allows the user to fetch and execute tasks from a Taskfile. + +:::note +This is the package which is most likely to be the subject of breaking changes +as we refine the API. +::: + +### [`github.com/go-task/task/v3/taskfile`] + +The `taskfile` package provides utilities for _reading_ Taskfiles from various +sources. These sources can be local files, remote files, or even in-memory +strings (via stdin). + +- [`taskfile.Node`] - A reference to the location of a Taskfile. A `Node` is an + interface that has several implementations: + - [`taskfile.FileNode`] - Local files + - [`taskfile.HTTPNode`] - Remote files via HTTP/HTTPS + - [`taskfile.GitNode`] - Remote files via Git + - [`taskfile.StdinNode`] - In-memory strings (via stdin) +- [`taskfile.Reader`] - Accepts a `Node` and reads the Taskfile from it. +- [`taskfile.Snippet`] - Mostly used for rendering Taskfile errors. A snippet + stores a small part of a taskfile around a given line number and column. The + output can be syntax highlighted for CLIs and include line/column indicators. + +### [`github.com/go-task/task/v3/taskfile/ast`] + +AST stands for ["Abstract Syntax Tree"][ast]. An AST allows us to easily +represent the Taskfile syntax in Go. This package provides a way to parse +Taskfile YAML into an AST and store them in memory. + +- [`ast.TaskfileGraph`] - Represents a set of Taskfiles and their dependencies + between one another. +- [`ast.Taskfile`] - Represents a single Taskfile or a set of merged Taskfiles. +The `Taskfile` type contains all of the subtypes for the Taskfile syntax, such +as `tasks`, `includes`, `vars`, etc. These are not listed here for brevity. + +### [`github.com/go-task/task/v3/errors`] + +Contains all of the error types used in Task. All of these types implement the +[`errors.TaskError`] interface which wraps Go's standard [`error`] interface. +This allows you to call the `Code` method on the error to obtain the unique exit +code for any error. + +## Reading Taskfiles + +Start by importing the `github.com/go-task/task/v3/taskfile` package. This +provides all of the functions you need to read a Taskfile into memory: + +```go +import ( + "github.com/go-task/task/v3/taskfile" +) +``` + +Reading Taskfiles is done by using a [`taskfile.Reader`] and an implementation +of [`taskfile.Node`]. In this example we will read a local file by using the +[`taskfile.FileNode`] type. You can create this by calling the +[`taskfile.NewFileNode`] function: + +```go +node := taskfile.NewFileNode("Taskfile.yml", "./path/to/dir") +``` + +and then create a your reader by calling the [`taskfile.NewReader`] function and +passing any functional options you want to use. For example, you could pass a +debug function to the reader which will be called with debug messages: + +```go +reader := taskfile.NewReader( + taskfile.WithDebugFunc(func(s string) { + slog.Debug(s) + }), +) +``` + +Now that everything is set up, you can read the Taskfile (and any included +Taskfiles) by calling the `Read` method on the reader and pass the `Node` as an +argument: + +```go +ctx := context.Background() +tfg, err := reader.Read(ctx, node) +// handle error +``` + +This returns an instance of [`ast.TaskfileGraph`] which is a "Directed Acyclic +Graph" (DAG) of all the parsed Taskfiles. We use this graph to store and resolve +the `includes` directives in Taskfiles. However most of the time, you will want +a merged Taskfile. To do this, simply call the `Merge` method on the Taskfile +graph: + +```go +tf, err := tfg.Merge() +// handle error +``` + +This compiles the DAG into a single [`ast.Taskfile`] containing all the +namespaces and tasks from all the Taskfiles we read. + +:::note +We plan to remove AST merging in the future as it is unnecessarily complex and +causes lots of issues with scoping. +::: + +{/* prettier-ignore-start */} +[`github.com/go-task/task/v3`]: https://pkg.go.dev/github.com/go-task/task/v3 +[`github.com/go-task/task/v3/taskfile`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile +[`github.com/go-task/task/v3/taskfile/ast`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile/ast +[`github.com/go-task/task/v3/errors`]: https://pkg.go.dev/github.com/go-task/task/v3/errors + +[`ast.TaskfileGraph`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile/ast#TaskfileGraph +[`ast.Taskfile`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile/ast#Taskfile +[`taskfile.Node`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile#Node +[`taskfile.FileNode`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile#FileNode +[`taskfile.HTTPNode`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile#HTTPNode +[`taskfile.GitNode`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile#GitNode +[`taskfile.StdinNode`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile#StdinNode +[`taskfile.NewFileNode`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile#NewFileNode +[`taskfile.Reader`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile#Reader +[`taskfile.NewReader`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile#NewReader +[`taskfile.Snippet`]: https://pkg.go.dev/github.com/go-task/task/v3/taskfile#Snippet +[`task.Executor`]: https://pkg.go.dev/github.com/go-task/task/v3#Executor +[`task.Formatter`]: https://pkg.go.dev/github.com/go-task/task/v3#Formatter +[`errors.TaskError`]: https://pkg.go.dev/github.com/go-task/task/v3/errors#TaskError +[`error`]: https://pkg.go.dev/builtin#error + +[ast]: https://en.wikipedia.org/wiki/Abstract_syntax_tree +{/* prettier-ignore-end */} diff --git a/website/versioned_docs/version-latest/reference/schema.mdx b/website/versioned_docs/version-latest/reference/schema.mdx index 29dc58c8..f426e3fa 100644 --- a/website/versioned_docs/version-latest/reference/schema.mdx +++ b/website/versioned_docs/version-latest/reference/schema.mdx @@ -196,9 +196,12 @@ If defined as a string this is a shell command, otherwise it is a map defining a The `for` parameter can be defined as a string, a list of strings or a map. If it is defined as a string, you can give it any of the following values: -- `source` - Will run the command for each source file defined on the task. +- `sources` - Will run the command for each source file defined on the task. (Glob patterns will be resolved, so `*.go` will run for every Go file that matches). +- `generates` - Will run the command for each file defined in the task's generates + list. (Glob patterns will be resolved, so `*.txt` will run for every text file + that matches). If it is defined as a list of strings, the command will be run for each value. diff --git a/website/versioned_docs/version-latest/reference/templating.mdx b/website/versioned_docs/version-latest/reference/templating.mdx index 98d45bae..fc5e4fb0 100644 --- a/website/versioned_docs/version-latest/reference/templating.mdx +++ b/website/versioned_docs/version-latest/reference/templating.mdx @@ -115,7 +115,7 @@ special variable will be overridden. | `TASKFILE` | The absolute path of the included Taskfile. | | `TASKFILE_DIR` | The absolute path of the included Taskfile directory. | | `TASK_DIR` | The absolute path of the directory where the task is executed. | -| `USER_WORKING_DIR` | The absolute path of the directory `task` was called from. | +| `USER_WORKING_DIR` | The absolute path of the directory `task` was called from, or the value of `--dir` (`-d`) if given. | | `CHECKSUM` | The checksum of the files listed in `sources`. Only available within the `status` prop and if method is set to `checksum`. | | `TIMESTAMP` | The date object of the greatest timestamp of the files listed in `sources`. Only available within the `status` prop and if method is set to `timestamp`. | | `TASK_VERSION` | The current version of task. | diff --git a/website/versioned_docs/version-latest/usage.mdx b/website/versioned_docs/version-latest/usage.mdx index 0bfa5086..e672cf82 100644 --- a/website/versioned_docs/version-latest/usage.mdx +++ b/website/versioned_docs/version-latest/usage.mdx @@ -61,6 +61,12 @@ In this example, we can run `cd ` and `task up` and as long as the `` directory contains a `docker-compose.yml`, the Docker composition will be brought up. +:::info + +`.USER_WORKING_DIR` will contain the value of the `--dir` (`-d`) flag, if given. + +::: + ### Running a global Taskfile If you call Task with the `--global` (alias `-g`) flag, it will look for your @@ -309,45 +315,38 @@ You can flatten the included Taskfile tasks into the main Taskfile by using the It means that the included Taskfile tasks will be available without the namespace. - + + - +```yaml +version: '3' - ```yaml - version: '3' +includes: + lib: + taskfile: ./Included.yml + flatten: true - includes: - lib: - taskfile: ./Included.yml - flatten: true +tasks: + greet: + cmds: + - echo "Greet" + - task: foo +``` - tasks: - greet: - cmds: - - echo "Greet" - - task: foo - ``` + + +```yaml +version: '3' - - - - ```yaml - version: '3' - - tasks: - foo: - cmds: - - echo "Foo" - ``` - - - +tasks: + foo: + cmds: + - echo "Foo" +``` + + If you run `task -a` it will print : @@ -368,43 +367,37 @@ Foo If multiple tasks have the same name, an error will be thrown: - + + - +```yaml +version: '3' +includes: + lib: + taskfile: ./Included.yml + flatten: true - ```yaml - version: '3' - includes: - lib: - taskfile: ./Included.yml - flatten: true +tasks: + greet: + cmds: + - echo "Greet" + - task: foo +``` - tasks: - greet: - cmds: - - echo "Greet" - - task: foo - ``` + + +```yaml +version: '3' - - +tasks: + greet: + cmds: + - echo "Foo" +``` - ```yaml - version: '3' - - tasks: - greet: - cmds: - - echo "Foo" - ``` - - - + + If you run `task -a` it will print: ```text @@ -420,35 +413,29 @@ You can do this by using the [`excludes` option](#exclude-tasks-from-being-inclu You can exclude tasks from being included by using the `excludes` option. This option takes the list of tasks to be excluded from this include. - + + - - ```yaml - version: '3' - includes: - included: - taskfile: ./Included.yml - excludes: [foo] - ``` +```yaml +version: '3' + includes: + included: + taskfile: ./Included.yml + excludes: [foo] +``` + + - - +```yaml +version: '3' - ```yaml - version: '3' +tasks: + foo: echo "Foo" + bar: echo "Bar" +``` - tasks: - foo: echo "Foo" - bar: echo "Bar" - ``` - - - + `task included:foo` will throw an error because the `foo` task is excluded but `task included:bar` will work and display `Bar`. @@ -1113,53 +1100,38 @@ variable types are supported: - `int` - `float` - `array` +- `map` :::note -Maps are not supported by default, but there is an -[experiment][map-variables] that can be enabled to add support. If -you're interested in this functionality, we would appreciate your feedback. -:pray: - -In the meantime, it is technically possible to define a map using a `ref` resolver and a templating function. For example: - -```yaml -version: '3' - -tasks: - task-with-map: - vars: - FOO: - ref: dict "a" "1" "b" "2" "c" "3" - cmds: - - echo {{.FOO}} -``` - -```txt -map[a:1 b:2 c:3] -``` - -OR by using the same technique with JSON: - -```yaml -version: '3' - -tasks: - task-with-map: - vars: - JSON: '{"a": 1, "b": 2, "c": 3}' - FOO: - ref: "fromJson .JSON" - cmds: - - echo {{.FOO}} -``` - -```txt -map[a:1 b:2 c:3] -``` +Defining a map requires that you use a special `map` subkey (see example below). ::: +```yaml +version: 3 + +tasks: + foo: + vars: + STRING: 'Hello, World!' + BOOL: true + INT: 42 + FLOAT: 3.14 + ARRAY: [1, 2, 3] + MAP: + map: {A: 1, B: 2, C: 3} + cmds: + - 'echo {{.STRING}}' # Hello, World! + - 'echo {{.BOOL}}' # true + - 'echo {{.INT}}' # 42 + - 'echo {{.FLOAT}}' # 3.14 + - 'echo {{.ARRAY}}' # [1 2 3] + - 'echo {{.ARRAY.0}}' # 1 + - 'echo {{.MAP}}' # map[A:1 B:2 C:3] + - 'echo {{.MAP.A}}' # 1 +``` + Variables can be set in many places in a Taskfile. When executing [templates][templating-reference], Task will look for variables in the order listed below (most important first): @@ -1270,13 +1242,8 @@ a value from one task to another. However, the templating engine is only able to output strings. If you want to pass something other than a string to another task then you will need to use a reference (`ref`) instead. - - - + + ```yaml version: 3 @@ -1295,7 +1262,7 @@ tasks: ``` - + ```yaml version: 3 @@ -1314,7 +1281,8 @@ tasks: - 'echo {{index .FOO 0}}' # <-- FOO is still a map so the task outputs 'A' as expected ``` - + + This also works the same way when calling `deps` and when defining a variable and can be used in any combination: @@ -1339,8 +1307,8 @@ tasks: ``` All references use the same templating syntax as regular templates, so in -addition to simply calling `.FOO`, you can also pass subkeys (`.FOO.BAR`) or -indexes (`index .FOO 0`) and use functions (`len .FOO`) as described in the +addition to calling `.FOO`, you can also pass subkeys (`.FOO.BAR`) or indexes +(`index .FOO 0`) and use functions (`len .FOO`) as described in the [templating-reference][templating-reference]: ```yaml @@ -1360,6 +1328,29 @@ tasks: - 'echo {{.FOO}}' # <-- FOO is just the letter 'A' ``` +### Parsing JSON/YAML into map variables + +If you have a raw JSON or YAML string that you want to process in Task, you can +use a combination of the `ref` keyword and the `fromJson` or `fromYaml` +templating functions to parse the string into a map variable. For example: + +```yaml +version: '3' + +tasks: + task-with-map: + vars: + JSON: '{"a": 1, "b": 2, "c": 3}' + FOO: + ref: "fromJson .JSON" + cmds: + - echo {{.FOO}} +``` + +```txt +map[a:1 b:2 c:3] +``` + ## Looping over values Task allows you to loop over certain values and execute a command for each. @@ -1433,9 +1424,13 @@ tasks: cmd: echo "{{.ITEM.OS}}/{{.ITEM.ARCH}}" ``` -### Looping over your task's sources +### Looping over your task's sources or generated files -You are also able to loop over the sources of your task: +You are also able to loop over the sources of your task or the files it +generates: + + + ```yaml version: '3' @@ -1450,14 +1445,37 @@ tasks: cmd: cat {{ .ITEM }} ``` -This will also work if you use globbing syntax in your sources. For example, if -you specify a source for `*.txt`, the loop will iterate over all files that -match that glob. + + -Source paths will always be returned as paths relative to the task directory. If -you need to convert this to an absolute path, you can use the built-in -`joinPath` function. There are some [special variables](/reference/templating/#special-variables) -that you may find useful for this. +```yaml +version: '3' + +tasks: + default: + generates: + - foo.txt + - bar.txt + cmds: + - for: generates + cmd: cat {{ .ITEM }} +``` + + + + +This will also work if you use globbing syntax in `sources` or `generates`. For +example, if you specify a source for `*.txt`, the loop will iterate over all +files that match that glob. + +Paths will always be returned as paths relative to the task directory. If you +need to convert this to an absolute path, you can use the built-in `joinPath` +function. There are some [special +variables](/reference/templating/#special-variables) that you may find useful +for this. + + + ```yaml version: '3' @@ -1475,11 +1493,33 @@ tasks: cmd: cat {{joinPath .MY_DIR .ITEM}} ``` + + + +```yaml +version: '3' + +tasks: + default: + vars: + MY_DIR: /path/to/dir + dir: '{{.MY_DIR}}' + generates: + - foo.txt + - bar.txt + cmds: + - for: generates + cmd: cat {{joinPath .MY_DIR .ITEM}} +``` + + + + ### Looping over variables -To loop over the contents of a variable, you simply need to specify the variable -you want to loop over. By default, string variables will be split on any -whitespace characters. +To loop over the contents of a variable, use the `var` key followed by the name +of the variable you want to loop over. By default, string variables will be +split on any whitespace characters. ```yaml version: '3' @@ -1508,7 +1548,7 @@ tasks: cmd: cat {{.ITEM}} ``` -You can also loop over arrays directly and maps: +You can also loop over arrays and maps directly: ```yaml version: 3 @@ -1674,36 +1714,45 @@ clear what they contain: version: '3' tasks: - echo-*: + start:*:*: vars: - TEXT: '{{index .MATCH 0}}' + SERVICE: "{{index .MATCH 0}}" + REPLICAS: "{{index .MATCH 1}}" cmds: - - echo {{.TEXT}} + - echo "Starting {{.SERVICE}} with {{.REPLICAS}} replicas" - run-*-*: + start:*: vars: - ARG_1: '{{index .MATCH 0}}' - ARG_2: '{{index .MATCH 1}}' + SERVICE: "{{index .MATCH 0}}" cmds: - - echo {{.ARG_1}} {{.ARG_2}} + - echo "Starting {{.SERVICE}}" ``` +This call matches the `start:*` task and the string "foo" is captured by the +wildcard and stored in the `.MATCH` variable. We then index the `.MATCH` array +and store the result in the `.SERVICE` variable which is then echoed out in the +cmds: + ```shell -# This call matches the "echo-*" task and the string "hello" is captured by the -# wildcard and stored in the .MATCH variable. We then index the .MATCH array and -# store the result in the .TEXT variable which is then echoed out in the cmds. -$ task echo-hello -hello -# You can use whitespace in your arguments as long as you quote the task name -$ task "echo-hello world" -hello world -# And you can pass multiple arguments -$ task run-foo-bar -foo bar +$ task start:foo +Starting foo ``` -If multiple matching tasks are found, an error occurs. If you are using included -Taskfiles, tasks in parent files will be considered first. +You can use whitespace in your arguments as long as you quote the task name: + +```shell +$ task "start:foo bar" +Starting foo bar +``` + +If multiple matching tasks are found, the first one listed in the Taskfile will +be used. If you are using included Taskfiles, tasks in parent files will be +considered first. + +```shell +$ task start:foo:3 +Starting foo with 3 replicas +``` ## Doing task cleanup with `defer` @@ -2285,9 +2334,11 @@ With the flags `--watch` or `-w` task will watch for file changes and run the task again. This requires the `sources` attribute to be given, so task knows which files to watch. -The default watch interval is 5 seconds, but it's possible to change it by -either setting `interval: '500ms'` in the root of the Taskfile or by passing it -as an argument like `--interval=500ms`. +The default watch interval is 100 milliseconds, but it's possible to change it +by either setting `interval: '500ms'` in the root of the Taskfile or by passing +it as an argument like `--interval=500ms`. +This interval is the time Task will wait for duplicated events. It will only run +the task again once, even if multiple changes happen within the interval. Also, it's possible to set `watch: true` in a given task and it'll automatically run in watch mode: @@ -2317,6 +2368,5 @@ if called by another task, either directly or as a dependency. {/* prettier-ignore-start */} [gotemplate]: https://golang.org/pkg/text/template/ -[map-variables]: ./experiments/map_variables.mdx [templating-reference]: ./reference/templating.mdx {/* prettier-ignore-end */}