Writing build scripts
One of the most common actions that Buildkite steps perform is running shell scripts. These scripts are checked in alongside your code and
The Buildkite Agent will run your scripts, capture and report the log output, and use the exit status to mark each job, as well as the overall build, as passed or failed.
The shell that runs your scripts in Buildkite is a clean Bash prompt with no settings. If you rely on anything from your
~/.bashrc files when you run scripts locally, you'll need to explicitly add the relevant items to your build scripts.
When writing Bash shell scripts there are a number of options you can set to help prevent unexpected errors:
||Exit script immediately if any command returns a non-zero exit status.|
||Exit script immediately if an undefined variable is used (for example,
||Ensure Bash pipelines (for example,
||Expand and print each command before executing. See Debugging your environment for more information.|
set command can be used to enable and disable options. For example,
set -u enables the
u option, and
set +u disables the
u option. You can also set multiple options at once, for example
set -ue enables both the
The following example enables the most commonly used options for build scripts:
#!/bin/bash set -euo pipefail run_tests
For a full list of options, see the Bash reference manual.
Note that while enabling the
u option is generally a good default to use for all build scripts, it can cause some tools like rvm to fail with “unbound variable” errors. If you encounter errors, you can either remove
u from the list of options, or run the tool causing the error wrapped in
set +u and
set -u to remove the option for only that command. For example:
set +u; rvm xxx; set -u.
Capturing exit status
Build scripts can sometimes contain commands that shouldn't affect the overall exit status. For example, take the following script:
#!/bin/bash # Note that we don't enable the 'e' option, which would cause the script to # immediately exit if 'run_tests' failed set -uo pipefail run_tests clean_up
Running this script will exit with the status returned by the final command,
clean_up. However, what we really care about is the exit status of the first command,
By using a variable to store the exit status of
run_tests, we can run additional commands while still returning the original exit status. For example:
#!/bin/bash # Note that we don't enable the 'e' option, which would cause the script to # immediately exit if 'run_tests' failed set -uo pipefail # Run the main command we're most interested in run_tests # Capture the exit status TESTS_EXIT_STATUS=$? # Run additional commands clean_up # Exit with the status of the original command exit $TESTS_EXIT_STATUS
Using this technique gives you control over the exit code of your script, and the final success or failure of your build job.
Debugging your environment
The first step in debugging your build script is to view the environment variables from the Buildkite web interface:
There may be additional environment variables available in your build job that
don't appear in this list, such as ones set by your
job lifecycle hooks.
To debug these, you can print them using
echo $SOME_VAR before the command
you're wanting to run. For example:
#!/bin/bash echo "$PATH" some_command
If you require more environment information, you can execute
env to print out all the environment variable names and their values. If you use
env you should filter the output using a tool such as
egrep, to ensure you don't leak private keys or other information.
If you use environment variables to define sensitive data such as API keys or Secret Access Keys, you should always filter the output of
env to ensure you're not exposing any secrets in your build log.
For example, the following prints all environment variable names and values containing the words "git" or "node", using a case-insensitive search:
#!/bin/bash env | grep -i -E 'git|node' some_command
Enabling Bash's debug mode using
set -x can also help to debug your build scripts. This debug output can be very noisy, so it's best enable this before the command you want to debug, and then to disable it straight after. For example:
#!/bin/bash set -x # Enable debugging some_command set +x # Disable debugging some_other_command
For more information about the
x option and debugging in general, see the Bash Guide for Beginners' page on debugging Bash scripts.
Help with linting and debugging
To check your shell scripts for common errors and mistakes we highly recommend using a linting tool like Shellcheck. Shellcheck is a shell script linter with a web-based front-end, a command line tool, and integrates directly with most code editors.
For an explanation of a shell code snippet, the explainshell.com tool is extremely useful. Explainshell can tell you, in plain English, what a line of shell code does. It also integrates the man pages of common tools such as
Managing log output
If your script is generating output that is too large, there are several strategies you can employ to reduce the output or redirect the log. Take a look at our guide to managing log output guide for a step by step introduction.