In this gist we are going to look at the simple steps required to build a staggered CI process using Github Actions that generate multi-platform images/packages/artifacts for any application. .
-
We have an application that is deployed across 2 environments:
live
preview
-
We want to generate artifacts targeted for each environment that we can later use for manual and/or Continuous Deployment. Below represents a directory structure of a sample application
alphaduriendur@Arkos-MacBook-Pro personal-portfolio % tree -L 1
.
├── backend
├── deployment
└── frontend
4 directories, 0 files
alphaduriendur@Arkos-MacBook-Pro frontend % tree -L 1
.
├── Dockerfile
├── README.md
├── analysis_options.yaml
├── android
├── assets
├── build
├── images
├── ios
├── lib
├── linux
├── macos
├── pubspec.lock
├── pubspec.yaml
├── test
├── web
└── windows
12 directories, 5 files
alphaduriendur@Arkos-MacBook-Pro frontend %
The frontend in this case is a flutter app with it's own
Dockerfile
that builds the web server image behina a Nginx Proxy.
- Here we create a simple Github Action Workflow that generates images based on the event:
preview
image on PRs and any following pushes to that PRlive
image on being merged to default branch.
Below is a sample Github Action that should give you a rough idea on how to go about it:
name: ci-frontend-web
on:
push:
branches:
- "master"
paths:
- "frontend/**"
pull_request:
branches: [master]
paths:
- "frontend/**"
jobs:
preview:
name: Build and push for Preview
if: github.event_name == 'pull_request' || github.ref == 'refs/heads/master' && ( github.event_name == 'push' || github.event_name == 'workflow_dispatch' )
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: ./frontend
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ secrets.DOCKERHUB_USERNAME }}/<repo-name>:preview
live:
name: Build and push for Live
if: github.ref == 'refs/heads/master' && ( github.event_name == 'push' || github.event_name == 'workflow_dispatch' )
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: ./frontend
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ secrets.DOCKERHUB_USERNAME }}/<repo-name>:live
In the above sample we are:
- Using QEMU to build two different architectures of our image -
amd64
andarm64
. This allows us to run our containerized/packaged artifacts in our desired choice of hardware architecture. Check the documentation to see all the different architectures you can target for your build. If you are a systems administrators that maintain hybrid systems of mixed platforms then this would mean you would have no problem moving your application from one hardware platform to the other. - We are also separating the workflow based on the event that was triggered.

You can see for just a PR the job for the live build did not happen and was skipped since it did not satisfy the requirement
- Now when you merge the PR you see both images being built.

Feel free to suppress the preview step on the merge action if you like.
For more information on Github actions worfklows - follow the official documentation