From 8108ca6d7e5f34b9b9723f945a9c4b137f2e10ef Mon Sep 17 00:00:00 2001 From: James Hegedus Date: Wed, 5 Apr 2023 23:31:56 +1000 Subject: [PATCH] docs: verbose plugin create command details (#1445) --- docs/plugins/create.md | 909 +++++++++++++++++++++++++++++++++++------ 1 file changed, 773 insertions(+), 136 deletions(-) diff --git a/docs/plugins/create.md b/docs/plugins/create.md index cafc9f05..b87566fa 100644 --- a/docs/plugins/create.md +++ b/docs/plugins/create.md @@ -1,104 +1,525 @@ # Create a Plugin -## What's in a Plugin +A plugin is a Git repo with some executable scripts to support versioning a +language / tool. These scripts are run by asdf using specific commands to +support features such as `asdf list-all `, `asdf install ` +etc. -A plugin is a git repo, with a couple executable scripts, to support versioning another language or tool. These scripts are run when `list-all`, `install` or `uninstall` commands are run. You can set or unset env vars and do anything required to setup the environment for the tool. +## Quickstart + +There are two options to get started with creating your own plugin: + +1. use the + [asdf-vm/asdf-plugin-template](https://github.com/asdf-vm/asdf-plugin-template) + repository to + [generate](https://github.com/asdf-vm/asdf-plugin-template/generate) a plugin + repo (named `asdf-`) with default scripts implemented. Once + generated, clone the repo and run the `setup.bash` script to interactively + update the template. +2. start your own repo called `asdf-` and implement the required + scripts as listed in the documentation below. + +### Golden Rules for Plugin Scripts + +- scripts should **NOT** call other `asdf` commands +- keep your dependency list of Shell tools/commands small +- avoid non-portable tools or command flags. For example, `sort -V`. See our + asdf core + [list of banned commands](https://github.com/asdf-vm/asdf/blob/master/test/banned_commands.bats) + +## Scripts Overview + +The full list of scripts callable from asdf. + +| Script | Description | +| :---------------------------------------------------------------------------------------------------- |:-----------------------------------------------------------------| +| [bin/list-all](#bin-list-all) | List all installable versions | +| [bin/download](#bin-download) | Download source code or binary for the specified version | +| [bin/install](#bin-install) | Installs the specified version | +| [bin/latest-stable](#bin-latest-stable) | List the latest stable version of the specified tool | +| [bin/help.overview](#bin-help.overview) | Output a general description about the plugin & tool | +| [bin/help.deps](#bin-help.deps) | Output a list of dependencies per Operating System | +| [bin/help.config](#bin-help.config) | Output plugin or tool configuration information | +| [bin/help.links](#bin-help.links) | Output a list of links for the plugin or tool | +| [bin/list-bin-paths](#bin-list-bin-paths) | List relative paths to directories with binaries to create shims | +| [bin/exec-env](#bin-exec-env) | Prepare the environment for running the binaries | +| [bin/exec-path](#bin-exec-path) | Output the executable path for a version of a tool | +| [bin/uninstall](#bin-uninstall) | Uninstall a specific version of a tool | +| [bin/list-legacy-filenames](#bin-list-legacy-filenames) | Output filenames of legacy version files: `.ruby-version` | +| [bin/parse-legacy-file](#bin-parse-legacy-file) | Custom parser for legacy version files | +| [bin/post-plugin-add](#bin-post-plugin-add) | Hook to execute after a plugin has been added | +| [bin/post-plugin-update](#bin-post-plugin-update) | Hook to execute after a plugin has been updated | +| [bin/pre-plugin-remove](#bin-pre-plugin-remove) | Hook to execute before a plugin is removed | + +To see which commands invoke which scripts, see the detailed documentation for +each script. + +## Environment Variables Overview + +The full list of Environment Variables used throughout all scripts. + +| Environment Variables | Description | +| :----------------------- |:----------------------------------------------------------------------------------------| +| `ASDF_INSTALL_TYPE` | `version` or `ref` | +| `ASDF_INSTALL_VERSION` | full version number or Git Ref depending on `ASDF_INSTALL_TYPE` | +| `ASDF_INSTALL_PATH` | the path to where the tool _should_, or _has been_ installed | +| `ASDF_CONCURRENCY` | the number of cores to use when compiling the source code. Useful for setting `make -j` | +| `ASDF_DOWNLOAD_PATH` | the path to where the source code or binary was downloaded to by `bin/download` | +| `ASDF_PLUGIN_PATH` | the path the plugin was installed | +| `ASDF_PLUGIN_SOURCE_URL` | the source URL of the plugin | +| `ASDF_PLUGIN_PREV_REF` | prevous `git-ref` of the plugin repo | +| `ASDF_PLUGIN_POST_REF` | updated `git-ref` of the plugin repo | +| `ASDF_CMD_FILE` | resolves to the full path of the file being sourced | + +::: tip NOTE + +**Not all environment variables are available in all scripts.** Check the +documentation for each script below to see which env vars are available to it. + +::: ## Required Scripts -- `bin/list-all` - lists all installable versions -- `bin/download` - download source code or binary for the specified version -- `bin/install` - installs the specified version +### `bin/list-all` -## Environment Variables +**Description** -All scripts except `bin/list-all` will have access to the following env vars to act upon: +List all installable versions. -- `ASDF_INSTALL_TYPE` - `version` or `ref` -- `ASDF_INSTALL_VERSION` - if `ASDF_INSTALL_TYPE` is `version` then this will be the version number. Else it will be the git ref that is passed. Might point to a tag/commit/branch on the repo. -- `ASDF_INSTALL_PATH` - the dir where it _has been_ installed (or _should_ be installed in case of the `bin/install` script) +**Output Format** -These additional environment variables will be available to the `bin/install` script: +Must print a string with a **space-separated** list of versions. For example: -- `ASDF_CONCURRENCY` - the number of cores to use when compiling the source code. Useful for setting `make -j`. -- `ASDF_DOWNLOAD_PATH` - the path to where the source code or binary was downloaded by the `bin/download` script. - -These additional environment variables will be available to the `bin/download` script: - -- `ASDF_DOWNLOAD_PATH` - the path to where the source code or binary should be downloaded. - -#### bin/list-all - -Must print a string with a space-separated list of versions. Example output would be the following: - -```shell +```text:no-line-numbers 1.0.1 1.0.2 1.3.0 1.4 ``` -Note that the newest version should be listed last so it appears closer to the user's prompt. This is helpful since the `list-all` command prints each version on its own line. If there are many versions it's possible the early versions will be off screen. +Newest version should be last. -If versions are being pulled from releases page on a website it's recommended to not sort the versions if at all possible. Often the versions are already in the correct order or, in reverse order, in which case something like `tac` should suffice. If you must sort versions manually you cannot rely on `sort -V` since it is not supported on OSX. An alternate sort function [like this is a better choice](https://github.com/vic/asdf-idris/blob/master/bin/list-all#L6). +asdf core will print each version on its own line, potentially pushing some +versions offscreen. -#### bin/download +**Sorting** -This script must download the source or binary, in the path contained in the `ASDF_DOWNLOAD_PATH` environment variable. If the downloaded source or binary is compressed, only the uncompressed source code or binary may be placed in the `ASDF_DOWNLOAD_PATH` directory. +If versions are being pulled from releases page on a website it's recommended to +leave the versions in the provided order as they are often already in the +correct order. If they are in reverse order piping them through `tac` should +suffice. -The script must exit with a status of `0` when the download is successful. If the download fails the script must exit with any non-zero exit status. +If sorting is unavoidable, `sort -V` is not portable, so we suggest either: -If possible the script should only place files in the `ASDF_DOWNLOAD_PATH`. If the download fails no files should be placed in the directory. +- [using the Git sort capability](https://github.com/asdf-vm/asdf-plugin-template/blob/main/template/lib/utils.bash) + (requires Git >= `v2.18.0`) +- [writing a custom sort method](https://github.com/vic/asdf-idris/blob/master/bin/list-all#L6) + (requires `sed`, `sort` & `awk`) -If this script is not present asdf will assume that the `bin/install` script is present and will download and install the version. asdf only works without this script to support legacy plugins. All plugins must include this script, and eventually support for legacy plugins will be removed. +**Environment Variables available to script** -#### bin/install +No environment variables are provided to this script. -This script should install the version, in the path mentioned in `ASDF_INSTALL_PATH`. By default, asdf will create shims for any files in `$ASDF_INSTALL_PATH/bin` (this can be customized with the optional [bin/list-bin-paths](#binlist-bin-paths) script). +**Commands that invoke this script** -The install script should exit with a status of `0` when the installation is successful. If the installation fails the script should exit with any non-zero exit status. +- `asdf list all [version]` +- `asdf list all nodejs`: lists all versions as returned by this script, one on + each line. +- `asdf list all nodejs 18`: lists all versions as returned by this script, one + on each line, with a filter matching any version beginning with `18` applied. -If possible the script should only place files in the `ASDF_INSTALL_PATH` directory once the build and installation of the tool is deemed successful by the install script. asdf [checks for the existence](https://github.com/asdf-vm/asdf/blob/242d132afbf710fe3c7ec23c68cec7bdd2c78ab5/lib/utils.sh#L44) of the `ASDF_INSTALL_PATH` directory in order to determine if that version of the tool is installed. If the `ASDF_INSTALL_PATH` directory is populated at the beginning of the installation process other asdf commands run in other terminals during the installation may consider that version of the tool installed, even when it is not fully installed. +**Call signature from asdf core** -If you want your plugin to work with asdf version 0.7._ and earlier and version 0.8._ and newer check for the presence of the `ASDF_DOWNLOAD_PATH` environment variable. If it is not set download the source code in the bin/install callback. If it is set assume the `bin/download` script already downloaded it. +No parameters provided. + +```bash:no-line-numbers +"${plugin_path}/bin/list-all" +``` + +--- + +### `bin/download` + +**Description** + +Download the source code or binary for a specific version of a tool to a specified location. + +**Implementation Details** + +- The script must download the source or binary to the directory specified by `ASDF_DOWNLOAD_PATH`. +- Only the decompressed source code or binary should be placed in the `ASDF_DOWNLOAD_PATH` directory. +- On failure, no files should be placed in `ASDF_DOWNLOAD_PATH`. +- Success should exit with `0`. +- Failure should exit with a non-zero status. + +**Legacy Plugins** + +Though this script is marked as _required_ for all plugins, it is _optional_ for "legacy" plugins which predate its introduction. + +If this script is absent, asdf will assume that the `bin/install` script is present and will download **and** install the version. + +All plugins must include this script as support for legacy plugins will eventually be removed. + +**Environment Variables available to script** + +- `ASDF_INSTALL_TYPE`: `version` or `ref` +- `ASDF_INSTALL_VERSION`: + - Full version number if `ASDF_INSTALL_TYPE=version`. + - Git ref (tag/commit/branch) if `ASDF_INSTALL_TYPE=ref`. +- `ASDF_INSTALL_PATH`: The path to where the tool _has been_, or _should be_ installed. +- `ASDF_DOWNLOAD_PATH`: The path to where the source code or binary was downloaded to. + +**Commands that invoke this script** + +- `asdf install [version]` +- `asdf install latest[:version]` +- `asdf install nodejs 18.0.0`: downloads the source code or binary for Node.js + version `18.0.0` and places it in the `ASDF_DOWNLOAD_PATH` directory. Then runs the `bin/install` script. + +**Call signature from asdf core** + +No parameters provided. + +```bash:no-line-numbers +"${plugin_path}"/bin/download +``` + +--- + +### `bin/install` + +**Description** + +Install a specific version of a tool to a specified location. + +**Implementation Details** + +- The script should install the specified version in the path `ASDF_INSTALL_PATH`. +- Shims will be created by default for any files in `$ASDF_INSTALL_PATH/bin`. This behaviour can be customised with the optional +[bin/list-bin-paths](#binlist-bin-paths) script. +- Success should exit with `0`. +- Failure should exit with a non-zero status. +- To avoid TOCTOU (Time-of-Check-to-Time-of-Use) issues, ensure the script only places files in `ASDF_INSTALL_PATH` once the build and installation of the tool is deemed a success. + +**Legacy Plugins** + +If the `bin/download` script is absent, this script should download **and** install the specified version. + +For compatibility with versions of the asdf core earlier than `0.7._` and newer than `0.8._`, check for the presence of the `ASDF_DOWNLOAD_PATH` environment +variable. If set, assume the `bin/download` script already downloaded the version, else download the source code in the `bin/install` script. + +**Environment Variables available to script** + +- `ASDF_INSTALL_TYPE`: `version` or `ref` +- `ASDF_INSTALL_VERSION`: + - Full version number if `ASDF_INSTALL_TYPE=version`. + - Git ref (tag/commit/branch) if `ASDF_INSTALL_TYPE=ref`. +- `ASDF_INSTALL_PATH`: The path to where the tool _has been_, or _should be_ installed. +- `ASDF_CONCURRENCY`: The number of cores to use when compiling source code. Useful for setting flags like `make -j`. +- `ASDF_DOWNLOAD_PATH`: The path where the source code or binary was downloaded to. + +**Commands that invoke this script** + +- `asdf install` +- `asdf install ` +- `asdf install [version]` +- `asdf install latest[:version]` +- `asdf install nodejs 18.0.0`: installs Node.js version `18.0.0` in the + `ASDF_INSTALL_PATH` directory. + +**Call signature from asdf core** + +No parameters provided. + +```bash:no-line-numbers +"${plugin_path}"/bin/install +``` ## Optional Scripts -#### bin/help scripts +### `bin/latest-stable` -This is not one callback script but rather a set of callback scripts that each print different documentation to STDOUT. The possible callback scripts are listed below. Note that `bin/help.overview` is a special case as it must be present for any help output to be displayed for the script. +**Description** -- `bin/help.overview` - This script should output a general description about the plugin and the tool being managed. No heading should be printed as asdf will print headings. Output may be free-form text but ideally only one short paragraph. This script must be present if you want asdf to provide help information for your plugin. All other help callback scripts are optional. -- `bin/help.deps` - This script should output the list of dependencies tailored to the operating system. One dependency per line. -- `bin/help.config` - This script should print any required or optional configuration that may be available for the plugin and tool. Any environment variables or other flags needed to install or compile the tool (for the users operating system when possible). Output can be free-form text. -- `bin/help.links` - This should be a list of links relevant to the plugin and tool (again, tailored to the current operating system when possible). One link per line. Lines may be in the format `: <link>` or just `<link>`. +Determine the latest stable version of a tool. If absent, the asdf core will `tail` the `bin/list-all` output which may be undesirable. -Each of these scripts should tailor their output to the current operating system. For example, when on Ubuntu the deps script could output the dependencies as apt-get packages that must be installed. The script should also tailor its output to the value of `ASDF_INSTALL_VERSION` and `ASDF_INSTALL_TYPE` when the variables are set. They are optional and will not always be set. +**Implementation Details** -The help callback script MUST NOT output any information that is already covered in the core asdf-vm documentation. General asdf usage information must not be present. +- The script should print the latest stable version of the tool to stdout. +- Non-stable or release candidate versions should be omitted. +- A filter query is provided as the first argument to the script. This should be used to filter the output by version number or tool provider. + - For instance, the output of `asdf list all ruby` from the [ruby plugin](https://github.com/asdf-vm/asdf-ruby) lists versions of Ruby from many providers: `jruby`, `rbx` & `truffleruby` amongst others. The user provided filter could be used by the plugin to filter the semver versions and/or provider. + ``` + > asdf latest ruby + 3.2.2 + > asdf latest ruby 2 + 2.7.8 + > asdf latest ruby truffleruby + truffleruby+graalvm-22.3.1 + ``` +- Success should exit with `0`. +- Failure should exit with a non-zero status. -#### bin/latest-stable +**Environment Variables available to script** -If this callback is implemented asdf will use it to determine the latest stable version of your tool instead of trying deduce it for you on its own. `asdf latest` deduces the latest version by looking at the last version printed by the `list-all` callback, after a few types of versions (like release candidate versions) are excluded from the output. This default behavior is undesirable when your plugin's `list-all` callback prints different variations of the same tool and the last version isn't the latest stable version of the variation you'd like to default to. For example with Ruby the latest stable version should be the regular implementation of Ruby (MRI), but truffleruby versions are printed last by the `list-all` callback. +- `ASDF_INSTALL_TYPE`: `version` or `ref` +- `ASDF_INSTALL_VERSION`: + - Full version number if `ASDF_INSTALL_TYPE=version`. + - Git ref (tag/commit/branch) if `ASDF_INSTALL_TYPE=ref`. +- `ASDF_INSTALL_PATH`: The path to where the tool _has been_, or _should be_ installed. -This callback is invoked with a single "filter" string as its only argument. This should be used for filter all latest stable versions. For example with Ruby, the user may choose to pass in `jruby` to select the latest stable version of `jruby`. +**Commands that invoke this script** -#### bin/list-bin-paths +- `asdf global <tool> latest`: set the global version of a tool to the latest stable version for that tool. +- `asdf local <name> latest`: set the local version of a tool to the latest stable version for that tool. +- `asdf install <tool> latest`: installs the latest version of a tool. +- `asdf latest <tool> [<version>]`: outputs the latest version of a tool based on the optional filter. +- `asdf latest --all`: outputs the latest version of all tools managed by asdf and whether they are installed. -List executables for the specified version of the tool. Must print a string with a space-separated list of dir paths that contain executables. The paths must be relative to the install path passed. Example output would be: +**Call signature from asdf core** -```shell +The script should accept a single argument, the filter query. + +```bash:no-line-numbers +"${plugin_path}"/bin/latest-stable "" +"${plugin_path}"/bin/latest-stable "$query" +``` + +--- + +### `bin/help.overview` + +**Description** + +Output a general description about the plugin and the tool being managed. + +**Implementation Details** + +- This script is required for any help output to be displayed for the plugin. +- No heading should be printed as asdf core will print headings. +- Output may be free-form text but ideally only one short paragraph. +- Must not output any information that is already covered in the core asdf-vm documentation. +- Should be tailored to the Operating System and version of the tool being installed (using optionally set Environment Variables `ASDF_INSTALL_VERSION` and `ASDF_INSTALL_TYPE`). +- Success should exit with `0`. +- Failure should exit with a non-zero status. + +**Environment Variables available to script** + +- `ASDF_INSTALL_TYPE`: `version` or `ref` +- `ASDF_INSTALL_VERSION`: + - Full version number if `ASDF_INSTALL_TYPE=version`. + - Git ref (tag/commit/branch) if `ASDF_INSTALL_TYPE=ref`. +- `ASDF_INSTALL_PATH`: The path to where the tool _has been_, or _should be_ installed. + +**Commands that invoke this script** + +- `asdf help <name> [<version>]`: Output documentation for plugin and tool + +**Call signature from asdf core** + +```bash:no-line-numbers +"${plugin_path}"/bin/help.overview +``` + +--- + +### `bin/help.deps` + +**Description** + +Output the list of dependencies tailored to the operating system. One dependency per line. + +```bash:no-line-numbers +git +curl +sed +``` + +**Implementation Details** + +- This script requires `bin/help.overview` for its output to be considered. +- Should be tailored to the Operating System and version of the tool being installed (using optionally set Environment Variables `ASDF_INSTALL_VERSION` and `ASDF_INSTALL_TYPE`). +- Success should exit with `0`. +- Failure should exit with a non-zero status. + +**Environment Variables available to script** + +- `ASDF_INSTALL_TYPE`: `version` or `ref` +- `ASDF_INSTALL_VERSION`: + - Full version number if `ASDF_INSTALL_TYPE=version`. + - Git ref (tag/commit/branch) if `ASDF_INSTALL_TYPE=ref`. +- `ASDF_INSTALL_PATH`: The path to where the tool _has been_, or _should be_ installed. + +**Commands that invoke this script** + +- `asdf help <name> [<version>]`: Output documentation for plugin and tool + +**Call signature from asdf core** + +```bash:no-line-numbers +"${plugin_path}"/bin/help.deps +``` + +--- + +### `bin/help.config` + +**Description** + +Output any required or optional configuration for the plugin and tool. For example, describe any environment variables or other flags needed to install or compile the tool. + +**Implementation Details** + +- This script requires `bin/help.overview` for its output to be considered. +- Output can be free-form text. +- Should be tailored to the Operating System and version of the tool being installed (using optionally set Environment Variables `ASDF_INSTALL_VERSION` and `ASDF_INSTALL_TYPE`). +- Success should exit with `0`. +- Failure should exit with a non-zero status. + +**Environment Variables available to script** + +- `ASDF_INSTALL_TYPE`: `version` or `ref` +- `ASDF_INSTALL_VERSION`: + - Full version number if `ASDF_INSTALL_TYPE=version`. + - Git ref (tag/commit/branch) if `ASDF_INSTALL_TYPE=ref`. +- `ASDF_INSTALL_PATH`: The path to where the tool _has been_, or _should be_ installed. + +**Commands that invoke this script** + +- `asdf help <name> [<version>]`: Output documentation for plugin and tool + +**Call signature from asdf core** + +```bash:no-line-numbers +"${plugin_path}"/bin/help.config +``` + +--- + +### `bin/help.links` + +**Description** + +Output a list of links relevant to the plugin and tool. One link per line. + +```bash:no-line-numbers +Git Repository: https://github.com/vlang/v +Documentation: https://vlang.io +``` + +**Implementation Details** + +- This script requires `bin/help.overview` for its output to be considered. +- One link per line. +- Format must be either: + - `<title>: <link>` + - or just `<link>` +- Should be tailored to the Operating System and version of the tool being installed (using optionally set Environment Variables `ASDF_INSTALL_VERSION` and `ASDF_INSTALL_TYPE`). +- Success should exit with `0`. +- Failure should exit with a non-zero status. + +**Environment Variables available to script** + +- `ASDF_INSTALL_TYPE`: `version` or `ref` +- `ASDF_INSTALL_VERSION`: + - Full version number if `ASDF_INSTALL_TYPE=version`. + - Git ref (tag/commit/branch) if `ASDF_INSTALL_TYPE=ref`. +- `ASDF_INSTALL_PATH`: The path to where the tool _has been_, or _should be_ installed. + +**Commands that invoke this script** + +- `asdf help <name> [<version>]`: Output documentation for plugin and tool + +**Call signature from asdf core** + +```bash:no-line-numbers +"${plugin_path}"/bin/help.links +``` + +--- + +### `bin/list-bin-paths` + +**Description** + +List directories containing executables for the specified version of the tool. + +**Implementation Details** + +- If this script is not present, asdf will look for binaries in the `"${ASDF_INSTALL_PATH}"/bin` directory & create shims for those. +- Output a space-separated list of paths containing executables. +- Paths must be relative to `ASDF_INSTALL_PATH`. Example output would be: + +```bash:no-line-numbers bin tools veggies ``` -This will instruct asdf to create shims for the files in `<install-path>/bin`, `<install-path>/tools` and `<install-path>/veggies` +This will instruct asdf to create shims for the files in: +- `"${ASDF_INSTALL_PATH}"/bin` +- `"${ASDF_INSTALL_PATH}"/tools` +- `"${ASDF_INSTALL_PATH}"/veggies` -If this script is not specified, asdf will look for the `bin` dir in an installation and create shims for those. +**Environment Variables available to script** -#### bin/exec-env +- `ASDF_INSTALL_TYPE`: `version` or `ref` +- `ASDF_INSTALL_VERSION`: + - Full version number if `ASDF_INSTALL_TYPE=version`. + - Git ref (tag/commit/branch) if `ASDF_INSTALL_TYPE=ref`. +- `ASDF_INSTALL_PATH`: The path to where the tool _has been_, or _should be_ installed. -Setup the env to run the binaries in the package. +**Commands that invoke this script** -#### bin/exec-path +- `asdf install <tool> [version]`: initially create shims for binaries. +- `asdf reshim <tool> <version>`: recreate shims for binaries. -Get the executable path for the specified version of the tool. Must print a string with the relative executable path. This allows the plugin to conditionally override the shim's specified executable path, otherwise return the default path specified by the shim. +**Call signature from asdf core** + +```bash:no-line-numbers +"${plugin_path}/bin/list-bin-paths" +``` + +--- + +### `bin/exec-env` + +**Description** + +Prepare the environment before executing the shims for the binaries for the tool. + +**Environment Variables available to script** + +- `ASDF_INSTALL_TYPE`: `version` or `ref` +- `ASDF_INSTALL_VERSION`: + - Full version number if `ASDF_INSTALL_TYPE=version`. + - Git ref (tag/commit/branch) if `ASDF_INSTALL_TYPE=ref`. +- `ASDF_INSTALL_PATH`: The path to where the tool _has been_, or _should be_ installed. + +**Commands that invoke this script** + +- `asdf which <command>`: Display the path to an executable +- `asdf exec <command> [args...]`: Executes the command shim for current version +- `asdf env <command> [util]`: Runs util (default: `env`) inside the environment used for command shim execution. + +**Call signature from asdf core** + +```bash:no-line-numbers +"${plugin_path}/bin/exec-env" +``` + +--- + +### `bin/exec-path` + +Get the executable path for the specified version of the tool. Must print a +string with the relative executable path. This allows the plugin to +conditionally override the shim's specified executable path, otherwise return +the default path specified by the shim. + +**Description** + +Get the executable path for the specified version of the tool. + +**Implementation Details** + +- Must print a string with the relative executable path. +- Conditionally override the shim's specified executable path, otherwise return the default path specified by the shim. ```shell Usage: @@ -111,71 +532,222 @@ Output: bin/foox ``` -#### bin/uninstall +**Environment Variables available to script** -Uninstalls a specific version of a tool. +- `ASDF_INSTALL_TYPE`: `version` or `ref` +- `ASDF_INSTALL_VERSION`: + - Full version number if `ASDF_INSTALL_TYPE=version`. + - Git ref (tag/commit/branch) if `ASDF_INSTALL_TYPE=ref`. +- `ASDF_INSTALL_PATH`: The path to where the tool _has been_, or _should be_ installed. -#### bin/list-legacy-filenames +**Commands that invoke this script** -Register additional setter files for this plugin. Must print a string with a space-separated list of filenames. +- `asdf which <command>`: Display the path to an executable +- `asdf exec <command> [args...]`: Executes the command shim for current version +- `asdf env <command> [util]`: Runs util (default: `env`) inside the environment used for command shim execution. -```shell -.ruby-version .rvmrc +**Call signature from asdf core** + +```bash:no-line-numbers +"${plugin_path}/bin/exec-path" "$install_path" "$cmd" "$relative_path" ``` -Note: This will only apply for users who have enabled the `legacy_version_file` option in their `~/.asdfrc`. +--- -#### bin/parse-legacy-file +### `bin/uninstall` -This can be used to further parse the legacy file found by asdf. If `parse-legacy-file` isn't implemented, asdf will simply cat the file to determine the version. The script will be passed the file path as its first argument. Note that this script should be **deterministic** and always return the same exact version when parsing the same legacy file. The script should return the same version regardless of what is installed on the machine or whether the legacy version is valid or complete. Some legacy file formats may not be suitable. +**Description** -#### bin/post-plugin-add +Uninstall the provided version of a tool. -This can be used to run any post-installation actions after the plugin has been added to asdf. +**Output Format** -The script has access to the path the plugin was installed (`${ASDF_PLUGIN_PATH}`) and the source URL (`${ASDF_PLUGIN_SOURCE_URL}`), if any was used. +Output should be sent to `stdout` or `stderr` as appropriate for the user. No output is read by subsequent execution in the core. -See also the related hooks: +**Environment Variables available to script** + +No environment variables are provided to this script. + +**Commands that invoke this script** + +- `asdf list all <name> <version>` +- `asdf uninstall nodejs 18.15.0`: Uninstalls the version `18.15.0` of nodejs, removing all shims including those installed global with `npm i -g` + +**Call signature from asdf core** + +No parameters provided. + +```bash:no-line-numbers +"${plugin_path}/bin/uninstall" +``` + +--- + +### `bin/list-legacy-filenames` + +**Description** + +List legacy configuration filenames for determining the specified version of the tool. + +**Implementation Details** + +- Output a space-separated list of filenames. + ```bash:no-line-numbers + .ruby-version .rvmrc + ``` +- Only applies for users who have enabled the `legacy_version_file` option in their `"${HOME}"/.asdfrc`. + +**Environment Variables available to script** + +- `ASDF_INSTALL_TYPE`: `version` or `ref` +- `ASDF_INSTALL_VERSION`: + - Full version number if `ASDF_INSTALL_TYPE=version`. + - Git ref (tag/commit/branch) if `ASDF_INSTALL_TYPE=ref`. +- `ASDF_INSTALL_PATH`: The path to where the tool _has been_, or _should be_ installed. + +**Commands that invoke this script** + +Any command which reads a tool version. + +**Call signature from asdf core** + +No parameters provided. + +```bash:no-line-numbers +"${plugin_path}/bin/list-legacy-filenames" +``` + +--- + +### `bin/parse-legacy-file` + +**Description** + +Parse the legacy file found by asdf to determine the version of the tool. Useful to extract version numbers from files like JavaScript's `package.json` or Golangs `go.mod`. + +**Implementation Details** + +- If not present, asdf will simply `cat` the legacy file to determine the version. +- Should be **deterministic** and always return the same exact version: + - when parsing the same legacy file. + - regardless of what is installed on the machine or whether the legacy version is valid or complete. Some legacy file formats may not be suitable. +- Output a single line with the version: + ```bash:no-line-numbers + 1.2.3 + ``` + +**Environment Variables available to script** + +No environment variables specifically set before this script is called. + +**Commands that invoke this script** + +Any command which reads a tool version. + +**Call signature from asdf core** + +The script should accept a single argument, the path to the legacy file for reading its contents. + +```bash:no-line-numbers +"${plugin_path}/bin/parse-legacy-file" "$file_path" +``` + +--- + +### `bin/post-plugin-add` + +**Description** + +Execute this callback script **after** the plugin has been _added_ to asdf with `asdf plugin add <tool>`. + +See also the related command hooks: - `pre_asdf_plugin_add` - `pre_asdf_plugin_add_${plugin_name}` - `post_asdf_plugin_add` - `post_asdf_plugin_add_${plugin_name}` -#### bin/post-plugin-update +**Environment Variables available to script** -This can be used to run any post-plugin-update actions after asdf has downloaded the updated plugin +- `ASDF_PLUGIN_PATH`: path where the plugin was installed. +- `ASDF_PLUGIN_SOURCE_URL`: URL of the plugin source. Can be a local directory path. -The script has access to the path the plugin was installed (`${ASDF_PLUGIN_PATH}`), previous git-ref (`${ASDF_PLUGIN_PREV_REF}`), and updated git-ref (`${ASDF_PLUGIN_POST_REF}`). +**Call signature from asdf core** -See also the related hooks: +No parameters provided. + +```bash:no-line-numbers +"${plugin_path}/bin/post-plugin-add" +``` + +--- + +### `bin/post-plugin-update` + +**Description** + +Execute this callback script **after** asdf has downloaded the _updated_ plugin with `asdf plugin update <tool> [<git-ref>]`. + +See also the related command hooks: - `pre_asdf_plugin_updated` - `pre_asdf_plugin_updated_${plugin_name}` - `post_asdf_plugin_updated` - `post_asdf_plugin_updated_${plugin_name}` -#### bin/pre-plugin-remove +**Environment Variables available to script** -This can be used to run any pre-removal actions before the plugin will be removed from asdf. +- `ASDF_PLUGIN_PATH`: path where the plugin was installed. +- `ASDF_PLUGIN_PREV_REF`: the plugin's previous git-ref +- `ASDF_PLUGIN_POST_REF`: the plugin's updated git-ref -The script has access to the path the plugin was installed in (`${ASDF_PLUGIN_PATH}`). +**Call signature from asdf core** -See also the related hooks: +No parameters provided. + +```bash:no-line-numbers +"${plugin_path}/bin/post-plugin-update" +``` + +--- + +### `bin/pre-plugin-remove` + +**Description** + +Execute this callback script **before** asdf has removed the plugin with `asdf plugin remove <tool>`. + +See also the related command hooks: - `pre_asdf_plugin_remove` - `pre_asdf_plugin_remove_${plugin_name}` - `post_asdf_plugin_remove` - `post_asdf_plugin_remove_${plugin_name}` -## Extension commands for asdf CLI. +**Environment Variables available to script** -It's possible for plugins to define new asdf commands by providing `lib/commands/command*.bash` scripts or executables that -will be callable using the asdf command line interface by using the plugin name as a subcommand. +- `ASDF_PLUGIN_PATH`: path where the plugin was installed. + +**Call signature from asdf core** + +No parameters provided. + +```bash:no-line-numbers +"${plugin_path}/bin/pre-plugin-remove" +``` + +<!-- TODO: document command hooks --> +<!-- ## Command Hooks --> + +## Extension Commands for asdf CLI <Badge type="danger" text="advanced" vertical="middle" /> + +It's possible for plugins to define new asdf commands by providing +`lib/commands/command*.bash` scripts or executables that will be callable using +the asdf command line interface by using the plugin name as a subcommand. For example, suppose a `foo` plugin has: -```shell +```shell:no-line-numbers foo/ lib/commands/ command.bash @@ -184,9 +756,9 @@ foo/ command-help.bash ``` -Users can now execute +Users can now execute: -```shell +```shell:no-line-numbers $ asdf foo # same as running `$ASDF_DATA_DIR/plugins/foo/lib/commands/command.bash` $ asdf foo bar # same as running `$ASDF_DATA_DIR/plugins/foo/lib/commands/command.bash bar` $ asdf foo help # same as running `$ASDF_DATA_DIR/plugins/foo/lib/commands/command-help.bash` @@ -195,69 +767,106 @@ $ asdf foo bat baz # same as running `$ASDF_DATA_DIR/plugins/foo/lib/commands/co ``` Plugin authors can use this feature to provide utilities related to their tools, -or even create plugins that are just new command extensions for asdf itself. +or even create plugins that are just new command extensions of asdf itself. -When invoked, if extension commands do not have their executable-bit set they will be -sourced as bash scripts. Also, the `$ASDF_CMD_FILE` resolves to the full path of the file being sourced. -If the executable bit is set, they are just executed and replace the asdf execution. +If the executable bit is set, the script is executed, replacing the asdf +execution. -A good example of this feature is for plugins like [`haxe`](https://github.com/asdf-community/asdf-haxe) -which provides the `asdf haxe neko-dylibs-link` to fix an issue where haxe executables expect to find -dynamic libraries relative to the executable directory. +If the executable bit is not set, asdf will source the scripts as Bash scripts. -If your plugin provides an asdf extension command, be sure to mention it in your plugin's README. +`$ASDF_CMD_FILE` resolves to the full path of the file being sourced. -## Custom shim templates +[`haxe`](https://github.com/asdf-community/asdf-haxe) is a great example of a +plugin which uses this feature. It provides the `asdf haxe neko-dylibs-link` to +fix an issue where Haxe executables expect to find dynamic libraries relative to +the executable directory. -**PLEASE use this feature only if absolutely required** +Be sure to list your asdf Extension Commands in your plugins README. -asdf allows custom shim templates. For an executable called `foo`, if there's a `shims/foo` file in the plugin, then asdf will copy that file instead of using its standard shim template. +## Custom Shim Templates <Badge type="danger" text="advanced" vertical="middle" /> -This must be used wisely. For now AFAIK, it's only being used in the Elixir plugin, because an executable is also read as an Elixir file apart from just being an executable. Which makes it not possible to use the standard bash shim. +::: warning -## Testing plugins +Please only use if **absolutely** required -`asdf` contains the `plugin-test` command to test your plugin. You can use it as follows +::: -```shell -asdf plugin test <plugin-name> <plugin-url> [--asdf-tool-version <version>] [--asdf-plugin-gitref <git-ref>] [test-command*] +asdf allows custom shim templates. For an executable called `foo`, if there's a +`shims/foo` file in the plugin, then asdf will copy that file instead of using +its standard shim template. + +**This must be used wisely.** + +As far as the asdf core team is aware, this feature is only in use in the +first-party [Elixir plugin](https://github.com/asdf-vm/asdf-elixir). This is +because an executable is also read as an Elixir file in addition to being an +executable. This makes it not possible to use the standard Bash shim. + +## Testing + +`asdf` contains the `plugin-test` command to test your plugin: + +```shell:no-line-numbers +asdf plugin test <plugin_name> <plugin_url> [--asdf-tool-version <version>] [--asdf-plugin-gitref <git_ref>] [test_command...] ``` -Only the two first arguments are required. -If \__version_ is specified, the tool will be installed with that specific version. Defaults to whatever returns `asdf latest <plugin-name>`. -If _git-ref_ is specified, the plugin itself is checked out at that commit/branch/tag, useful for testing a pull-request on your plugin's CI. +- `<plugin_name>` & `<plugin_url>` are required +- If optional `[--asdf-tool-version <version>]` is specified, the tool will be + installed with that specific version. Defaults to `asdf latest <plugin-name>` +- If optional `[--asdf-plugin-gitref <git_ref>]` is specified, the plugin itself + is checked out at that commit/branch/tag. This is useful for testing a + pull-request on your plugin's CI. +- Optional parameter `[test_command...]` is the command to execute to validate + the installed tool works correctly. Typically `<tool> --version` or + `<tool> --help`. For example, to test the NodeJS plugin, we could run + ```shell:no-line-numbers + # asdf plugin test <plugin_name> <plugin_url> [test_command] + asdf plugin test nodejs https://github.com/asdf-vm/asdf-nodejs.git node --version + ``` -Rest arguments are considered the command to execute to ensure the installed tool works correctly. -Normally it would be something that takes `--version` or `--help`. -For example, to test the NodeJS plugin, we could run +::: tip Note -```shell -asdf plugin test nodejs https://github.com/asdf-vm/asdf-nodejs.git node --version -``` +We recommend testing in both Linux & macOS CI environments -We strongly recommend you test your plugin on a CI environment and make sure it works on both Linux and OSX. +::: -#### Example GitHub Action +### GitHub Action -The [asdf-vm/actions](https://github.com/asdf-vm/actions) repo provides a GitHub Action for testing your plugins hosted on github. +The [asdf-vm/actions](https://github.com/asdf-vm/actions) repo provides a GitHub +Action for testing your plugins hosted on GitHub. A sample +`.github/workflows/test.yaml` Actions Workflow: ```yaml -steps: - - name: asdf_plugin_test - uses: asdf-vm/actions/plugin-test@v1 - with: - command: "my_tool --version" - env: - GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} # automatically provided +name: Test +on: + push: + branches: + - main + pull_request: + +jobs: + plugin_test: + name: asdf plugin test + strategy: + matrix: + os: + - ubuntu-latest + - macos-latest + runs-on: ${{ matrix.os }} + steps: + - name: asdf_plugin_test + uses: asdf-vm/actions/plugin-test@v2 + with: + command: "<MY_TOOL> --version" ``` -#### Example TravisCI config +### TravisCI Config -Here is a sample `.travis.yml` file, customize it to your needs +A sample `.travis.yml` file, customize it to your needs ```yaml language: c -script: asdf plugin test nodejs $TRAVIS_BUILD_DIR 'node --version' +script: asdf plugin test <MY_TOOL> $TRAVIS_BUILD_DIR '<MY_TOOL> --version' before_script: - git clone https://github.com/asdf-vm/asdf.git asdf - . asdf/asdf.sh @@ -266,25 +875,26 @@ os: - osx ``` -Note: -When using another CI, you will need to check what variable maps to the repo path. +::: tip NOTE -You also have the option to pass a relative path to `plugin-test`. +When using another CI you may need to pass a relative path to the plugin +location: -For example, if the test script is ran in the repo directory: `asdf plugin test nodejs . 'node --version'`. +```shell:no-line-numbers +asdf plugin test <tool_name> <path> '<tool_command> --version' +``` -## GitHub API Rate Limiting +::: -If your plugin's `list-all` depends on accessing the GitHub API, make sure you provide an Authorization token when accessing it, otherwise your tests might fail due to rate limiting. +## API Rate Limiting -To do so, create a [new personal token](https://github.com/settings/tokens/new) with only `public_repo` access. - -Then on your travis.ci build settings add a _secure_ environment variable for it named something like `GITHUB_API_TOKEN`. And _DO NOT_ EVER publish your token in your code. - -Finally, add something like the following to `bin/list-all` +If a command depends on accessing an external API, like `bin/list-all` or +`bin/latest-stable`, it may experience rate limiting during automated testing. +To mitigate this, ensure there is a code-path which provides an authentication +token via an environment variable. For example: ```shell -cmd="curl -s" +cmd="curl --silent" if [ -n "$GITHUB_API_TOKEN" ]; then cmd="$cmd -H 'Authorization: token $GITHUB_API_TOKEN'" fi @@ -292,10 +902,37 @@ fi cmd="$cmd $releases_path" ``` -## Submitting plugins to the official plugins repository +### `GITHUB_API_TOKEN` -`asdf` can easily install plugins by specifying the plugin repository url, e.g. `plugin add my-plugin https://github.com/user/asdf-my-plugin.git`. +To utilise the `GITHUB_API_TOKEN`, create a +[new personal token](https://github.com/settings/tokens/new) with only +`public_repo` access. -To make it easier on your users, you can add your plugin to the official plugins repository to have your plugin listed and easily installable using a shorter command, e.g. `asdf plugin add my-plugin`. +Then add this to your CI pipeline environment variables. -Follow the instruction at the plugins repository: [asdf-vm/asdf-plugins](https://github.com/asdf-vm/asdf-plugins). +::: warning + +NEVER publish your authentication tokens in your code repository + +::: + +## Plugin Shortname Index + +::: tip + +The recommended installation method for a plugin is via direct URL installation: + +```shell:no-line-numbers +# asdf plugin add <name> <git_url> + asdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs +``` + +::: + +If the `git_url` is not provided, asdf will use the +[Shortname Index repository](https://github.com/asdf-vm/asdf-plugins) to +determine the exact `git_url` to use. + +You can add your plugin to the +[Shortname Index](https://github.com/asdf-vm/asdf-plugins) by following the +instructions in that repo.