GitHub Actions - Build and Push a Docker Image

Github Actions are here, and they are really great to work with. Let's take a look at how you would use a GitHub action to build and push a Docker image to your GitHub container registry.

First, here's the YAML file we will consider. It lives in a repository at .github/workflows/dockerpublish.yml

name: Docker

on:
  push:
    branches:
      - master

env:
  IMAGE_NAME: docker.pkg.github.com/${{ github.repository }}/ghost

jobs:
  push:
    runs-on: ubuntu-latest
    if: github.event_name == 'push'
    steps:
      - uses: actions/checkout@v2
      - name: Build image
        run: docker build . --file Dockerfile --tag $IMAGE_NAME:${{ github.run_number }}
      - name: Log into registry
        run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login docker.pkg.github.com -u ${{ github.actor }} --password-stdin
      - name: Push images
        run: |
          docker tag $IMAGE_NAME:${{ github.run_number }} $IMAGE_NAME:latest
          docker push $IMAGE_NAME:${{ github.run_number }}
          docker push $IMAGE_NAME:latest

Let's break that down into smaller chunks to observe what is happening.

First, we name this workflow (pipeline) "Docker". That translates to what you see here:

Note that you could have many workflows in your repository. They will show up in a list in that same location.

Next, we tell GitHub Actions that we only want to trigger this workflow when there is a push to the branch named master using these lines:

on:
  push:
    branches:
      - master

This one is pretty simple. We simply set an environment variable IMAGE_NAME with a value. In this case, ${{ github.repository }} will be replaced with the name of the repository. For consistency, here are the two lines used to do that:

env:
  IMAGE_NAME: docker.pkg.github.com/${{ github.repository }}/ghost

You can specify as many environment variables as you need in this section.

Now we can start defining our jobs like this:

jobs:
  push:
    runs-on: ubuntu-latest
    if: github.event_name == 'push'
    steps:

Don't be confused here -- this instance of push on the second line is simply the name I gave the job. Then we set the job to run on ubuntu and state that we only want to run the job if the event trigger was a git push. Lastly, we put in the steps block. Now let's take a look at the steps that occur.

- uses: actions/checkout@v2

This is a reusable unit of code that has been written by the community (by GitHub in this case). It simply checks out my code. (More information)

- name: Build image
run: docker build . --file Dockerfile --tag $IMAGE_NAME:${{ github.run_number }}

This is how you define a bash script that you want to run. You give the step a name and define the command you want to run.

- name: Log into registry
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login docker.pkg.github.com -u ${{ github.actor }} --password-stdin

This step is how you would go about logging into your personal github package registry so that you can push or pull image containers. Note that I have created a secret in this repository called GITHUB_TOKEN which contains a personal access token that I generated via GitHub. You will need to do the same if you plan to use the GitHub package registry.

The final step is an example of a multi-line script. Using the | character (standard for YAML), says "continue reading until you reach the next line at this indent level"... or something like that anyway.

- name: Push images
run: |
  docker tag $IMAGE_NAME:${{ github.run_number }} $IMAGE_NAME:latest
  docker push $IMAGE_NAME:${{ github.run_number }}
  docker push $IMAGE_NAME:latest

That's the gist of GitHub Actions wrapped up into a relatively simple pipeline. The pipelines are relatively simple to write and have a lot of extremely powerful capabilities that continue to grow in relation to other CI/CD tools.

Here are some useful links to go with this content: