Skip to content

Build Runner Images #26

Build Runner Images

Build Runner Images #26

name: Build Runner Images
on:
schedule:
- cron: "0 3 * * 0" # Weekly on Sunday at 3 AM UTC - cleanup orphaned images
workflow_dispatch:
inputs:
version:
description: "Development version tag (must start with 'dev-', e.g., dev-test, dev-feature-x)"
required: true
default: "dev-"
target:
description: "Build target"
required: true
default: "all"
type: choice
options:
- github-runner
- gitea-runner
- gitlab-runner
- all
push:
tags:
- "ubuntu-24.04-github/*" # Matches: ubuntu-24.04-github/2.322.0-1
- "ubuntu-24.04-gitea/*" # Matches: ubuntu-24.04-gitea/0.2.11-1
- "ubuntu-24.04-gitlab/*" # Matches: ubuntu-24.04-gitlab/18.7.0-1
branches:
- main
paths:
- "images/docker/ubuntu-24.04/**"
env:
REGISTRY: ghcr.io
IMAGE_BASE: ${{ github.repository_owner }}/fireactions-images
jobs:
# Determine what to build based on trigger and changed paths
prepare:
runs-on: ubuntu-latest
if: github.event_name != 'schedule'
outputs:
targets: ${{ steps.set-targets.outputs.targets }}
version: ${{ steps.set-targets.outputs.version }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Detect changed paths
if: github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/')
uses: dorny/paths-filter@v3
id: changes
with:
filters: |
shared:
- 'images/docker/ubuntu-24.04/Dockerfile'
- 'images/docker/ubuntu-24.04/overlay/common/**'
github:
- 'images/docker/ubuntu-24.04/overlay/github/**'
gitea:
- 'images/docker/ubuntu-24.04/overlay/gitea/**'
gitlab:
- 'images/docker/ubuntu-24.04/overlay/gitlab/**'
docs_only:
- 'images/docker/ubuntu-24.04/*.md'
- name: Validate manual dispatch version
if: github.event_name == 'workflow_dispatch'
run: |
VERSION="${{ github.event.inputs.version }}"
# Enforce 'dev-*' pattern for manual dispatch
if [[ ! "${VERSION}" =~ ^dev-.+ ]]; then
echo "::error::Manual dispatch versions must start with 'dev-' (e.g., dev-test, dev-feature-x). Release versions should use git tags."
exit 1
fi
echo "✅ Version '${VERSION}' is valid (dev build)"
- name: Determine build targets
id: set-targets
run: |
# Determine which targets to build
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
# Manual dispatch - use user selection
VERSION="${{ github.event.inputs.version }}"
TARGET="${{ github.event.inputs.target }}"
elif [[ "${{ github.ref }}" == refs/tags/ubuntu-24.04-github/* ]]; then
TAG="${GITHUB_REF#refs/tags/}"
VERSION="${TAG#ubuntu-24.04-github/}"
TARGET="github-runner"
elif [[ "${{ github.ref }}" == refs/tags/ubuntu-24.04-gitea/* ]]; then
TAG="${GITHUB_REF#refs/tags/}"
VERSION="${TAG#ubuntu-24.04-gitea/}"
TARGET="gitea-runner"
elif [[ "${{ github.ref }}" == refs/tags/ubuntu-24.04-gitlab/* ]]; then
TAG="${GITHUB_REF#refs/tags/}"
VERSION="${TAG#ubuntu-24.04-gitlab/}"
TARGET="gitlab-runner"
else
# Push to main - smart detection based on changed paths
VERSION="latest"
if [[ "${{ steps.changes.outputs.shared }}" == "true" ]]; then
# Shared files changed (Dockerfile or common overlay) - build all
echo "📦 Shared files changed - building all targets"
TARGET="all"
elif [[ "${{ steps.changes.outputs.docs_only }}" == "true" && \
"${{ steps.changes.outputs.github }}" != "true" && \
"${{ steps.changes.outputs.gitea }}" != "true" && \
"${{ steps.changes.outputs.gitlab }}" != "true" ]]; then
# Only docs changed - skip builds
echo "📝 Only documentation changed - skipping builds"
TARGET="none"
else
# Build only changed targets
TARGETS_ARRAY=()
[[ "${{ steps.changes.outputs.github }}" == "true" ]] && TARGETS_ARRAY+=("github-runner")
[[ "${{ steps.changes.outputs.gitea }}" == "true" ]] && TARGETS_ARRAY+=("gitea-runner")
[[ "${{ steps.changes.outputs.gitlab }}" == "true" ]] && TARGETS_ARRAY+=("gitlab-runner")
if [[ ${#TARGETS_ARRAY[@]} -eq 0 ]]; then
echo "🤷 No relevant changes detected - skipping builds"
TARGET="none"
else
echo "🎯 Building changed targets: ${TARGETS_ARRAY[*]}"
# Build JSON array directly
printf -v TARGETS_JSON '"%s",' "${TARGETS_ARRAY[@]}"
TARGETS="[${TARGETS_JSON%,}]"
echo "targets=${TARGETS}" >> $GITHUB_OUTPUT
echo "version=${VERSION}" >> $GITHUB_OUTPUT
exit 0
fi
fi
fi
# Build JSON array of targets
if [[ "${TARGET}" == "none" ]]; then
TARGETS='[]'
elif [[ "${TARGET}" == "all" ]]; then
TARGETS='["github-runner","gitea-runner","gitlab-runner"]'
else
TARGETS='["'"${TARGET}"'"]'
fi
echo "targets=${TARGETS}" >> $GITHUB_OUTPUT
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "Building targets: ${TARGETS} with version: ${VERSION}"
# Build each target image on native runners for each architecture
build:
needs: prepare
if: needs.prepare.outputs.targets != '[]'
runs-on: ${{ matrix.runner }}
permissions:
contents: read
packages: write
strategy:
fail-fast: false
matrix:
target: ${{ fromJson(needs.prepare.outputs.targets) }}
arch:
- amd64
- arm64
include:
- arch: amd64
runner: ubuntu-latest
platform: linux/amd64
- arch: arm64
runner: ubuntu-24.04-arm
platform: linux/arm64
steps:
- name: Set variables
id: vars
run: |
VERSION="${{ needs.prepare.outputs.version }}"
TARGET="${{ matrix.target }}"
# Map target to image name: github-runner -> ubuntu-24.04-github
case "${TARGET}" in
github-runner) IMAGE_SUFFIX="ubuntu-24.04-github" ;;
gitea-runner) IMAGE_SUFFIX="ubuntu-24.04-gitea" ;;
gitlab-runner) IMAGE_SUFFIX="ubuntu-24.04-gitlab" ;;
esac
IMAGE_NAME="${{ env.REGISTRY }}/${{ env.IMAGE_BASE }}/${IMAGE_SUFFIX}"
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "image_name=${IMAGE_NAME}" >> $GITHUB_OUTPUT
echo "image_suffix=${IMAGE_SUFFIX}" >> $GITHUB_OUTPUT
echo "target=${TARGET}" >> $GITHUB_OUTPUT
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push by digest
id: build
uses: docker/build-push-action@v6
with:
context: images/docker/ubuntu-24.04
target: ${{ matrix.target }}
platforms: ${{ matrix.platform }}
push: true
pull: true # Always pull fresh base images (fireteact:latest, fireactions, ubuntu)
outputs: type=image,name=${{ steps.vars.outputs.image_name }},push-by-digest=true,name-canonical=true
cache-from: type=gha,scope=${{ matrix.target }}-${{ matrix.arch }}
cache-to: type=gha,mode=max,scope=${{ matrix.target }}-${{ matrix.arch }}
labels: |
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
org.opencontainers.image.revision=${{ github.sha }}
org.opencontainers.image.version=${{ steps.vars.outputs.version }}
org.opencontainers.image.title=${{ steps.vars.outputs.image_suffix }}
- name: Export digest
run: |
mkdir -p /tmp/digests
digest="${{ steps.build.outputs.digest }}"
touch "/tmp/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v4
with:
name: digests-${{ matrix.target }}-${{ matrix.arch }}
path: /tmp/digests/*
if-no-files-found: error
retention-days: 1
# Create multi-arch manifests for each target
manifest:
needs: [prepare, build]
if: needs.prepare.outputs.targets != '[]'
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
strategy:
matrix:
target: ${{ fromJson(needs.prepare.outputs.targets) }}
steps:
- name: Set variables
id: vars
run: |
VERSION="${{ needs.prepare.outputs.version }}"
TARGET="${{ matrix.target }}"
# Map target to image name: github-runner -> ubuntu-24.04-github
case "${TARGET}" in
github-runner) IMAGE_SUFFIX="ubuntu-24.04-github" ;;
gitea-runner) IMAGE_SUFFIX="ubuntu-24.04-gitea" ;;
gitlab-runner) IMAGE_SUFFIX="ubuntu-24.04-gitlab" ;;
esac
IMAGE_NAME="${{ env.REGISTRY }}/${{ env.IMAGE_BASE }}/${IMAGE_SUFFIX}"
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "image_name=${IMAGE_NAME}" >> $GITHUB_OUTPUT
echo "image_suffix=${IMAGE_SUFFIX}" >> $GITHUB_OUTPUT
echo "target=${TARGET}" >> $GITHUB_OUTPUT
- name: Download digests
uses: actions/download-artifact@v4
with:
path: /tmp/digests
pattern: digests-${{ matrix.target }}-*
merge-multiple: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Create manifest list and push
working-directory: /tmp/digests
run: |
IMAGE_NAME="${{ steps.vars.outputs.image_name }}"
VERSION="${{ steps.vars.outputs.version }}"
# Build tags array
TAGS="-t ${IMAGE_NAME}:${VERSION}"
if [[ "${VERSION}" != "latest" ]]; then
TAGS="${TAGS} -t ${IMAGE_NAME}:latest"
fi
# Create and push manifest
docker buildx imagetools create ${TAGS} \
$(printf "${IMAGE_NAME}@sha256:%s " *)
- name: Inspect image
run: |
docker buildx imagetools inspect ${{ steps.vars.outputs.image_name }}:${{ steps.vars.outputs.version }}
- name: Summary
run: |
echo "## ${{ steps.vars.outputs.image_suffix }} Image Published" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Image:** \`${{ steps.vars.outputs.image_name }}\`" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Tags:**" >> $GITHUB_STEP_SUMMARY
echo "- \`${{ steps.vars.outputs.version }}\`" >> $GITHUB_STEP_SUMMARY
if [[ "${{ steps.vars.outputs.version }}" != "latest" ]]; then
echo "- \`latest\`" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Architectures:** \`amd64\`, \`arm64\`" >> $GITHUB_STEP_SUMMARY
# Cleanup orphaned images (runs weekly on schedule)
cleanup:
runs-on: ubuntu-latest
if: github.event_name == 'schedule'
permissions:
packages: write
strategy:
matrix:
package:
- fireactions-images/ubuntu-24.04-github
- fireactions-images/ubuntu-24.04-gitea
- fireactions-images/ubuntu-24.04-gitlab
steps:
- name: Delete orphaned images
uses: dataaxiom/ghcr-cleanup-action@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
package: ${{ matrix.package }}
delete-untagged: true
delete-ghost-images: true
delete-partial-images: true
older-than: 3 days
keep-n-tagged: 5
- name: Summary
run: |
echo "## GHCR Cleanup Completed" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Removed orphaned/untagged images from \`${{ env.REGISTRY }}/${{ matrix.package }}\`" >> $GITHUB_STEP_SUMMARY