diff --git a/website/.vitepress/config.ts b/website/.vitepress/config.ts index 92ad218a..fe9bb55e 100644 --- a/website/.vitepress/config.ts +++ b/website/.vitepress/config.ts @@ -370,6 +370,10 @@ export default defineConfig({ text: 'Guide', link: '/docs/guide' }, + { + text: 'Remote Taskfiles', + link: '/docs/remote-taskfiles' + }, { text: 'Reference', collapsed: true, diff --git a/website/src/docs/remote-taskfiles.md b/website/src/docs/remote-taskfiles.md new file mode 100644 index 00000000..93553710 --- /dev/null +++ b/website/src/docs/remote-taskfiles.md @@ -0,0 +1,336 @@ +--- +outline: deep +--- + +# Remote Taskfiles + +::: danger + +Never run remote Taskfiles from sources that you do not trust. + +::: + +Task 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. + +Task uses "nodes" to reference remote Taskfiles. There are a few different types +of node which you can use: + +::: code-group + +```text [HTTP/HTTPS] +https://raw.githubusercontent.com/go-task/task/main/website/src/public/Taskfile.yml +``` + +```text [Git over HTTP] +https://github.com/go-task/task.git//website/src/public/Taskfile.yml?ref=main +``` + +```text [Git over SSH] +git@github.com/go-task/task.git//website/src/public/Taskfile.yml?ref=main +``` + +::: + +## Node Types + +### HTTP/HTTPS + +`https://raw.githubusercontent.com/go-task/task/main/website/src/public/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 in turn until it finds a valid file. If it still does not +find a valid Taskfile, an error is returned. + +### Git over HTTP + +`https://github.com/go-task/task.git//website/src/public/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 over SSH + +`git@github.com/go-task/task.git//website/src/public/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 in our repository that you can use for +testing and that we will use throughout this document: + +```yaml +version: '3' + +tasks: + default: + cmds: + - task: hello + + hello: + cmds: + - echo "Hello Task!" +``` + +## Specifying a remote entrypoint + +By default, Task will look for one of the 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: + +::: code-group + +```shell [HTTP/HTTPS] +$ task --taskfile https://raw.githubusercontent.com/go-task/task/main/website/src/public/Taskfile.yml +task: [hello] echo "Hello Task!" +Hello Task! +``` + +```shell [Git over HTTP] +$ task --taskfile https://github.com/go-task/task.git//website/src/public/Taskfile.yml?ref=main +task: [hello] echo "Hello Task!" +Hello Task! +``` + +```shell [Git over SSH] +$ task --taskfile git@github.com/go-task/task.git//website/src/public/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. + +::: code-group + +```yaml [HTTP/HTTPS] +version: '3' + +includes: + my-remote-namespace: https://raw.githubusercontent.com/go-task/task/main/website/src/public/Taskfile.yml +``` + +```yaml [Git over HTTP] +version: '3' + +includes: + my-remote-namespace: https://github.com/go-task/task.git//website/src/public/Taskfile.yml?ref=main +``` + +```yaml [Git over SSH] +version: '3' + +includes: + my-remote-namespace: git@github.com/go-task/task.git//website/src/public/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. +For example: + +```yaml +version: '3' + +includes: + my-remote-namespace: https://{{.TOKEN}}@raw.githubusercontent.com/my-org/my-repo/main/Taskfile.yml +``` + +## Special Variables + +The file-path [special variables](../reference/templating.md#file-paths) behave +differently when a Taskfile is loaded from a remote source, because there is no +local file or directory that corresponds 1:1 to the Taskfile: + +| Variable | Value when loaded remotely | +| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `TASKFILE` / `ROOT_TASKFILE` | The original URL, unchanged | +| `TASKFILE_DIR` / `ROOT_DIR` | Empty string — a directory variable cannot point to a URL | +| `TASK_DIR` | Resolved against `USER_WORKING_DIR` (relative `dir:` → joined with `USER_WORKING_DIR`, empty `dir:` → `USER_WORKING_DIR`, absolute `dir:` → kept as-is) | + +If a remote Taskfile includes a local Taskfile (or vice-versa), each variable +reflects the source of the Taskfile it refers to. + +## Security + +### Automatic checksums + +Running commands from sources that you do not control is always a potential +security risk. For this reason, we have added some automatic checks when using +remote Taskfiles: + +1. When running a task from a remote Taskfile for the first time, Task will + print a warning to the console asking you to check that you are sure that you + trust the source of the Taskfile. If you do not accept the prompt, then Task + will exit with code `104` (not trusted) and nothing will run. If you accept + the prompt, the remote Taskfile will run and further calls to the remote + Taskfile will not prompt you again. +2. Whenever you run a remote Taskfile, Task will create and store a checksum of + the file that you are running. If the checksum changes, then Task will print + another warning to the console to inform you that the contents of the remote + file has changed. If you do not accept the prompt, then Task will exit with + code `104` (not trusted) and nothing will run. If you accept the prompt, the + checksum will be updated and the remote Taskfile will run. + +Sometimes you need to run Task in an environment that does not have an +interactive terminal, so you are not able to accept a prompt. In these cases you +are able to tell task to accept these prompts automatically by using the `--yes` +flag or the `--trust` flag. The `--trust` flag allows you to specify trusted +hosts for remote Taskfiles, while `--yes` applies to all prompts in Task. You +can also configure trusted hosts in your +[taskrc configuration](./reference/config.md#remotetrusted-hosts) using +`remote.trusted-hosts`. Before enabling automatic trust, you should: + +1. Be sure that you trust the source and contents of the remote Taskfile. +2. Consider using a pinned version of the remote Taskfile (e.g. A link + containing a commit hash) to prevent Task from automatically accepting a + prompt that says a remote Taskfile has changed. + +### Manual checksum pinning + +Alternatively, if you expect the contents of your remote files to be a constant +value, you can pin the checksum of the included file instead: + +```yaml +version: '3' + +includes: + included: + taskfile: https://taskfile.dev + checksum: c153e97e0b3a998a7ed2e61064c6ddaddd0de0c525feefd6bba8569827d8efe9 +``` + +This will disable the automatic checksum prompts discussed above. However, if +the checksums do not match, Task will exit immediately with an error. When +setting this up for the first time, you may not know the correct value of the +checksum. There are a couple of ways you can obtain this: + +1. Add the include normally without the `checksum` key. The first time you run + the included Taskfile, a `.task/remote` temporary directory is created. Find + the correct set of files for your included Taskfile and open the file that + ends with `.checksum`. You can copy the contents of this file and paste it + into the `checksum` key of your include. This method is safest as it allows + you to inspect the downloaded Taskfile before you pin it. +2. Alternatively, add the include with a temporary random value in the + `checksum` key. When you try to run the Taskfile, you will get an error that + will report the incorrect expected checksum and the actual checksum. You can + copy the actual checksum and replace your temporary random value. + +### TLS + +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 downloaded via an unencrypted connection. Sources that are not +protected by TLS are vulnerable to man-in-the-middle attacks and should be +avoided unless you know what you are doing. + +#### Custom Certificates + +If your remote Taskfiles are hosted on a server that uses a custom CA +certificate (e.g., a corporate internal server), you can specify the CA +certificate using the `--cacert` flag: + +```shell +task --taskfile https://internal.example.com/Taskfile.yml --cacert /path/to/ca.crt +``` + +For servers that require client certificate authentication (mTLS), you can +provide a client certificate and key: + +```shell +task --taskfile https://secure.example.com/Taskfile.yml \ + --cert /path/to/client.crt \ + --cert-key /path/to/client.key +``` + +::: warning + +Encrypted private keys are not currently supported. If your key is encrypted, +you must decrypt it first: + +```shell +openssl rsa -in encrypted.key -out decrypted.key +``` + +::: + +These options can also be configured in the +[configuration file](#configuration). + +## Caching & Running Offline + +Whenever you run a remote Taskfile, the latest copy will be downloaded from the +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 +the `--timeout` flag and specifying a duration. For example, `--timeout 5s` will +set the timeout to 5 seconds. + +By default, the cache is stored in the Task temp directory (`.task`). You can +override the location of the cache by using the `--remote-cache-dir` flag, the +`remote.cache-dir` option in your +[configuration file](./reference/config.md#remotecache-dir), or 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. + +## Configuration + +It is also possible to adjust remote Taskfile functionality can be edited via a +configuration file. Check out the + +- [CLI flags](./reference/cli.md#remote) +- [Environment Variables](./reference/environment.md#remote-taskfile-variables) +- [Config File](./reference/config.md#remote)