ci: multi-arch Docker via native runners + Node 24 opt-in + PAT for tag-trigger chain

docker-publish.yml:
  - Rewrote as matrix build + manifest merge.
  - amd64 on ubuntu-latest, arm64 on ubuntu-24.04-arm (free for public
    repos). No QEMU — argon2 and every other native dep compile on
    their target CPU, no more SIGILL / exit 132.
  - Per-arch GHA cache scopes so builds don't thrash each other.
  - Final step merges both digests under one tag (vX.Y.Z + latest),
    publishing a real multi-arch manifest. `docker pull` from either
    arch gets the right variant automatically.

auto-version.yml, version-bump.yml:
  - Checkout now uses `secrets.RELEASE_PAT || secrets.GITHUB_TOKEN`.
    With RELEASE_PAT set, the tag push this workflow does DOES
    trigger downstream (android-release, docker-publish). Without
    it, falls back to GITHUB_TOKEN (no downstream trigger, what we
    have today).

All workflows (auto-version, version-bump, android-release,
docker-publish):
  - Added FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true' so actions
    still on Node 20 runtime (checkout/cache/setup-*) opt in to
    Node 24 early. GitHub makes Node 24 default 2026-06-02 and
    removes Node 20 2026-09-16.

To finish the chain (one-time user step): create a fine-grained
PAT with "Contents: Read and write" on this repo and add as
RELEASE_PAT secret. After that `feat:` / `fix:` commits auto-tag
AND auto-build with zero manual intervention.
This commit is contained in:
Daniel 2026-04-15 00:05:34 +02:00
parent f6397ae635
commit 9fa16e6192
4 changed files with 119 additions and 27 deletions

View file

@ -13,6 +13,9 @@ on:
description: 'Manual tag to build (e.g. v6.1.1)'
required: true
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true'
permissions:
contents: write # needed to create GitHub releases from the runner

View file

@ -21,6 +21,10 @@ on:
push:
branches: [main]
# Opt in to Node 24 runtime early (deprecation of Node 20 begins 2026-06-02)
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true'
permissions:
contents: write
@ -33,7 +37,14 @@ jobs:
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
# Use RELEASE_PAT (a Personal Access Token you add as a repo
# secret) so the tag push this workflow performs actually
# triggers the downstream tag-based workflows (android-release,
# docker-publish). GITHUB_TOKEN pushes are deliberately
# blocked from triggering other workflows by GitHub.
# Fine-grained PAT with "Contents: Read and write" on this
# repo is enough.
token: ${{ secrets.RELEASE_PAT || secrets.GITHUB_TOKEN }}
- name: Find last semver tag
id: last

View file

@ -1,27 +1,51 @@
name: Build & Push Docker Image
# Multi-arch build using NATIVE runners for each platform, then a
# manifest-list push. No QEMU emulation — amd64 builds on x86 runner,
# arm64 builds on ubuntu-24.04-arm runner. argon2 and every other
# native dep compile natively on their target arch.
#
# Result: `danielonyejesi/pediatric-ai-scribe-v3:X.Y.Z` (and :latest)
# is one tag serving the correct variant to amd64 or arm64 hosts.
on:
push:
tags: ['v*']
workflow_dispatch:
inputs:
tag:
description: 'Docker image tag (e.g. v8, latest)'
description: 'Tag to publish (e.g. v6.2.0)'
required: false
default: 'latest'
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true'
IMAGE: danielonyejesi/pediatric-ai-scribe-v3
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
# Build one variant per matrix entry, push by digest only.
name: Build ${{ matrix.platform }}
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- platform: linux/amd64
runner: ubuntu-latest
- platform: linux/arm64
runner: ubuntu-24.04-arm
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
- name: Docker metadata (for labels)
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.IMAGE }}
- name: Set up Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
@ -30,8 +54,56 @@ jobs:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Extract version tag
id: meta
- name: Build & push by digest
id: build
uses: docker/build-push-action@v5
with:
context: .
platforms: ${{ matrix.platform }}
labels: ${{ steps.meta.outputs.labels }}
outputs: type=image,name=${{ env.IMAGE }},push-by-digest=true,name-canonical=true,push=true
cache-from: type=gha,scope=${{ matrix.platform }}
cache-to: type=gha,mode=max,scope=${{ matrix.platform }}
- name: Export digest for the merge job
run: |
mkdir -p /tmp/digests
DIG="${{ steps.build.outputs.digest }}"
touch "/tmp/digests/${DIG#sha256:}"
- name: Upload digest artifact
uses: actions/upload-artifact@v4
with:
name: digests-${{ matrix.platform == 'linux/amd64' && 'amd64' || 'arm64' }}
path: /tmp/digests/*
if-no-files-found: error
retention-days: 1
merge:
# Combine the two single-platform digests into one multi-arch manifest
# published under the real tags (vX.Y.Z and latest).
name: Merge manifests
needs: build
runs-on: ubuntu-latest
steps:
- name: Download digests
uses: actions/download-artifact@v4
with:
path: /tmp/digests
pattern: digests-*
merge-multiple: true
- name: Set up 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: Resolve tag
id: tag
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "tag=${{ github.event.inputs.tag || 'latest' }}" >> $GITHUB_OUTPUT
@ -39,24 +111,27 @@ jobs:
echo "tag=${GITHUB_REF_NAME}" >> $GITHUB_OUTPUT
fi
- name: Build and Push
uses: docker/build-push-action@v5
- name: Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
context: .
push: true
images: ${{ env.IMAGE }}
tags: |
danielonyejesi/pediatric-ai-scribe-v3:${{ steps.meta.outputs.tag }}
danielonyejesi/pediatric-ai-scribe-v3:latest
cache-from: type=gha
cache-to: type=gha,mode=max
# x86 only — argon2 native build fails under QEMU ARM64
# emulation on a GitHub-hosted amd64 runner (SIGILL / exit 132).
# Add linux/arm64 back with a native ARM runner when/if you
# actually deploy to ARM hardware.
platforms: linux/amd64
type=raw,value=${{ steps.tag.outputs.tag }}
type=raw,value=latest
- name: Create manifest list & push
working-directory: /tmp/digests
run: |
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf "${{ env.IMAGE }}@sha256:%s " *)
- name: Inspect final image
run: docker buildx imagetools inspect ${{ env.IMAGE }}:${{ steps.tag.outputs.tag }}
- name: Summary
run: |
echo "### Docker image published" >> $GITHUB_STEP_SUMMARY
echo "- \`danielonyejesi/pediatric-ai-scribe-v3:${{ steps.meta.outputs.tag }}\`" >> $GITHUB_STEP_SUMMARY
echo "- \`danielonyejesi/pediatric-ai-scribe-v3:latest\`" >> $GITHUB_STEP_SUMMARY
echo "### Multi-arch image published" >> $GITHUB_STEP_SUMMARY
echo "- \`${{ env.IMAGE }}:${{ steps.tag.outputs.tag }}\`" >> $GITHUB_STEP_SUMMARY
echo "- \`${{ env.IMAGE }}:latest\`" >> $GITHUB_STEP_SUMMARY
echo "- Platforms: linux/amd64, linux/arm64 (built on native runners)" >> $GITHUB_STEP_SUMMARY

View file

@ -22,6 +22,9 @@ on:
description: 'Or exact version (e.g. 7.0.0) — overrides bump'
required: false
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true'
permissions:
contents: write
@ -33,7 +36,7 @@ jobs:
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
token: ${{ secrets.RELEASE_PAT || secrets.GITHUB_TOKEN }}
- name: Compute next version
id: v