This page shows you how to write your own Buildkite plugin, and how to validate the
plugin.yml file which describes it against the plugin schema.
Tutorial: write a plugin
In this tutorial, we'll create a Buildkite plugin called "File Counter", which counts the number of files in the build directory once the command has finished, and creates a build annotation with the count.
Step 1: Create a new git repository
The most common kind of Buildkite plugin is a Git repository, with a descriptive name ending in
-buildkite-plugin. This suffix is required to allow using the
user/plugin-name syntax in pipelines. Let's create a new Git repository following these naming conventions:
mkdir file-counter-buildkite-plugin cd file-counter-buildkite-plugin git init
We recommend using the
-buildkite-plugin suffix in the repository name because:
user/plugin-namesyntax rather than the full URL.
Step 2: Add a plugin.yml
plugin.yml to describe how the plugin appears in the Buildkite plugins directory, what it requires, and what configuration options it accepts.
configuration property defines the validation rules for the plugin configuration using the JSON Schema format. The plugin in this tutorial has a single
pattern property, of type
Configuration properties are available to the hook script as environment variables with the naming pattern
<PLUGIN_NAME> is not the name defined in
plugin.yml but the repository or folder name compatible with Bash environment variables (in uppercase and only letters, numbers, and underscores). In this case, the configured value of
pattern will be available as
📘 Accessing properties on plugins referenced with Git URLs
Note that if you reference a plugin with a full URL ending in
.git and that plugin's name does not end with
-buildkite-plugin, variable names will include
_GIT as part of the plugin name. For example, the value of the configuration
https://github.com/my-org/my-plugin.git#v1.0.0 will be available as
Valid plugin.yml properties
|name||The name of the plugin, in Title Case.|
|description||A short sentence describing what the plugin does.|
|author||A URL to the plugin author (for example, website or GitHub profile).|
|requirements||An array of commands that are expected to exist in the agent's
|configuration||A JSON Schema describing the valid configuration options available.|
Step 3: Validate the plugin
The Buildkite Plugin Linter is an app that helps ensure your plugin is up-to-date and has all the files required to list it in the plugins directory. The app is available as a Docker image you can run on the command line, with a dedicated plugin, or with Docker Compose. We recommend you start by running the linter on the command line, and then include the dedicated plugin in the pipeline for your plugin.
Run on the command line
You can run the plugin linter with the following Docker command:
docker run -it --rm -v "$PWD:/plugin:ro" buildkite/plugin-linter --id a-github-user/file-counter
Run with the dedicated plugin
If your plugin has a Buildkite pipeline, you can add a step to lint it using the corresponding plugin:
Run with Docker Compose
If you want to run the linter using Docker Compose, you can add the following to a
You can then run the tests using the following command:
docker-compose run --rm lint
Step 4: Add a hook
Plugins can implement a number of plugin hooks. For this plugin, create a
post-command hook in a
mkdir hooks touch hooks/post-command chmod +x hooks/post-command
Step 5: Add a test
The next step is to test the
post-command hook using BATS, and the
buildkite/plugin-tester Docker image.
mkdir tests touch tests/post-command.bats chmod +x tests/post-command.bats
Create the following
To run the test, run the following Docker command:
docker run -it --rm -v "$PWD:/plugin:ro" buildkite/plugin-tester
✓ Creates an annotation with the file count 1 test, 0 failures
To make it easier to run this command, create a Docker Compose file:
You can now run the tests using the following command:
docker-compose run --rm tests
Step 6: Add a readme
Next, add a
README.md file to introduce the plugin to the world:
# File Counter Buildkite Plugin Annotates the build with a file count. ## Example Add the following to your `pipeline.yml`: ```yml steps: - command: ls plugins: - a-github-user/file-counter#v1.0.0: pattern: '*.md' ``` ## Configuration ### `pattern` (Required, string) The file name pattern, for example `*.ts`. Supports any pattern supported by [find -name](http://man7.org/linux/man-pages/man1/find.1.html). ## Developing To run the tests: ```shell docker-compose run --rm tests ``` ## Contributing 1. Fork the repo 2. Make the changes 3. Run the tests 4. Commit and push your changes 5. Send a pull request
Developing a plugin with a feature branch
When developing plugins, it is useful to have a quick feedback loop between making a change in your plugin code, and seeing the effects in a Buildkite pipeline. Let's say you're developing your feature on
my-org/plugin#dev-branch. By default, if a Buildkite agent sees that it needs the plugin
my-org/plugin#dev-branch, and it already has a checkout matching that, it will not pull any changes from the Git repository. But if you do want to see changes reflected immediately, set
One way to try this is to add the following step to the Buildkite pipeline where you're testing your plugin. Configuring
BUILDKITE_PLUGINS_ALWAYS_CLONE_FRESH on only one step means that other plugins, which are unlikely to be changing in the meantime, won't get unnecessarily cloned on every step invocation. You need agent version v3.37.0 or above to use
Publish to the Buildkite plugins directory
To add your plugin to the Buildkite plugins directory, publish your repository to a public GitHub repository and add the
buildkite-plugin repository topic tag. For full instructions, see the plugins directory documentation.
Designing plugins: single-command plugins versus library plugins
When writing plugins, there are two patterns you can choose from:
- A single-command plugin: a small, declarative plugin, which exposes a single command for use in your pipeline steps. Most plugins follow this pattern.
- A library plugin, or super-plugin: this plugin type assembles multiple commands into one plugin. Refer to the library example Buildkite plugin for an example of how to set up this type of plugin.
If you don't plan to share the plugin outside of one repository, you can use a vendored plugin. Vendored plugins sit alongside the rest of the repository code, and you include them with a relative path:
Vendored plugins run after non-vendored plugins and don't have access to all the same hooks. See the documentation about job lifecycle hooks to learn more.