Elixir Phoenix Continuous Integration#

This guide shows you how to use Semaphore to set up a continuous integration (CI) pipeline for an Elixir Phoenix web application.

Demo project#

Semaphore maintains an example Elixir Phoenix project that you can use to get started:

In the repository, you will find an annotated Semaphore configuration file: .semaphore/semaphore.yml.

The application uses Mix, Phoenix, ExUnit, Wallaby with headless Chrome, Credo, Dialyxir, and PostgreSQL as database.

Overview of the CI pipeline#

The demo Elixir Phoenix CI pipeline performs the following tasks:

  • Builds the project using Mix.
  • Performs static code analysis using credo, format, and dialyzer in parallel jobs.
  • Runs ExUnit tests, including integration tests with Wallaby using headless Chrome.

When code scanning detects errors, it's good practice to not run any further tests and fail the build early. For fast feedback, we configured the pipeline to fail the build before proceeding to automated tests.

The final example pipeline is composed of three blocks:

Elixir Phoenix CI pipeline

Sample configuration#

The project uses the configuration shown below. If you're new to Semaphore, we recommend going through the guided tour and linked documentation pages before trying out the example project.

# Use the latest stable version of Semaphore 2.0 YML syntax:
version: v1.0

# Name of your pipeline. In this example we connect two pipelines with
# a promotion, so it helps to differentiate the job of each.
name: Elixir Phoenix example CI pipeline on Semaphore

# An agent defines the environment in which your code runs.
# It is a combination of one of available machine types and operating
# system images. See:
# https://docs.semaphoreci.com/ci-cd-environment/machine-types/
# https://docs.semaphoreci.com/ci-cd-environment/ubuntu-18.04-image/
agent:
  machine:
    type: e1-standard-2
    os_image: ubuntu1804

# Blocks make up the structure of a pipeline and are executed sequentially.
# Each block has a task that defines one or many parallel jobs. Jobs define
# the commands to execute.
# See https://docs.semaphoreci.com/essentials/concepts/
blocks:
  - name: Set up
    task:
      jobs:
      - name: compile and build plts
        commands:
          # Checkout code from Git repository. This step is mandatory if the
          # job is to work with your code.
          - checkout

          - bin/setup_ci_elixir
          - sem-version elixir 1.8.1

          # Restore dependencies from the cache, command won't fail if it's
          # missing. More on caching:
          # - https://docs.semaphoreci.com/essentials/caching-dependencies-and-directories/
          # - https://docs.semaphoreci.com/programming-languages/elixir/
          - cache restore
          # Cache the PLT generated from the dialyzer to speed up pipeline process.
          # As suggested here - https://github.com/jeremyjh/dialyxir#continuous-integration
          # This will need to be rebuilt if elixir or erlang version changes.
          - cache restore dialyzer-plt

          - mix deps.get
          - mix do compile, dialyzer --plt
          - MIX_ENV=test mix compile

          # Store deps after compilation, otherwise rebar3 deps (that is, most
          # Erlang deps) won't be cached:
          - cache store
          - cache store dialyzer-plt priv/plts/

  - name: Analyze code
    task:
      # Commands in a prologue run at the beginning of each parallel job.
      # https://docs.semaphoreci.com/reference/pipeline-yaml-reference/
      prologue:
        commands:
        - checkout
        - bin/setup_ci_elixir
        - sem-version elixir 1.8.1
        - cache restore
        - cache restore dialyzer-plt
      # This block contains 3 parallel jobs:
      jobs:
      - name: credo
        commands:
        - mix credo -a

      - name: dialyzer
        commands:
        - mix dialyzer --halt-exit-status --no-compile

      - name: formatter
        commands:
        - mix format --check-formatted

  - name: Run tests
    task:
      prologue:
        commands:
        - checkout
        - bin/setup_ci_elixir
        - sem-version elixir 1.8.1
        - cache restore

      jobs:
      - name: ex_unit
        # Define an environment variable
        # See https://docs.semaphoreci.com/essentials/environment-variables/
        env_vars:
        - name: DATABASE_URL
          value: "ecto://postgres:@0.0.0.0:5432/sema_test"
        commands:
        # Start Postgres database service
        # https://docs.semaphoreci.com/reference/toolbox-reference/#sem-service
        - sem-service start postgres
        - mix test

Database access#

PostgreSQL and MySQL instances run inside each job and can be accessed with a blank password. For more information on starting and using databases, see the Ubuntu image and sem-service reference documentation.

Browser testing#

To enable headless Chrome support, add the following to your config/test.exs file:

config :wallaby,
  driver: Wallaby.Experimental.Chrome

Semaphore provides Chrome preinstalled, so no further action is needed on your part.

Run the demo Elixir project yourself#

A good way to start using Semaphore is to take a demo project and run it yourself. Here’s how to build the demo project with your own account:

  1. Fork the project on GitHub to your own account.
  2. Clone the repository to your local machine.
  3. In Semaphore, follow the link in the sidebar to create a new project.
  4. Create a secret as per the instructions above.
  5. Edit any file and push to GitHub, and Semaphore will run the CI/CD pipeline.

Next steps#

Congratulations! You have set up your first Elixir continuous integration project on Semaphore. Next, you will probably want to configure deployment. For more information and practical examples, see: