A GitHub workflow for Elixir releases

Elixir 1.9 comes with support for building releases so I created a GitHub workflow that builds and publishes a phoenix application release to GitHub releases.

UPDATE: GitHub has changed the format of their workflows so this is now redundant! I’ve published an updated version.

Since Elixir 1.9 hasn’t been released yet I created a new elixir action that contains elixir 1.9.0-rc.0 so we can take advantage of the new features.

I also needed an action with elixir & postgres so I can run the tests (GitHub actions don’t currently allow you to link a postgres container). For conveinence this is using elixir 1.8.2 since there are no official elixir 1.9.0 containers yet, this will use the same version eventually.

Finally I needed an action to publish the release to GitHub, this bundles the ghr tool to make this easier.

The resulting release is built when the master branch is updated and is versioned automatically. For this to work properly you need to have a version tag eg. git tag -a v1.0 -m 'v1.0'.

The workflow

This final workflow looks like this

GitHub Workflow

This is the workflow source

workflow "Test and publish release" {
  on = "push"
  resolves = [
    "mix test",
    "github release",
  ]
}

action "mix deps.get" {
  uses = "moomerman/actions/elixir/1.9.0@master"
  runs = "mix"
  args = "deps.get"
}

action "mix format.check" {
  uses = "moomerman/actions/elixir/1.9.0@master"
  runs = "mix"
  args = "format --check-formatted"
  needs = ["mix deps.get"]
}

action "yarn install" {
  uses = "docker://node:latest"
  needs = ["mix deps.get"]
  runs = "yarn"
  args = "--cwd assets install"
}

action "mix deps.compile (test)" {
  uses = "moomerman/actions/elixir/1.8.2-postgres@master"
  runs = "mix"
  args = "deps.compile"
  env = {
    MIX_ENV = "test"
  }
  needs = ["mix deps.get"]
}

action "mix test" {
  uses = "moomerman/actions/elixir/1.8.2-postgres@master"
  args = "test"
  env = {
    MIX_ENV = "test"
  }
  needs = [
    "mix deps.compile (test)",
    "yarn install",
  ]
  secrets = ["XXX", "YYY"]
}

action "branch master" {
  uses = "actions/bin/filter@master"
  args = "branch master"
  needs = ["mix test"]
}

action "yarn deploy" {
  uses = "docker://node:latest"
  runs = "yarn"
  args = "--cwd assets deploy"
  needs = ["branch master"]
}

action "mix deps.compile (prod)" {
  uses = "moomerman/actions/elixir/1.9.0@master"
  runs = "mix"
  args = "deps.compile"
  env = {
    MIX_ENV = "prod"
  }
  needs = ["branch master"]
}

action "mix phx.digest" {
  uses = "moomerman/actions/elixir/1.9.0@master"
  runs = "mix"
  args = "phx.digest"
  env = {
    MIX_ENV = "prod"
  }
  needs = ["yarn deploy", "mix deps.compile (prod)"]
}

action "mix release" {
  uses = "moomerman/actions/elixir/1.9.0@master"
  runs = "mix"
  args = "release"
  env = {
    MIX_ENV = "prod"
  }
  needs = ["mix phx.digest"]
}

action "github release" {
  uses = "moomerman/actions/bin/ghr@master"
  env = {
    RELEASE_PATH = "_build/prod/rel"
    APPLICATION = "<< your app >>"
  }
  needs = ["mix release", "mix test"]
  secrets = ["ACTIONS_TOKEN"]
}

The resulting release is available in GitHub GitHub Release

Wrapping up

It took a while to get the dependencies set up correctly to take advantage of as many actions running in parallel as possible but I think it is now fully optimised.

The whole workflow takes about 7 minutes to run, which is mainly because GitHub actions don’t currently support any form of caching. I’m looking forward to the launch of GitHub releases this summer, hopefully a lot of these features will be included.

Next I’m looking at building a workflow that triggers on a new release, downloads and runs it and then executes a suite of acceptance tests driving a headless chrome browser. If the tests pass then it will automatically deploy the release.

If you have any tips or feedback please get in touch via Twitter.