I’ve been missing for a while, busy with WiFiChallenge Academy and several exciting projects coming this year. I hope you find this article helpful.
GitHub Basic Workflows: Compile, Release, and Create Docker Images Link to heading
In software development, automating workflows is crucial for efficiency. This article guides you through:
- Using Git tags for automated versioning and releases.
- Building and pushing Docker images to GitHub Container Registry (GHCR) and Docker Hub.
- Creating multi-OS releases automatically upon tagging (Windows, Linux, macOS).
These workflows apply seamlessly to both your main and development branches.
Note: This method works well for me, but I’m always open to improvements. If you find a better way, feel free to reach out!
Table Of Contents
Fundamental Concepts Link to heading
GitHub Workflows and Actions Link to heading
GitHub Actions let you automate tasks such as building, testing and deploying your code directly in your repository. This automation consists of two main components.
-
A workflow is defined in YAML files located in the
.github/workflows
folder of your repository. It outlines the entire process by specifying when tasks should run, for example after a push event, on a pull request or at scheduled intervals, and it defines a series of jobs with multiple steps. -
An action is an individual task or command that performs a specific function, for example checking out your code or running tests. Actions can be reused across various workflows to create a complete automation pipeline.
In short, workflows describe the overall process while actions serve as the building blocks that complete each step.
Git tags Link to heading
Git tags are essential markers in version control that help you identify significant points in your project’s history, such as stable releases (e.g., v1.0, v2.0). They serve as bookmarks that make it easier to locate or revert to important states of your repository. In this article, tags are used to trigger workflows on the main branch. You can create a tag and push it to your repository with the following commands:
# Create tag
git tag <tag-name>
# Push tags to GitHub
git push origin --tags
This approach ensures that every important release is clearly defined in your project’s timeline.
Docker Publishing Link to heading
We’ll automate building and pushing Docker images to:
- GitHub Container Registry (
ghcr.io
) - Docker Hub
These images facilitate easy deployment in various environments.
Workflow 1: Build and Publish Docker Images (Main Branch – Tag Trigger) Link to heading
Creating Docker images automatically for both GitHub Packages and Docker Hub simplifies deployment and ensures consistency. I this case I will use a template repository in my GitHub:
Workflow Overview Link to heading
This GitHub Actions workflow includes several clearly defined steps:
- Trigger: (on: push → tags): The workflow is triggered whenever a tag that starts with
v
(likev1.0.0
,v2.3
, etc.) is pushed. - Set up QEMU & Set up Docker Buildx
- QEMU enables cross-compilation for multiple CPU architectures (e.g.,
amd64
,arm64
). - Buildx allows building Docker images for multiple platforms simultaneously.
- QEMU enables cross-compilation for multiple CPU architectures (e.g.,
- Checkout: This step pulls your repository’s code into the runner.
- Set lowercase repository name: Some container registries and Docker requirements prefer lowercase. This step transforms your repo’s name to lowercase and stores it as an output variable.
- Extract metadata (docker/metadata-action): This action automatically generates Docker tags and labels based on your repo’s metadata—very handy for consistent version tagging.
- Login to Docker Hub & GitHub Container Registry:
- We use
docker/login-action
for Docker Hub, requiringDOCKERHUB_TOKEN
as a secret in your GitHub repository settings. - For GitHub Container Registry, we pipe
GITHUB_TOKEN
to Docker login.
- We use
- Build and push
This final step compiles the Docker image for multiple architectures and pushes it to both Docker Hub and GHCR, streamlining the deployment process.
Complete YAML Workflow Link to heading
Create a file at ./.github/workflows/docker-image.yml
:
name: Docker Build and Publish project
on:
push:
tags:
- 'v*'
jobs:
docker:
runs-on: ubuntu-latest
steps:
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Checkout
uses: actions/checkout@v3
- name: Set lowercase repository name
id: set_lowercase_repo
run: |
LOWERCASE_REPO=$(echo "${GITHUB_REPOSITORY}" | tr '[:upper:]' '[:lower:]')
echo "::set-output name=lowercase_repo::${LOWERCASE_REPO}"
# docker
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ steps.set_lowercase_repo.outputs.lowercase_repo }}
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ github.actor }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
run: echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
# Push
- name: Build and push
id: docker_build
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: |
${{ steps.meta.outputs.tags }}
ghcr.io/${{ steps.set_lowercase_repo.outputs.lowercase_repo }}:latest
ghcr.io/${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
- To upload to Docker Hub, add a DockerHub token to your repository’s Actions secrets. Go to Settings > Secrets and variables > Actions and add
DOCKERHUB_TOKEN
.
-
To enable pushing to GitHub’s container registry, ensure that write permissions are granted to the workflow.
-
The
GITHUB_TOKEN
is automatically provided by GitHub and used in the workflow for authenticating registry operations.
Example Result Link to heading
After the workflow completes successfully, you can view its status on your GitHub Actions page. The Docker images are published to both Docker Hub and GitHub Container Registry, as illustrated by these sample screenshots:
Workflow 2: Build and Publish Docker Images from the Dev Branch Link to heading
Workflow Overview Link to heading
All commits to your dev
branch will trigger a Docker build. Most steps are identical, but note:
- Trigger: Runs on every push to the
dev
branch. - Tags: We use
:dev
for both Docker Hub and GHCR images. This helps distinguish them from production/stable images.
Full YAML Link to heading
Create a file at ./.github/workflows/docker-image-dev.yml
:
name: Docker Build and Publish project DEV
on:
push:
branches:
- 'dev'
jobs:
docker:
runs-on: ubuntu-latest
steps:
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Checkout
uses: actions/checkout@v3
- name: Set lowercase repository name
id: set_lowercase_repo
run: |
LOWERCASE_REPO=$(echo "${GITHUB_REPOSITORY}" | tr '[:upper:]' '[:lower:]')
echo "::set-output name=lowercase_repo::${LOWERCASE_REPO}"
# Docker
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ steps.set_lowercase_repo.outputs.lowercase_repo }}
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ github.actor }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
run: echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
# Push
- name: Build and push
id: docker_build
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: |
${{ steps.set_lowercase_repo.outputs.lowercase_repo }}:dev
ghcr.io/${{ steps.set_lowercase_repo.outputs.lowercase_repo }}:dev
labels: ${{ steps.meta.outputs.labels }}
Example Result Link to heading
Once changes are pushed to the dev
branch, GitHub Actions builds and publishes Docker images with the :dev
tag. This continuous integration makes it clear when a non-production version is being updated, as shown in these screenshots:
Workflow 3: Building and Releasing for All Operating Systems (in Golang) Link to heading
Workflow Overview Link to heading
This workflow automates the process of compiling your project for multiple operating systems so that your software is easily accessible to a wide audience. Triggered by pushing a version tag (for example, v1.2.3
), it follows these steps:
- Trigger: The workflow triggers on any pushed tag matching
v*
(e.g.,v1.0.0
,v2.3.1
, etc.). - Build Job (Matrix Build):
- Check Out your code.
- Set Up Go to a specific version.
- Compile the application for each OS and architecture combination specified in the matrix (
windows
,linux
,darwin
foramd64
andarm64
). - Upload each compiled binary as an artifact.
- Publish Release Job:
- Download the artifacts from the Build job.
- Create a new (draft) GitHub Release using your tag name.
- Upload the downloaded binaries as Release assets, making them available for users to download.
YAML Workflow Link to heading
Create a file, for example, ./.github/workflows/release.yml
:
name: Build and Release
on:
push:
tags:
- 'v*' # Trigger this workflow whenever a push event includes a tag like 'v1.0.0', 'v2.3.4', etc.
jobs:
build:
name: Build
runs-on: ubuntu-latest
strategy:
matrix:
# Define the Go version(s) you want to use for the build
go-version: [1.21.6] # Make sure this version is valid
# Define the set of operating systems (OS) to build for
os: [windows, linux, darwin]
# Define the set of architectures (ARCH) to build for
arch: [amd64, arm64] # darwin does not support 386 or arm
steps:
# Step 1: Check out the repository code so it can be built
- name: Checkout code
uses: actions/checkout@v3
# Step 2: Set up the specified Go version
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: ${{ matrix.go-version }}
# Step 3: Build the application for each OS and architecture in the matrix
- name: Build binary
run: |
if [ "${{ matrix.os }}" = "windows" ]; then
GOOS=${{ matrix.os }} GOARCH=${{ matrix.arch }} go build -o "${{ github.event.repository.name }}-${{ matrix.os }}-${{ matrix.arch }}.exe"
else
GOOS=${{ matrix.os }} GOARCH=${{ matrix.arch }} go build -o "${{ github.event.repository.name }}-${{ matrix.os }}-${{ matrix.arch }}"
fi
# Step 4: Upload the built binaries as artifacts for use by the next job
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
# The artifact name includes OS and ARCH (and .exe for Windows)
name: ${{ github.event.repository.name }}-${{ matrix.os }}-${{ matrix.arch }}${{ matrix.os == 'windows' && '.exe' || '' }}
# Provide the path to the generated binary
path: ${{ github.event.repository.name }}-${{ matrix.os }}-${{ matrix.arch }}${{ matrix.os == 'windows' && '.exe' || '' }}
if-no-files-found: error
publish_release:
# This job depends on the build job completing successfully
needs: [build]
runs-on: ubuntu-latest
permissions:
contents: write # Required to create and modify releases
actions: read # Access to actions
steps:
# Step 5: Download all the build artifacts produced by the previous job
- name: Download build artifacts
uses: actions/download-artifact@v4
# Step 6: Create a new draft release using the tag's name
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref_name }}
release_name: Release ${{ github.ref_name }}
draft: true
prerelease: false
# Step 7: Upload the previously downloaded binaries to the new release as assets
- name: Upload release assets
uses: dwenegar/upload-release-assets@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
release_id: ${{ steps.create_release.outputs.id }}
assets_path: .
Example Result Link to heading
After pushing a version tag (for example, v0.1
), this workflow compiles the project for multiple OS/architecture combinations and publishes the resulting binaries in a draft GitHub Release. This clearly links the compiled assets to a specific version, as seen in the sample screenshot below:
Changing the Configuration for Other Languages Link to heading
Building and Releasing C Binaries Link to heading
- Remove Go Steps:
- Delete any references to
actions/setup-go@v2
and the Go build commands in your workflow.
- Delete any references to
- Install C Compiler:
- For Ubuntu runners, add a step to install build tools (e.g.,
sudo apt-get install build-essential
).
- For Ubuntu runners, add a step to install build tools (e.g.,
- Compile C:
- Replace the
go build
step with a GCC (or Clang) command, for example:gcc main.c -o myprogram-${{ matrix.os }}-${{ matrix.arch }}
- Replace the
- Upload Assets:
- In the “Upload Release Assets” step, list your new C binary names (e.g.,
myprogram-windows-amd64.exe
,myprogram-linux-amd64
) instead of the Go binaries.
- In the “Upload Release Assets” step, list your new C binary names (e.g.,
Building and Releasing Python Executables Link to heading
- Remove Go Steps:
Similarly, remove the Go setup and build steps. - Install Python & Dependencies:
Useactions/setup-python@v2
to install Python and any necessary bundling tools such aspyinstaller
(installed viapip
). - Create Python Executable:
Replace the Go build command with a PyInstaller (or another bundler) command, for example:pyinstaller --onefile my_script.py mv dist/my_script my_script-${{ matrix.os }}
- Upload Assets:
Update the asset paths in the “Upload Release Assets” step to reflect the Python executable names (for example,my_script-windows.exe
,my_script-linux
).
Resources Link to heading
Below are some official documentation links and resources you may find helpful:
-
GitHub Actions Documentation
https://docs.github.com/en/actions
A comprehensive guide to building your workflows. -
Docker Documentation
https://docs.docker.com
Official Docker docs for container best practices. -
GitHub Packages (Container Registry)
https://docs.github.com/en/packages
Learn how to configure, store, and manage packages (including Docker images) in GitHub. -
Docker Buildx GitHub Action
https://github.com/docker/build-push-action
Repository for the official Dockerbuild-push-action
, including advanced configurations. -
actions/create-release
https://github.com/actions/create-release
Learn more about creating GitHub releases through Actions. -
actions/upload-release-asset
https://github.com/actions/upload-release-asset
Documentation on how to upload release assets to your GitHub Releases.