Continuous Deployment to Gigalixir with GitHub Actions

We are going to set up a continuous integration / continuous deployment (CI/CD) pipeline with GitHub Actions to automatically run your automated tests on each pull request and automate deployment to Gigalixir when you push commits to your default (e.g. main) branch.

Overview

Here are the topics we’ll cover while deploying the project.

  1. What is Continuous Integration / Continuous Deployment (CI/CD)?
  2. Continuous Integration with GitHub Actions
  3. Add Continuous Deployment to Gigalixir using GitHub Actions

Prerequisites

To follow this guide, make sure you have at least the following applications installed on your machine. In the brackets are the versions with which this guide was written.

  • Elixir (1.12.3-otp-24)
  • Erlang (OTP 24.1)
  • Phoenix (1.5.13)
  • Node.js with npm (15.14.0)

Make sure to install at least Elixir 1.9 since we are using releases. We recommend using asdf to manage Erlang and Elixir versions.

Your new Phoenix project should be deployed to Gigalixir. We’ll start with the Gigalixir app we’ve been using to demo deployment on Gigalixir.

We’ve covered deploying Phoenix to Gigalixir and using Gigalixir’s remote debugging tools. Git clone from this commit in our project called HelloGigalixir.

What is Continuous Integration / Continuous Deployment (CI/CD)?

According to Red Hat, the "CI" in CI/CD always refers to continuous integration, which is an automation process for developers. Successful CI means new code changes to an app are regularly built, tested, and merged to a shared repository.

The "CD" in CI/CD refers to continuous delivery and/or continuous deployment.

Continuous delivery usually means a developer’s changes to an application are automatically bug tested and uploaded to a repository (like GitHub), where they can then be deployed to a live production environment by the operations team.

Continuous deployment (the other possible "CD") can refer to automatically releasing a developer’s changes from the repository to production.

We plan to create a complete CI/CD pipeline to deploy our Phoenix app to Gigalixir using GitHub Actions.

CI with GitHub Actions

First we will implement a CI workflow using GitHub Actions. We’ll use the workflow to run our automated test suite when we push code to GitHub and hopefully reduce bugs in production.

GitHub Actions allows you to start with a preconfigured workflow yaml file template. We’ll use the template for Elixir.

In you GitHub repository, click the "Actions" tab then "Set up this workflow".

Elixir Template for GitHub Actions

We need to make some updates to the GitHub Actions Elixir example workflow template, the main change is to add the database.

We have a Phoenix app and we used the CI GitHub Action ci.yml file from the Elixir Companies website project on GitHub as a reference.

Update elixir.yml

Here is the updated elixir.yml workflow yaml file for our Github Actions. We’ve named the workflow Elixir CI. We’ll discuss the details of each section below.

name: Elixir CI

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    name: Build and test
    runs-on: ubuntu-latest
    services:
      db:
        env:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: postgres
        image: postgres:11
        ports: ['5432:5432']
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
    - uses: actions/checkout@v2
    - name: Set up Elixir
      uses: erlef/setup-elixir@885971a72ed1f9240973bd92ab57af8c1aa68f24
      with:
        elixir-version: '1.12.3' # Define the elixir version [required]
        otp-version: '24.1' # Define the OTP version [required]
    - name: Restore dependencies cache
      uses: actions/cache@v2
      with:
        path: deps
        key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }}
        restore-keys: ${{ runner.os }}-mix-
    - name: Install dependencies
      run: mix deps.get
    - name: Run tests
      run: mix test
    - name: Check Formatting
      run: mix format --check-formatted

For now, we’ll use the default settings to run our workflow for both pull requests and pushes to main.

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

Then we’ll run our first job we’ve labelled Build and test on the latest ubuntu. Before we get to the Elixir setup, we also need to set up our Postgres service. Postgres options include our environment variables.

  test:
    name: Build and test
    runs-on: ubuntu-latest
    services:
      db:
        env:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: postgres
        image: postgres:11
        ports: ['5432:5432']
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

With the database created, we can use Checkout and Setup Elixir to prepare the environment.

Setup Elixir includes defining your elixir version and otp version. Then to install dependencies run mix deps and call mix test to automatically run tests in the test suite.

