What we’ll see in this blog post is a simple pattern to build a container image and push it to Docker Hub. What happens is based on whether we’re on a branch, responding to a pull-request, or responding to changes on
In its simplest form an Action: -
.yaml) file in the directory
There’s a lot more to it. You can refer to the official syntax documentation, a comprehensive reference. Importantly, as we’ll see here you can build some useful actions with just a few lines of YAML.
What we’ll be doing here is writing a pair of actions that: -
mainas a container image tagged
:latest. This will require Docker credentials
To create the actions we: -
We often need to handle actions during a Pull Request - usually this is just to ensure that any testing, linting or building works when submitted from a fork of a repository.
We can’t usually push (publish) any results at this time as this requires authentication with the container registry. As these secrets are injected into the action through repository or organisation secrets, which are not available during a Pull Request, we therefore cannot login to a registry during a pull request. Hence, in pull requests we tend to limit ourselves to linting and testing.
We need to provide a
token) for our chosen container registry (Docker Hub). We can’t put these in the Action directly but secrets can be injected in the form of environment variables (secrets).
As our secrets are used across a number of similar repositories we can place the username and password values into secrets that are part of our GitHub’s organisation. that way we only need to define them once but they can be used on all our repositories.
To add an organisation secret…
Now let’s create our action.
In the repository, create an action file called
.github/workflows/build.yaml. The build action will simply build the image on any branch or pull request.
All actions have a name, it’s free-form text. We tend to stick to space-separated lower-case as a style. So start your
build.yaml with a name: -
Now define the triggers that will start the action. Here we want to trigger on pushes (except those to the
main branch) and pull requests: -
You might need to use
mainfor old-style repositories. All newly created repositories now use
mainas the main branch.
Now we define the steps for our action in a job, that runs on ubuntu.
Here we simply rely on a library (built-in) action to build the image. An action written by the community.
We could write the individual docker build steps that are required to build a container image, but it’s already been done (in a much more powerful way), so we just use another action.
The docker/build-push-action we use doesn’t need anything else. It assumes the
Dockerfile is in the root of the repository and simply builds our container based on that fact.
We can use a different Dockerfile, name the image, provide tags, push to a registry and build for multiple architectures (ARM for example) using this one action. For our example, we don’t need any of that, so the build command is just one short and simple line in the action.
That’s it - our container will be built on any non-main branch or pull-request.
Our final workflow file looks like this: -
Now let’s write a separate action to build and push the container image, one that will run on the main branch.
In the repository, create an action file
.github/workflows/build-latest.yaml. The build and push action will look like this: -
You’ll see that we use another action (docker/login-action) to simplify logging into a container registry. We provide this action with the secrets we created earlier to allow us to safely login to Docker Hub.
We also provide the docker/build-push-action with an image name and tag for use when pushing to Docker Hub.
This action will run on any change that’s made to the main branch.
In future posts we’ll advance our experience with Actions and look at: -