Skip to content

Commit

Permalink
build: refactor container build process (#3403)
Browse files Browse the repository at this point in the history
### Motivation

There is a bug building `arm64` containers under `amd64` which is
causing segfaults, we currently use QEMU to cross compile the containers
which can be reasonably slow.

There is also another bug where external contributors cannot trigger
builds on pull requests to basemaps which leaves pull requests unable to
be merged. eg: #3396

The build process for containers was also split across two workflows
this merges the workflows into a single container building workflow.

### Modifications

Disable build pipelines for external pull requests as they do not have
access to our secrets, which causes them to fail.

Split container builds into a matrix build using arm64 runners for arm
containers and amd64 runners for x86 containers.

### Verification

Created a number of containers with `container` tag on pull requests and
pushed commits to the master branch on `blacha/basemaps`
  • Loading branch information
blacha authored Feb 19, 2025
1 parent ef3a85e commit 724d580
Show file tree
Hide file tree
Showing 5 changed files with 237 additions and 158 deletions.
282 changes: 201 additions & 81 deletions .github/workflows/containers.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,120 @@
name: Containers

on: [push]
# Build containers when pushed to master or if a pull request has been labeled with "container"
on:
push:
branches:
- master
pull_request:
branches:
- master
types: [labeled, synchronize, opened]

jobs:
build-containers:
setup:
# Determine if any containers need to be built and what tags they will result in
name: Setup container tags

runs-on: ubuntu-latest

outputs:
# Version information
# v7.0.0
version: ${{ steps.version.outputs.version }}
# v7
version_major: ${{ steps.version.outputs.version_major }}
# v7.1
version_major_minor: ${{ steps.version.outputs.version_major_minor }}

# Tagging information as a JSON object
# eg { cli: ["ghcr.io/linz/basemaps/cli:latest", "ghcr.io/linz/basemaps/cli:v7"], needs_containers: true }
tags: ${{ steps.tags.outputs.result }}

steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup tags
id: version
run: |
GIT_VERSION=$(git describe --tags --always --match 'v*')
GIT_VERSION_MAJOR=$(echo $GIT_VERSION | cut -d. -f1)
GIT_VERSION_MAJOR_MINOR=$(echo $GIT_VERSION | cut -d. -f1,2)
echo "version=${GIT_VERSION}" >> $GITHUB_OUTPUT
echo "version_major=${GIT_VERSION_MAJOR}" >> $GITHUB_OUTPUT
echo "version_major_minor=${GIT_VERSION_MAJOR_MINOR}" >> $GITHUB_OUTPUT
- name: Create Image Tags
id: tags
uses: actions/github-script@v7
with:
script: |
// Images to create tags for
const images = ['cli', 'server'];
// Mapping of images to their tags
// cli => "ghcr.io/linz/basemaps/cli:latest,ghcr.io/linz/basemaps/cli:v7"
const output = {};
// List of tags to apply to images, eg "v7" or "latest"
const tags = [];
// If on master ensure that the "latest" and a specific tag version is used
if ('${{ github.ref }}' == 'refs/heads/master' ){
tags.push('latest');
// If on a release commit add `v7`, `v7.1` and `v7.1`
if (`${{ toJson(github.event.head_commit.message) }}`.startsWith('release:')) {
tags.push('${{ steps.version.outputs.version_major }}');
tags.push('${{ steps.version.outputs.version_major_minor }}');
}
tags.push('${{ steps.version.outputs.version }}');
}
// If a pull request is labeled as "container", ensure a pull request tag is created
// "ghcr.io/linz/basemaps/cli:pr-1124"
const labels = ${{ toJson(github.event.pull_request.labels.*.name) }}
if (labels.includes('container')) {
tags.push('pr-${{ github.event.number }}')
}
for (const img of images) {
const repo = `ghcr.io/${{ github.repository }}/${img}`
output[img] = JSON.stringify(tags.map(t => `${repo}:${t}`))
}
// Have any tags been created
output.needs_containers = tags.length > 0;
return output;
- name: List tags
run: |
echo ${{ toJson(steps.tags.outputs.result) }} | jq
build-containers:
needs: setup

runs-on: ${{ matrix.os }}

strategy:
matrix:
include:
- os: ubuntu-latest
arch: amd64
platform: linux/amd64

- os: ubuntu-24.04-arm
arch: arm64
platform: linux/arm64

permissions:
id-token: write
contents: read
packages: write

steps:
- uses: linz/action-typescript@v3

Expand All @@ -18,33 +128,17 @@ jobs:
CURRENT_VERSION=$(node -p "require('./lerna.json').version")
git tag v${CURRENT_VERSION} -m v${CURRENT_VERSION} || true # Only create the tag if it doesn't exist
# Package all the files
- name: Bundle & Package all files
run: |
npx lerna run bundle --stream
npm pack --workspaces
env:
NODE_ENV: 'production'

- name: Set up Docker Qemu
id: qemu
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v3

- name: Setup tags
id: version
run: |
GIT_VERSION=$(git describe --tags --always --match 'v*')
GIT_VERSION_MAJOR=$(echo $GIT_VERSION | cut -d. -f1)
GIT_VERSION_MAJOR_MINOR=$(echo $GIT_VERSION | cut -d. -f1,2)
echo "version=${GIT_VERSION}" >> $GITHUB_OUTPUT
echo "version_major=${GIT_VERSION_MAJOR}" >> $GITHUB_OUTPUT
echo "version_major_minor=${GIT_VERSION_MAJOR_MINOR}" >> $GITHUB_OUTPUT
- name: Copy packages and files
run: |
# Files are packed into the base directory
Expand All @@ -53,83 +147,109 @@ jobs:
cp -r packages/lambda-tiler/static/ packages/server/
cp -r packages/lambda-tiler/static/ packages/cli/
- name: Create docker metadata
id: meta
uses: docker/metadata-action@v5
with:
labels: org.opencontainers.image.version=${{ needs.setup.outputs.version }}
org.opencontainers.image.licenses=MIT
tags: |
type=sha
- name: Login to GitHub Container Registry
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3
uses: docker/login-action@v3 # v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: '@basemaps/cli - Build and export to Docker'
uses: docker/build-push-action@v5
- name: 'Build Container - @basemaps/cli'
uses: docker/build-push-action@v6
id: 'build_cli'
with:
context: packages/cli
load: true
tags: |
ghcr.io/linz/basemaps/cli:latest
ghcr.io/linz/basemaps/cli:${{ steps.version.outputs.version }}
- name: '@basemaps/cli - Test'
labels: ${{ steps.meta.outputs.labels }}
outputs: type=image,"name=ghcr.io/${{ github.repository }}/cli",push-by-digest=true,push=${{ fromJson(needs.setup.outputs.tags).needs_containers == true }}
build-args: |
GIT_HASH=${{ github.sha }}
GIT_VERSION=${{ needs.setup.outputs.version }}
GITHUB_RUN_ID=${{ github.run_id }}
- name: 'Build Container - @basemaps/server'
uses: docker/build-push-action@v6
id: 'build_server'
with:
context: packages/server
labels: ${{ steps.meta.outputs.labels }}
outputs: type=image,"name=ghcr.io/${{ github.repository }}/server",push-by-digest=true,push=${{ fromJson(needs.setup.outputs.tags).needs_containers == true }}
build-args: |
GIT_HASH=${{ github.sha }}
GIT_VERSION=${{ needs.setup.outputs.version }}
GITHUB_RUN_ID=${{ github.run_id }}
- name: Export digests
run: |
docker run --rm ghcr.io/linz/basemaps/cli:${{ steps.version.outputs.version }} --help
mkdir -p ${{ runner.temp }}/digests/cli ${{ runner.temp }}/digests/server
digest="${{ steps.build_cli.outputs.digest }}"
touch "${{ runner.temp }}/digests/cli/${digest#sha256:}"
- name: '@basemaps/cli - Build and push'
uses: docker/build-push-action@v5
with:
context: packages/cli
platforms: linux/arm64,linux/amd64
tags: |
ghcr.io/linz/basemaps/cli:latest
ghcr.io/linz/basemaps/cli:${{ steps.version.outputs.version }}
push: ${{github.ref == 'refs/heads/master' && startsWith(github.event.head_commit.message, 'release:') == false}}
digest="${{ steps.build_server.outputs.digest }}"
touch "${{ runner.temp }}/digests/server/${digest#sha256:}"
- name: '@basemaps/cli - Build and push Major/Minor'
uses: docker/build-push-action@v5
- name: Upload digest
uses: actions/upload-artifact@v4
with:
context: packages/cli
platforms: linux/arm64,linux/amd64
# Publish :v6 and :v6.38 tags when publishing a release
tags: |
ghcr.io/linz/basemaps/cli:latest
ghcr.io/linz/basemaps/cli:${{ steps.version.outputs.version_major }}
ghcr.io/linz/basemaps/cli:${{ steps.version.outputs.version_major_minor }}
ghcr.io/linz/basemaps/cli:${{ steps.version.outputs.version }}
push: ${{github.ref == 'refs/heads/master' && startsWith(github.event.head_commit.message, 'release:')}}

- name: '@basemaps/server - Build and export to Docker'
uses: docker/build-push-action@v5
with:
context: packages/server
load: true
tags: |
ghcr.io/linz/basemaps/server:latest
ghcr.io/linz/basemaps/server:${{ steps.version.outputs.version }}
- name: '@basemaps/server - Test'
run: |
docker run --rm ghcr.io/linz/basemaps/server:${{ steps.version.outputs.version }} --version
name: digests-${{ matrix.arch }}
path: ${{ runner.temp }}/digests/*
if-no-files-found: error
retention-days: 1

merge:
# Find all the containers built from the matrix jobs then tag and publish them
name: Merge and publish containers

if: ${{ fromJson(needs.setup.outputs.tags).needs_containers == true }}

- name: '@basemaps/server - Build and push'
uses: docker/build-push-action@v5
permissions:
id-token: write
contents: read
packages: write

runs-on: ubuntu-latest

needs:
- build-containers
- setup

steps:
- name: Download digests
uses: actions/download-artifact@v4
with:
context: packages/server
platforms: linux/arm64,linux/amd64
tags: |
ghcr.io/linz/basemaps/server:latest
ghcr.io/linz/basemaps/server:${{ steps.version.outputs.version }}
push: ${{github.ref == 'refs/heads/master' && startsWith(github.event.head_commit.message, 'release:') == false}}
path: ${{ runner.temp }}/digests
pattern: digests-*
merge-multiple: true

- name: Show digests
working-directory: ${{ runner.temp }}/digests
run: ls -R .

- name: '@basemaps/server - Build and push Major/Minor'
uses: docker/build-push-action@v5
- name: Login to GHCR
uses: docker/login-action@v3
with:
context: packages/server
platforms: linux/arm64,linux/amd64
# Publish :v6 and :v6.38 tags when publishing a release
tags: |
ghcr.io/linz/basemaps/server:latest
ghcr.io/linz/basemaps/server:${{ steps.version.outputs.version_major }}
ghcr.io/linz/basemaps/server:${{ steps.version.outputs.version_major_minor }}
ghcr.io/linz/basemaps/server:${{ steps.version.outputs.version }}
push: ${{github.ref == 'refs/heads/master' && startsWith(github.event.head_commit.message, 'release:')}}

registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Create manifest list and push
working-directory: ${{ runner.temp }}/digests
run: |
cd cli
docker buildx imagetools create $(jq -cr '. | map("-t " + .) | join(" ")' <<< '${{ fromJson(needs.setup.outputs.tags).cli }}') \
$(printf 'ghcr.io/${{ github.repository }}/cli@sha256:%s ' *)
cd ../server/
docker buildx imagetools create $(jq -cr '. | map("-t " + .) | join(" ")' <<< '${{ fromJson(needs.setup.outputs.tags).server }}') \
$(printf 'ghcr.io/${{ github.repository }}/server@sha256:%s ' *)
67 changes: 0 additions & 67 deletions .github/workflows/pull-request-container.yml

This file was deleted.

Loading

0 comments on commit 724d580

Please sign in to comment.