DEV Community

loading...
Cover image for Automate Publishing of Your Github Repository to  Docker Hub

Automate Publishing of Your Github Repository to Docker Hub

Ivan V.
Full-stack web developer
・5 min read

In this blog post, I'm going to show you how to create a Docker image from your github repository and publish it to the Docker hub container registry. So anytime you cut a new release or commit to the master branch, Github actions will build your image and push it to the registry.

Why?

Why would you publish your repository as a Linux container image? Good question. Not all projects should be published as Linux container images. The rule of thumb is if your code can be used as a standalone piece of software (front-end and back-end applications, servers, databases, command-line programs) then it could and it should be packaged as a Linux container.

By publishing your code to the container registries ( it doesn't have to be a Docker registry) you can automate the deployment of your apps by pulling those published containers to your hosting providers, stopping the old ones, and running the new ones ( I'm going to show you how to do this in a future article).

The Plan

This tutorial will be software language-agnostic since we are not going to concentrate on the process of packaging the code itself, but we are going to concentrate on setting up continuous integration and tying it all together (although the example repository is a Node.js server application).

The Code

When building software one very important rule is that the master branch should always be deployable. So we are only ever going to publish Docker images from the master branch although you could publish from different branches and create different images such as canary, beta etc...

So we have two options for the master branch. We could publish the images on every commit to the master or we could publish the images only when we create/publish a new release.
If you are unsure what Github releases are and how to use them, you can consult Github documentation.

This is all the code that is needed for the github actions workflow.

name: Build and publish Docker image
on:
  release:
    types: [published]
# on:
#   push:
#     branches:
#       - master
jobs:
  push_to_registry:
    name: Build and publish
    runs-on: ubuntu-latest
    steps:
      - name: Get release tag
        id: tag_name
        run: echo ::set-output name=SOURCE_TAG::${GITHUB_REF#refs/tags/}
      - name: Check out the repo
        uses: actions/checkout@v2
      - name: Npm Install
        run: npm ci
      - name: Npm Build
        run: npm run build
      - name: Login to DockerHub
        run: echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
      - name: Build image
        env:
          SOURCE_TAG: ${{ steps.tag_name.outputs.SOURCE_TAG }}
        run: ./docker-image/build.sh
      - name: Docker Hub Description
        uses: peter-evans/dockerhub-description@v2
        env:
          DOCKERHUB_USERNAME: ${{ secrets.DOCKER_USERNAME }}
          DOCKERHUB_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
          DOCKERHUB_REPOSITORY: ivandotv/grant-server
Enter fullscreen mode Exit fullscreen mode

Now, I'm going to explain each step individually.

First, we need to respond to the event that is triggered on the repository. In our case, we are responding to the release event of type published. This means that the workflow will only ever be triggered when this event happens. There are a few ways you can trigger the release event on the repository, you can automate it (by creating another workflow that does this for you), or you can do it manually. You can read more about the github workflow events from the official documentation.

on:
  release:
    types: [published]
Enter fullscreen mode Exit fullscreen mode

Or if you want to respond to every push to the master branch you can use this code (commented out in the original):

 on:
   push:
     branches:
       - master
Enter fullscreen mode Exit fullscreen mode

Next, we need to set up a job that is going to do all the heavy lifting. The job id is push_to_registry and the name is "Build and publish". It is going to run on Ubuntu 20.04

jobs:
  push_to_registry:
    name: Build and publish
    runs-on: ubuntu-20.04
    steps:
    # see next step
Enter fullscreen mode Exit fullscreen mode

The Steps

1 - Since we are building the image after a new release (which always should have a git tag) we are going to retrieve that tag, which will later be used in step 6. In case if you are using a push to master event then you should use the latest commit data instead of a tag, something like git rev-parse --short HEAD.

- name: Get release tag
  id: tag_name
  run: echo ::set-output name=SOURCE_TAG::${GITHUB_REF#refs/tags/}
Enter fullscreen mode Exit fullscreen mode

2 - Check out the repository. This means that we are going to have the full repository (all the files) to work with inside our workflow.

- name: Check out the repo
  uses: actions/checkout@v2
Enter fullscreen mode Exit fullscreen mode

3 - Since the example is a Node.js application we are going to install all node package dependencies we need:

- name: Npm Install
  run: npm ci
Enter fullscreen mode Exit fullscreen mode

4 - Build the application by running the build script from the package.json.
Note that the build script could run other tasks like lint and test but at this point, we are counting on that the application works as expected since all the testing should be done in some previous workflow that you have set up when pushing commits to your branches.

- name: Npm Build
  run: npm run build
Enter fullscreen mode Exit fullscreen mode

5 - Login to Docker hub. For this step, you need to setup github secrets (that are going to be available inside the workflow). We need docker username and docker password. How to create secrets is explained in Github docs

- name: Login to DockerHub
  run: echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
Enter fullscreen mode Exit fullscreen mode

6 - Build the image. Building the Docker image involves a few command line calls, so the best thing is to create a script that will group all those commands in a single file that you can run easily. We are setting the SOURCE_TAG environment variable that we are referencing from the previous step (steps.tag_name - step 1), this variable will be picked up by the script ./docker-image/build.sh (this is the file inside the repository itself) and be used to tag the docker image before pushing to Docker hub.

- name: Build image
  env:
    SOURCE_TAG: ${{ steps.tag_name.outputs.SOURCE_TAG }}
  run: ./docker-image/build.sh
Enter fullscreen mode Exit fullscreen mode

7 - Optional. Since Docker hub images also have the concept of a README file, we are going to take the README.md from the repository and also put it up on the Docker hub. That way we have only one source for the documentation, both for the Github repository and the Docker hub.

- name: Docker Hub Description
   uses: peter-evans/dockerhub-description@v2
   env:
    DOCKERHUB_USERNAME: ${{ secrets.DOCKER_USERNAME }}
    DOCKERHUB_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
    DOCKERHUB_REPOSITORY: ivandotv/grant-server
Enter fullscreen mode Exit fullscreen mode

That is all it takes to automate the publishing of container images to the Docker hub.

Now, if you take a look at the repository (link provided below) you will find workflow files in .github/workflows directory.

.github/
└── workflows
    ├── CI.yml
    └── docker-build.yml
Enter fullscreen mode Exit fullscreen mode

There are two files: CI.yml and docker-build.yml. Code referenced in this tutorial is from the later file, the former one is used to test the code, publish to NPM and create and then tag a release. That file triggers the release event that workflow in this tutorial (docker-build.yml) is using to start the publishing process. If you are developing JavaScript applications you might want to look at that file as well.

Next, we have docker-image directory with two files

docker-image/
├── build.sh
└── Dockerfile
Enter fullscreen mode Exit fullscreen mode

I haven't covered those files in this tutorial because they are used for building the actual Docker image itself, build.sh file contains command-line instructions on how to prepare for the build process, and Dockerfile contains instruction for building the image.

Repository: ivandotv/grant-server
Docker hub page: Grant server

Thank you for reading.

Discussion (0)