If you're hosting your software project on GitHub and it runs in a container, here's a dead-simple, drop in GitHub Actions pipeline you can use to release your image.

Features

  • OCI image annotations

  • GitHub/DockerHub repository README syncing

  • DockerHub & GitHub Container Registry publishing

  • GitHub Release creation

Workflow

Releasing is based on tags. If you adhere to semantic versioning, a release will be built and published on every tag starting with v. So, pushing a v0.1.0 tag to your main branch will build and push a container image to <GitHub username>/<repo name>:v0.1.0 on DockerHub & GHCR

Prerequisites

If you want to publish images to DockerHub, you'll need to define the following secrets in your GitHub repo's environment:

  • DOCKERHUB_USERNAME: Doesn't necessarily need to be a secret

  • DOCKERHUB_PASSWORD: Yes, password I'm afraid. Necessary for the README syncing

  • DOCKERHUB_TOKEN: For authenticating image pushes

So, here it is

Write this in the root of your repository to something like .github/workflows/release.yaml

Modify the on conditions to your liking. The following example is what I use for Go projects

name: Build & release Docker images

on:
  push:
    branches:
      - master
    tags:
      - 'v*'
    paths:
      - 'Dockerfile'
      - '*.go'
      - 'go.*'
      - '.github/**'

  pull_request:
  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Fetch history
        run: git fetch --prune --unshallow

      - name: Configure Git
        run: |
          git config user.name "$GITHUB_ACTOR"
          git config user.email "$GITHUB_ACTOR@users.noreply.github.com"          

      - name: Get the current ref name
        run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" | sed 's/\//_/g' >> $GITHUB_ENV

      - name: Setup QEMU
        uses: docker/setup-qemu-action@v1

      - name: Setup Docker Buildx
        uses: docker/setup-buildx-action@v1

      - name: Login to DockerHub
        if: ${{ startsWith(github.ref, 'refs/tags/') }}
        uses: docker/login-action@v1
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Login to GitHub Container Registry
        if: ${{ startsWith(github.ref, 'refs/tags/') }}
        uses: docker/login-action@v1 
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Annotate Docker images
        if: ${{ startsWith(github.ref, 'refs/tags/') }}
        id: docker_meta
        uses: crazy-max/ghaction-docker-meta@v1
        with:
          images: ${{ github.repository }}

      - name: Build Docker image
        uses: docker/build-push-action@v2
        with:
          context: .
          file: ./Dockerfile
          platforms: linux/amd64
          push: false
          labels: ${{ steps.docker_meta.outputs.labels }}
          tags: ${{ github.repository }}:${{ env.RELEASE_VERSION }}

      - name: Publish Docker image to DockerHub & GitHub Container Registry
        uses: docker/build-push-action@v2
        with:
          context: .
          file: ./Dockerfile
          platforms: linux/amd64
          push: ${{ startsWith(github.ref, 'refs/tags/') }}
          labels: ${{ steps.docker_meta.outputs.labels }}
          tags: |
            ${{ github.repository }}:${{ env.RELEASE_VERSION }}
            ghcr.io/${{ github.repository }}:${{ env.RELEASE_VERSION }}            

      - name: Update DockerHub repo description
        if: ${{ startsWith(github.ref, 'refs/tags/') }}
        uses: peter-evans/dockerhub-description@v2
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_PASSWORD }}
          repository: ${{ github.repository }}

      - name: Create Release
        if: ${{ startsWith(github.ref, 'refs/tags/') }}
        uses: actions/create-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ github.ref }}
          release_name: Release ${{ env.RELEASE_VERSION }}

I hope some people find this useful :)