August 29th, 2019 · Written by Nick Vernij
GitHub released their own CI recently, its UI and marketing look very fancy. I had to set up a new Elixir + Phoenix project and decided to give it a shot.
If you want to skip the reading, here is a working workflow for you: https://gist.github.com/Nickforall/e49f5f3c37414e05f9a6c604accf2c3e
It took me a full morning to get a green check because the default Elixir template is very minimal and the documentation for creating workflows is quite a lot of reading to get through.
There are some nooks and crannies to get through, but when you get it to work it’s very powerful. Essentially, the default Elixir workflow is fine for testing your average Elixir library however, a Phoenix app will need some additional setup and services. In my example, I needed to set-up a PostgreSQL service.
First of all, we want to set the MIX_ENV
environment
variable, so when we use Ecto, it knows it should setup the test
database. We can define global environment variables when defining the
container in which our tests will run.
container:
image: elixir:1.9.1-slim
env:
MIX_ENV: test
But to use Ecto we actually need a database. In the workflow definition, we can define a list of services. These are docker images that are prerequisites for our test steps to run.
This is what my services configuration looks like:
services:
postgres:
image: postgres
ports:
- 5432:5432
env:
POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 3
Let’s go through this service definition rule-by-rule:
image: postgres
This defines what Docker image to pull in and run, in my case I am using the official Docker image for Postgres: https://hub.docker.com/_/postgres
ports:
- 5432:5432
We always have to define which ports we want to expose for our Docker container. In this case, I am just exposing the default Postgres port.
env:
POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres
The Docker container we use has some environment variables defined, which it will use to configure the Postgres server. A full list of those variables can be found in the Postgres Docker Hub description.
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
Lastly, we have to pass some additional options. These options will
directly map to the docker create
command. The reason we
need to add this health check is because
docker create
exits before the container is actually ready
to be used.
Luckily for us, there is a simple command
pg_isready
available, which we can run on the container to
check the status of the instance. As soon as the Postgres container is
ready, Docker will exit and your test run will continue.
The default Elixir workflow should already have some
steps
defined. We are modifying the
Run Tests
step a bit so it will create a database and
passes some additional environment variables.
All services defined will create a virtual host on the network, named
after the service. This means you cannot simply reach the Postgres on
localhost
, but instead have to connect to
postgres
.
I have added an environment variable in my "run" step that exposes the name of the services.
- name: Run Tests
run: mix test
env:
DB_HOST: postgres
Additionally I modified my Mix test configuration in
config/test.exs
to read this variable and fall back on
localhost by default
# Configure your database
config :myapp, MyApp.Repo,
username: "postgres",
password: "postgres",
database: "myapp_test",
hostname: System.get_env("DB_HOST", "localhost"),
pool: Ecto.Adapters.SQL.Sandbox
By default phoenix apps should have an alias for
mix run test
that runs ecto.setup
,
ecto.migrate
and test
.
When pushing new commits to your repository, the workflow should trigger, do all kinds of magic and run this command for you.
You can find the full file in this gist: https://gist.github.com/Nickforall/e49f5f3c37414e05f9a6c604accf2c3e
If you have any questions feel free to hit me up on twitter