Automating Tag Creation, Release, and Docker Image Publish with GitHub Actions
Tags: githubactions, cicd, automation, docker
Published: 2023-07-26 20:13:21

I'm currently working on the improvement of the [wallabag-kindle-consumer](https://github.com/natilou/wallabag-kindle-consumer) service.  Since the original repository hasn't been maintained, I decided to fork it. My first goal was to have an initial dev version (0.1.0) and a Docker image published on ghcr.io. Additionally, I wanted to automate the tag creation, the release of the service, and the process of building and publishing the Docker image using GitHub Actions.

I watched a couple of YouTube videos (I'll leave the links at the end of the article) and read GitHub Actions documentation, but the resources showed how to use different GitHub Actions separately and didn't combine them. After many tries, I finally managed to do it. I decided to write this article to show how I accomplished it, and hopefully, it can help others who want to do the same but don't know where to start.

**Step 1** - **[Create a Personal Access Token (PAT)](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens)on GitHub**. Make sure to select the `repo` and `write:packages` scopes.

**Step 2** - **Add the created `PAT` in the repository's secrets**:
   In the repository's page, navigate to:
   - `Settings` >> `Secrets and variables` >> `Actions` >> click in `New repository secret` and paste the created token.

**Step 3** - **Grant read and write permissions to GitHub Actions**:
   In the repository's page, navigate to:
   - `Settings` >> `Actions` >> `General` >> Scroll to the end of the page to find `Workflow permissions`, then select `Read and write permissions`.

These steps are necessary because when using GitHub Actions, the workflows automatically create a `GITHUB_TOKEN`, which is available as an environment variable within every GitHub Actions workflow. However, this token lacks the specific permissions we need in this case. By using the `PAT`, we can trigger subsequent workflows, such as building and publishing the Docker image after tag creation and release. 

**Step 4** - **Create workflows using the following actions**:

For automating releases based on [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/), I used the [google-github-actions/release-please-action@v3](https://github.com/google-github-actions/release-please-action).

For the Docker image workflow, I used:
- [docker/login-action@v2](https://github.com/docker/login-action) to login against a Docker registry.
- [docker/metadata-action@v4](https://github.com/docker/metadata-action) to extract metadata (tags, labels) from Git reference and GitHub events for Docker.
- [docker/build-push-action@v4](https://github.com/docker/build-push-action) to build and push Docker images with Buildx.

First workflow file (`relese-please.yml`):
``` yaml
name: release-please

on:
  push:
    branches:
      - main

permissions:
  contents: write
  pull-requests: write

jobs:
  release-please:
    runs-on: ubuntu-latest
    steps:
      - name: release-please
        id: release
        uses: google-github-actions/release-please-action@v3
        with:
          release-type: simple
          package-name: release
          bump-minor-pre-major: true
          bump-patch-for-minor-pre-major: true
          token: ${{ secrets.PAT }}
```

Second workflow file (`deploy.yml`):
``` yaml
name: build-docker-image

on:
  push:
    tags:
      - 'v*'

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build-and-push-image:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    steps:
      - name: Checkout repository
        uses: actions/checkout@v3

      - name: Log in to the Container registry
        uses: docker/login-action@v2
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.PAT }}

      - name: Extract metadata (tags, labels) for Docker
        id: meta
        uses: docker/metadata-action@v4
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=semver,pattern={{version}}

      - name: Build and push Docker image
        uses: docker/build-push-action@v4
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}

```

It's important to note that the first version of the release when using Release Please Action will be "1.0.0" by default. If you want "0.1.0" as initial version (like me), you can achieve this by creating an empty commit with the following command: 
``` bash
git commit --allow-empty -m "chore: release 0.1.0" -m "Release-As: 0.1.0"`
``` 
This will create a pull request with the desired version. You can find more information in the [documentation](https://github.com/googleapis/release-please#how-do-i-change-the-version-number).

In summary, using these two workflows and a Personal Access Token, we can configure GitHub Actions to automate the tags creation, releases, and the process of building and publishing the Docker image.
 

Thanks to [@DevOpsJourney](https://www.youtube.com/@DevOpsJourney) for the [GitHub Packages.. Containers in a GitHub repo?](https://www.youtube.com/watch?v=gqseP_wTZsk) video and thanks to [@eddiejaoude](https://www.youtube.com/@eddiejaoude) for the [GitHub Actions can automate your releases from your git commit messages - conventional commits](https://www.youtube.com/watch?v=fcHJZ4pMzBs&t=319s) video.