GitHub Actions for Elixir Phoenix

I’ve been reviewing a few things recently around how we are not only using Elixir at Sauce, but also how we are deploying and hosting it. I’ll be adding in some more Elixir posts in the future, but today I just want to focus on GitHub Actions.

Normally we build, deploy etc via CircleCI, however GitHub Actions gives us a useful alternative all under the one roof of GitHub.

As part of my investigation, I’ve taken an existing project using CircleCI and tried to replicate as closely as possible into GitHub Actions.

This is a work in progress, so definitely don’t take this as gospel. You should definitely read through the GitHub Actions Documentation to get a better understanding.

The Outcome

What I wanted to achieve with this investigation is work out how to have a GitHub action that would be triggered on a Git push that would do the following overall tasks:

  • Setup Elixir and Erlang
  • Run static code analysis (Credo, Sobelow, Dialyzer)
  • Run Tests
  • Deploy to a hosting service, if the previous 2 steps have completed successfully

GitHub Flow image

GitHub Actions Setup

To use GitHub actions, you should create the following file structure

.github/workflows/action_name.yml

GitHub will pick that action up when you next push up to the repository.

GitHub actions need an environment to get up and running, that environment should match the environment you are deploying to, so the same Linux, Erlang and Elixir versions.

Events

The first thing to consider is which events should trigger your GitHub Action. To see the full list of options, read the documentation.

For the purposes of this #til, we’ll just use push and pull_request.

Notice how we can control which branches the events are tied to also. It’s important to consider this carefully, as it will have billing implications. Each running of an Action costs, so you should control when you want those Actions running.

Setup Elixir and Erlang

Before running any Mix commands, we need to get Erlang and Elixir setup. These should match what you are using in the project, and in your hosting environment.

For a full list of Ubuntu versions available in GitHub Actions, there is an up to date list here.

In a perfect world all these dependencies should be cached, so any further steps in this job run can use the cached versions, rather than having to get them again. I’ll add in caching on the final full version.

Static Code Analysis

It’s good to run tools such a Dialyzer, Credo and Sobelow on our Elixir projects, to route out any issues with the code before deployment.

For this, we’ll set up a second step that will be run after the Setup step.

Note the needs on line 5

Run Tests

As with all projects, we should run tests regularly. We’ll need to add in a Postgres instance here, so the tests can run against that database. Again, spot the usage of needs so this step relies on the original dependency setup step.

Deployment

For this deployment I’ve used Render, so the deployment steps are easy, but GitHub Actions can be used to deploy anywhere we wish.

The key things to spot at the use of needs for both of the previous steps, and also the dynamic creation of env vars. The env vars are tied to encrypted secrets that are stored in the GitHub project.

Improvements

As mentioned at the previously, this is only meant as a guide to get you started on GitHub Actions, there are definitely improvements to be made here, including:

  • Don’t follow the deploy step on every single branch push
  • Deploy branches to their own environments
  • Caching of dependencies

Below is a full example of the Action I setup. The example includes:

  • Canceling previous running actions
  • Dependency Caching
  • Dialyzer PLT caching

John Polling

Developer, tinkerer, occasionally useful