Last week, GitHub presented the new Actions platform with support for CI/CD. As early adopters, we have been using it for our internal core infrastructure CI/CD pipelines.
In this post, we share lessons from our upgrade to the latest Actions release.

The new GitHub Actions Beta.

Migrating from HCL to YAML syntax

The most prominent change has been that Actions now uses YAML instead of the HCL spec. To make the transition smoother, GitHub offers a migration tool, supplemented by a comprehensive guide. The migration tool worked very well for us.
When using the migration tool, it is important to note that:
  1. All comments in the main.workflow get dropped
  2. All parallel workflows are serialized

An example of an Actions Beta workflow being migrated to the new pipeline.

In the new workflow:
  • All parallel executions are linearized.
  • The parallelization is at the job level instead of the workspace level. This allows jobs to scale better, as each job runs on a dedicated VM.
  • Each job can contain multiple steps that share the file system.
Therefore, once you run the migration tool, we highly recommend reviewing your new workflow file to identify any parallelization opportunities.

Workflow Tips and Best Practices

Here, we share some tips and best practices from our experience making builds run better.

Print the GitHub Context in the first step for debug purposes

In order to configure workflows correctly, we need to know what our Actions payloads look like. The GitHub Context contains information about workflows, virtual envs, jobs and steps.
We can set a GITHUB_CONTEXT environment variable and use the toJson function within a run block to print the GitHub Context.
jobs:
  my-job
    name: my-job
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@master
    - name: Print GitHub Context # Debug step
      env:
        GITHUB_CONTEXT: ${{ toJson(github) }}
      run: echo "$GITHUB_CONTEXT"

The required configuration necessary to print the GitHub Context.

An example GitHub Context for the push event can be found here. The docs cover a complete list of event types of payloads.

Filtering steps without failing your workflow

The previous Actions Beta allowed actions to return an EX_CONFIG status code indicating that the action terminated, but did not fail. This was useful for terminating concurrent actions, and to prevent further actions from starting.
We relied heavily on this feature:
  • For pushing images to our registry
  • To avoid triggering builds on branch delete
  • To restrict rolling updates only to tag pushes
action "build api-upload" {
  uses    = "actions/docker/cli@master"

  args = [
    "build",
    "-t",
    "api-upload",
    "./services/api-upload",
  ]
}

action "build api-domains" {
  uses    = "actions/docker/cli@master"
  args = [
    "build",
    "--build-arg",
    "-t",
    "api-domains",
    "./services/api-domains",
  ]
}

action "tag filter" {
  needs = [
    "docker build api-domains",
    "docker build api-upload",
  ]

  uses = "actions/bin/filter@master"
  args = "tag and not deleted"
}

action "deploy api-domains" {
  needs = ["docker push api-domains"]
  uses  = "docker://zeit/deploy"
}

action "deploy api-upload" {
  needs = ["docker push api-upload"]
  uses  = "docker://zeit/deploy"
}

Simplified version of a workflow in the previous Actions Beta.

In the example above, the filter action checks if the current workflow reference corresponds to a tag push, and proceeds with the deploy actions if that is the case.
This feature is not yet available in the new Actions platform. However, we can accomplish the same behavior in the latest Actions by using the newly introduced if condition in the step definition.
api-upload:
  name: api-upload
  runs-on: ubuntu-latest
  steps:
  - uses: actions/checkout@master
  - name: build api-upload
      run: docker build --build-arg "IMAGE_RELEASE_VERSION=$(echo
        ${{ github.ref }} | cut -f3 -d'/')" -t api-upload ./services/api-upload
  - name: tag
    if: contains(github.ref, 'tags')
    env:
      REF: ${{ github. ref }}
    run: docker tag api-upload  zeit/api-upload:${REF##*/}
  - name: deploy
    uses: docker://zeit/deploy
    if: contains(github.ref, 'tags')

api-domains:
 name: api-domains
 runs-on: ubuntu-latest
 steps:
 - uses: actions/checkout@master
 - name: build api-domains
     run: docker build  --build-arg "IMAGE_RELEASE_VERSION=$(echo
       ${{ github.ref }} | cut -f3 -d'/')" -t api-domains ./services/api-domains
 - name: tag
    if: contains(github.ref, 'tags')
    env:
      REF: ${{ github. ref }}
    run: docker tag api-domains  zeit/api-domains:${REF##*/}
  - name: deploy
    uses: docker://zeit/deploy
    if: contains(github.ref, 'tags')

The same workflow in the latest GitHub Actions.

Further explanation:
  • In the previous Actions Beta definition, both the build and deploy actions execute in parallel in a single VM. To emulate the same behavior with the latest Actions Beta, you need to split them into different job definitions (api-upload and api-domains). Each job now runs in parallel on its own VM, so you can no longer share the filesystem between them.
  • Since the new Actions Beta doesn't handle EX_CONFIG yet, you can't use filtering steps like in our first example. Instead, you can add an if condition to each step to achieve the same result. Otherwise, if you exit anything but 0 on any step, the workflow is marked as failed.
  • As most of the current runners already have docker and other tools installed, you can avoid using the actions/docker/cli dependency.

Using pre-built images in Docker container steps

There are two ways to host your docker actions — within a public repo, or by building the image yourself, pushing it to an image registry, and referencing it with the docker:// prefix.
We've found the the latter approach to be more efficient — it prevents a long docker build.

Using a Docker GitHub repo reference.

Using a Docker Hub image reference

Conclusion

At ZEIT we are very excited about the opportunities GitHub Actions opens up for the developer community, and are proud to have been early adopters.
Have are you using GitHub actions in your workflow? Let us know via Twitter.