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.
![Image showing workflow event](https://private-user-images.githubusercontent.com/11951136/342119879-237299fe-c17b-4548-a03c-e1adc8523e33.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjIyNDg1NjgsIm5iZiI6MTcyMjI0ODI2OCwicGF0aCI6Ii8xMTk1MTEzNi8zNDIxMTk4NzktMjM3Mjk5ZmUtYzE3Yi00NTQ4LWEwM2MtZTFhZGM4NTIzZTMzLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA3MjklMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwNzI5VDEwMTc0OFomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWE1MjQ3NjI3NGM3NWFiNWIzNTAwOTk4OWFlYTg1NmIxYjhlYmFhZTc1NDlkYzk0MTAyYWNiODRlYjE5OGU0MjkmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.32tF37bjvfQw9YVJMREVDwX80EAHbAo3fzD5Wq8LU6o)
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.
![Merge action triggers both workflows](https://private-user-images.githubusercontent.com/11951136/342120430-29aad330-e972-44bb-889a-2f933941d1ad.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjIyNDg1NjgsIm5iZiI6MTcyMjI0ODI2OCwicGF0aCI6Ii8xMTk1MTEzNi8zNDIxMjA0MzAtMjlhYWQzMzAtZTk3Mi00NGJiLTg4OWEtMmY5MzM5NDFkMWFkLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA3MjklMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwNzI5VDEwMTc0OFomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTdhNjdkOGFkOTZiNTU0ZTViZTkxNGM5NjljODFlNGY1ZDQ4NTI5MTZmYWUxNjRiNDYyYmEwNTk0MmNhOWJlMDYmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.3b4-qGl592lXzgyYbImjO7KJvz6i2JlOK27R1VQ9Kq8)
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