You can also do things like check formatting with mix format and static code analysis. You can accomplish static code analysis with a tool like Credo.

    steps:
    - uses: actions/checkout@v2
    - name: Set up Elixir
      uses: erlef/setup-elixir@885971a72ed1f9240973bd92ab57af8c1aa68f24
      with:
        elixir-version: '1.12.3' # Define the elixir version [required]
        otp-version: '24.1' # Define the OTP version [required]
    - name: Restore dependencies cache
      uses: actions/cache@v2
      with:
        path: deps
        key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }}
        restore-keys: ${{ runner.os }}-mix-
    - name: Install dependencies
      run: mix deps.get
    - name: Run tests
      run: mix test
    - name: Check Formatting
      run: mix format --check-formatted

Copy and paste the updated code into the new elixir.yml file on GitHub and commit the change.

Run workflow

Now GitHub Actions should run your new test suite. Click the "Actions" tab to watch the progress and results of your Elixir CI workflow.

If your mix format fails, you need to git pull the elixir.yml changes to your local machine, run mix format on the file(s) and then push changes back to GitHub.

Successful GitHub Actions workflow

You may see some deprecation warnings, but the GitHub Actions workflow exits with success after the tests all pass.

Add CD to Gigalixir using GitHub Actions

Now we’ll add CD to our CI/CD pipeline. With continuous deployment, if the tests pass after pushing changes to your GitHub default branch, the changes will automatically get deployed to production.

We’ll add a deploy step in the workflow using the Gigalixir CI/CD guide. There are two options for GitHub Actions. We’ll use Jesse Shieh’s gist as a starting point with some minor modifications.

To begin this section, git pull the changes to the git repository on your local machine from your default branch and checkout a new branch action:

git pull
git checkout -b action

Add deploy job

Add the following to the end of your elixir.yml workflow yaml file.

  deploy:
    runs-on: ubuntu-latest
    if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
    needs: test
    steps:
      - uses: actions/checkout@v1
      - run: sudo pip install gigalixir --ignore-installed six
      - run: gigalixir login -e "${{ secrets.GIGALIXIR_EMAIL }}" -y -p "${{ secrets.GIGALIXIR_PASSWORD }}"
      - run: gigalixir git:remote ${{ secrets.GIGALIXIR_APP_NAME }}
      - run: git push -f gigalixir HEAD:refs/heads/master

The deploy workflow will only run when commits are pushed to main, either directly or when you merge a pull request.

    if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}

It also uses needs: test to configure the workflow to run deploy only after a successful test job, see the jobs.<job_id>.needs documentation. Without this update, GitHub runs the jobs in parallel by default.

The deploy workflow steps will:

  1. Install gigalixir CLI
  2. Login in to Gigalixir through the CLI
  3. Add the gigalixir remote to git
  4. Push your code to initiate deployment including our automated database migration

Note: We automated database migrations in our previous post, so we don’t need to accomplish migrations in the workflow.

GitHub Secrets

We will use GitHub secrets to create and store encrypted secrets to use in the workflow for our Gigalixir username, password, and app name.

Follow the instructions for creating and storing encrypted secrets for GIGALIXIR_EMAIL GIGALIXIR_PASSWORD and GIGALIXIR_APP_NAME.

You can get your app name using the gigalixir CLI with the command gigalixir apps from your project folder.

Gigalixir secrets for GitHub Actions

Once you’ve stored your username, password, and app name:

git add .
git commit -m "add deploy job to GitHub Actions"
git push origin action

Then open a pull request on GitHub.

Run workflow

Now go to the "Actions" tab again to see the progress and results of your workflow. Your test should pass. They deploy job shouldn’t run since it only runs on pushes to main.

Test only GitHub Action workflow

Now merge your changes with the main branch in your git repository and push to origin.

git checkout main
git merge action --ff-only
git push

This time both the test and deploy jobs should run successfully.

Test and deploy GitHub Actions workflow

Now visit https://app_name.gigalixirapp.com/users and test your app running in production is deployed properly. If you run into problems, the finished project is available on GitHub as StakNine HelloGigalixir.

Conclusion

Congratulations! You’ve built a CI/CD pipeline with GitHub Actions to automatically run your automated tests on each pull request and automate deployment to Gigalixir when you push commits to your default (e.g. main) branch. Now that you’ve set up continuous deployment, check out our article covering the best error monitoring services for Elixir so you can deploy with confidence and effectively manage errors in your system.

Want To Know How To Deploy Phoenix Apps Using A single Command?

This brand-new FREE training reveals the most powerful new way to reduce your deployment time and skyrocket your productivity… and truly see your programming career explode off the charts!