Run Your CI Steps Locally

Tags

Adjacent traffic lights with one red and one green

CI just failed your pull request again. You ran the tests locally, and they passed. What’s the problem?

After clicking through the logs, you see it: you forgot to format the code. Code formatting is one of the checks specified in your CI’s .yml configuration file, and it’s one you often forget. There’s also that static security check and the opinionated style linter.

Sighing, you go through the list, running each command locally and fixing the issues. Wouldn’t it be nice if you could run your CI locally and see the failures before making a PR?

Write a script

While it doesn’t fully replicate the CI environment, a simple solution to this is to write a shell script which runs all the automated checks a PR could need.

First, start by creating a script called ./run_ci.

$ touch run_ci
$ chmod a+x run_ci

Next, find all the commands your CI is configured to run and update ./run_ci to run them.

Finally, remove those steps from the CI configuration and have it ./run_ci instead.

Now, before you make a PR, you can ./run_ci locally to ensure you checked everything that CI will check.

What /.run_ci looks like

Here’s a generic template that your script could follow, which applies to any programming language:

#!/bin/bash
# Script to be run in CI and also locally, to simulate what CI will do, as a
# pre-PR final check

# Ensure that any failing command will exit this script
set -e

export CODE_ENV=test

# Run this first to fail fast, before bothering to install dependencies
./check_code_formatting

# If actually running in CI (most CI servers set this ENV var)
if [ "$CI" == "true" ]
then
  ./install_dependencies
  ./setup_database
else
  echo "Skipping CI setup; not needed in local dev environments"
fi

# Other checks, from fastest to slowest
./run_linter
./run_tests
./run_static_security_analysis

On an Elixir Phoenix project, the specifics might be something like this:

#!/bin/bash
# Script to be run in CI and also locally, to simulate what CI will do, as a
# pre-PR final check

# Ensure that any failing command will exit this script
set -e

export MIX_ENV=test

# Run this first to fail fast
echo "Checking formatting"
mix format --check-formatted

# If actually running in CI
if [ "$CI" == "true" ]
then
  mix local.hex --force
  mix local.rebar --force
  mix deps.get
  mix ecto.create
  mix ecto.migrate
else
  echo "Skipping CI setup"
fi

# Other checks, from fastest to slowest
echo "Linting"
mix lint
echo "Security check"
mix sobelow
echo "Checking for warnings"
mix compile --force --warnings-as-errors
echo "Running tests and checking coverage"
mix test --cover

Voila! Add this to your workflow and you’ll have a lot fewer surprises when opening PRs.

DockYard is a digital product agency offering custom software, mobile, and web application development consulting. We provide exceptional professional services in strategy, user experience, design, and full stack engineering using Ember.js, React.js, Ruby, and Elixir. With a nationwide staff, we’ve got consultants in key markets across the United States, including San Francisco, Los Angeles, Denver, Chicago, Austin, New York, and Boston.