Skip to content

Build Bluefin dakota (aarch64) #204

Build Bluefin dakota (aarch64)

Build Bluefin dakota (aarch64) #204

Workflow file for this run

name: Build Bluefin dakota (aarch64)
# Standalone aarch64 build — completely decoupled from x86_64.
#
# Design invariants:
# - ARM NEVER blocks an x86_64 build, publish, promote, or release.
# - No job in publish.yml, promote-testing-to-main.yml, or execute-release.yml
# depends on this workflow. Decoupling is structural, not just continue-on-error.
# - No remote execution (no RE service for ARM); enable-push: true so artifacts
# land in the remote CAS for subsequent builds and warm-cache runs.
#
# Triggers:
# - workflow_run: fires after x86_64 publish completes on testing — ARM builds
# after x86 CAS writes are done, avoiding CAS write contention
# - workflow_dispatch: manual recovery / on-demand
#
# Published tags (when qualifying event on testing/main):
# :aarch64 — latest successful aarch64 build from testing
# :aarch64-<sha> — immutable per-commit tag
on:
# Triggered after x86_64 publish completes — ARM builds after x86 CAS writes
# are done, avoiding CAS write contention.
workflow_run:
workflows: ["Publish Bluefin dakota"]
types: [completed]
branches: [testing]
workflow_dispatch:
permissions: read-all
env:
IMAGE_NAME: dakota
IMAGE_REGISTRY: ghcr.io/${{ github.repository_owner }}
# just export/lint tag the image as {image_name}:{image_tag}. Without this the
# export lands at dakota:latest while the push step looks for dakota:aarch64
# ("image not known", exit 125) — masked by job-level continue-on-error.
BUILD_IMAGE_TAG: aarch64
# All BuildStream cache writers share one CAS serialization group. ARM remains
# decoupled from x86 publication and release, but cannot contend for CAS writes.
concurrency:
group: dakota-bst-build-global
cancel-in-progress: false
jobs:
build-aarch64:
name: Build OCI image (aarch64)
runs-on: ubuntu-24.04-arm
# Skip if triggered by workflow_run but the upstream publish failed.
if: >
github.event_name != 'workflow_run' ||
github.event.workflow_run.conclusion == 'success'
# ARM failures are non-blocking and never affect x86_64 publication.
continue-on-error: true
timeout-minutes: 420
outputs:
pushed: ${{ steps.push.outputs.pushed }}
sha: ${{ steps.context.outputs.sha }}
aarch64_digest: ${{ steps.push.outputs.digest }}
permissions:
contents: read
packages: write
id-token: write
attestations: write
steps:
- name: Resolve build context
id: context
run: |
echo "sha=${{ github.sha }}" >> "$GITHUB_OUTPUT"
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- name: Setup runner
uses: projectbluefin/actions/bootc-build/setup-runner@v1
with:
storage-backend: btrfs
update-podman: true
native-overlay: true
install-tools: '["just"]'
- name: Capture build timestamp
id: timestamp
run: echo "created=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_OUTPUT"
- name: Restore BST workspace cache (aarch64)
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
with:
path: ~/.cache/buildstream
key: bst-aarch64-${{ github.ref_name }}-${{ hashFiles('elements/gnome-build-meta.bst', 'elements/freedesktop-sdk.bst', 'Justfile') }}
restore-keys: |
bst-aarch64-${{ github.ref_name }}-
bst-aarch64-
bst-warm-aarch64-
# No remote execution for ARM yet; enable-push so artifacts land in CAS
# for subsequent builds and warm-cache runs.
- name: Generate BuildStream CI config
env:
CASD_CLIENT_CERT: ${{ vars.CASD_CLIENT_CERT }}
CASD_CLIENT_KEY: ${{ secrets.CASD_CLIENT_KEY }}
uses: ./.github/actions/generate-bst-ci-config
with:
enable-remote-execution: 'false'
enable-push: 'true'
- name: Build OCI image with BuildStream (aarch64)
env:
BST_FLAGS: --no-interactive --config /src/buildstream-ci.conf --option arch aarch64
run: just bst build oci/bluefin.bst
timeout-minutes: 390
- name: Export OCI image from BuildStream
id: export
env:
BST_FLAGS: --no-interactive --config /src/buildstream-ci.conf --option arch aarch64
BUILD_IMAGE_NAME: ${{ env.IMAGE_NAME }}
OCI_IMAGE_CREATED: ${{ steps.timestamp.outputs.created }}
OCI_IMAGE_REVISION: ${{ github.sha }}
OCI_IMAGE_VERSION: aarch64
run: |
just export
echo "image_ref=${{ env.IMAGE_NAME }}:aarch64" >> "$GITHUB_OUTPUT"
- name: Validate with bootc container lint
run: just lint
- name: Upload build logs
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: buildstream-logs-aarch64-${{ github.sha }}
path: logs/
retention-days: 7
if-no-files-found: ignore
- name: Login to GHCR
if: >-
github.event_name == 'push' ||
github.event_name == 'workflow_run' ||
github.event_name == 'workflow_dispatch'
env:
GH_ACTOR: ${{ github.actor }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo "$GH_TOKEN" | sudo podman login ghcr.io --username "$GH_ACTOR" --password-stdin
mkdir -p ~/.docker
echo "$GH_TOKEN" | podman login ghcr.io --username "$GH_ACTOR" --password-stdin \
--compat-auth-file ~/.docker/config.json
- name: Push to GHCR
id: push
if: >-
github.event_name == 'push' ||
github.event_name == 'workflow_run' ||
github.event_name == 'workflow_dispatch'
env:
BUILD_SHA: ${{ github.sha }}
run: |
IMAGE="${{ env.IMAGE_REGISTRY }}/${{ env.IMAGE_NAME }}"
LOCAL="localhost/${{ steps.export.outputs.image_ref }}"
# Tag the immutable SHA-pinned ref first; push it with --digestfile so
# the captured digest matches exactly what was uploaded. The floating
# :aarch64 tag is pushed afterwards and points at the same manifest.
sudo podman tag "${LOCAL}" "${IMAGE}:aarch64-${BUILD_SHA}"
sudo podman tag "${LOCAL}" "${IMAGE}:aarch64"
PUSH_OK=false
for attempt in 1 2 3; do
sudo podman push --compression-format=zstd \
--digestfile "${RUNNER_TEMP}/digest.txt" \
"${IMAGE}:aarch64-${BUILD_SHA}" && { PUSH_OK=true; break; }
echo "Push attempt ${attempt} failed for :aarch64-${BUILD_SHA}, retrying in 5s..."
[ "$attempt" -lt 3 ] && sleep 5
done
if [ "$PUSH_OK" != "true" ]; then
echo "ERROR: all push attempts failed for :aarch64-${BUILD_SHA} — aarch64 build not signed/published"
echo "pushed=false" >> "$GITHUB_OUTPUT"
exit 1
fi
DIGEST="$(cat "${RUNNER_TEMP}/digest.txt")"
echo "digest=${DIGEST}" >> "$GITHUB_OUTPUT"
# Floating :aarch64 — best-effort, never blocks signing of the pinned tag.
for attempt in 1 2 3; do
sudo podman push --compression-format=zstd \
"${IMAGE}:aarch64" && break
echo "Push attempt ${attempt} failed for :aarch64, retrying in 5s..."
[ "$attempt" -lt 3 ] && sleep 5
done
echo "pushed=true" >> "$GITHUB_OUTPUT"
- name: Install oras
if: steps.push.outputs.pushed == 'true'
uses: oras-project/setup-oras@1d808f7d7f6995cc68b7bf507bfe5c5446e1dc9d # v2.0.1
- name: Sign image and create attestation
# Mirrors publish.yml exactly. generate-sbom: false — dakota uses BST-native
# provenance (just sbom) handled separately; Syft post-build scan is less
# accurate for BST builds.
if: steps.push.outputs.pushed == 'true'
uses: projectbluefin/actions/bootc-build/sign-and-publish@v1
with:
image: ${{ env.IMAGE_REGISTRY }}/${{ env.IMAGE_NAME }}
digest: ${{ steps.push.outputs.digest }}
generate-sbom: false
github-token: ${{ secrets.GITHUB_TOKEN }}