Files
maas-proxmox/.gitea/workflows/build-image.yml
ilkermanap 4719d30359 Rebuild on kernel changes and on age, not just on pve-manager
The build condition only compared pve-manager, which left two gaps. Kernel
security fixes ship in proxmox-default-kernel and do not bump pve-manager, so the
updates that matter most would not have triggered a rebuild. Debian base security
updates bump neither, so an image could have sat unchanged indefinitely while its
openssl and glibc went stale.

scripts/ci/decide-build.sh now rebuilds when pve-manager changes, when
proxmox-default-kernel changes, or when the newest release passes MAX_AGE_DAYS
(30). It compares against image-info.txt from the last release rather than
inferring from tag names, so the comparison reflects what is actually inside the
published image, and it can be run by hand to see the decision without triggering
anything.

The daily schedule stays. Measured from the trixie repository, Proxmox publishes
about weekly — 56 pve-manager and 28 proxmox-kernel versions since 9.0 — so daily
checking costs about 30 seconds on the days nothing changed and cuts worst-case
staleness from a week to a day. Tags carry the date now (pve-<version>-<date>)
because an age-triggered rebuild can repeat a version.

The README gains a "Prebuilt images" section linking the releases page, with the
checksum and MAAS upload commands, and a "Release automation" section explaining
the trigger table and stating plainly that the host-mode runner gives root on the
build machine to anything that can dispatch a workflow.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 22:59:10 +02:00

117 lines
3.8 KiB
YAML

# Copyright (C) 2026 Ilker Manap
# SPDX-License-Identifier: AGPL-3.0-or-later
#
# Builds the MAAS image and publishes it as a Gitea release.
#
# Runs daily, but only *builds* when the Proxmox repository actually carries a
# newer pve-manager than the last published release. Proxmox does not ship daily,
# so an unconditional daily build would produce ~45 GB a month of near-identical
# artifacts for nothing. Trigger manually with `force` to rebuild anyway.
#
# The runner is registered in host mode: steps run directly on the build machine
# as root, because the build needs /dev/kvm, qemu-nbd, FUSE and root privileges.
# Anything that can dispatch a workflow here therefore has root on that machine.
name: build-image
on:
schedule:
- cron: '0 3 * * *'
workflow_dispatch:
inputs:
force:
description: 'Build even if the version has not changed'
type: boolean
default: false
concurrency:
group: build-image
cancel-in-progress: false
jobs:
build:
runs-on: maas-builder
timeout-minutes: 120
env:
GITEA_API: ${{ github.server_url }}/api/v1/repos/${{ github.repository }}
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
KEEP_RELEASES: '3'
# Rebuild even without a Proxmox version change once an image reaches this
# age, so Debian base security updates make it into the image.
MAX_AGE_DAYS: '30'
steps:
- name: Check out
# A plain clone rather than actions/checkout: the runner is in host mode
# and has no Node.js runtime for JavaScript actions.
run: |
set -eux
# Step out of the workspace before removing it: the shell starts *in*
# $GITHUB_WORKSPACE, and deleting the current working directory makes
# every later command fail with "Unable to read current working directory".
cd /
rm -rf "$GITHUB_WORKSPACE"
git clone --depth 1 --branch "$GITHUB_REF_NAME" \
"$GITHUB_SERVER_URL/$GITHUB_REPOSITORY.git" "$GITHUB_WORKSPACE"
cd "$GITHUB_WORKSPACE" && git log --oneline -1
- name: Decide whether to build
id: decide
run: |
set -eu
cd "$GITHUB_WORKSPACE"
./scripts/ci/decide-build.sh "$MAX_AGE_DAYS" > "$GITHUB_OUTPUT"
if [ "${{ inputs.force }}" = "true" ]; then
echo "forced by manual trigger"
sed -i 's/^build=no$/build=yes/' "$GITHUB_OUTPUT"
fi
cat "$GITHUB_OUTPUT"
- name: Build
if: steps.decide.outputs.build == 'yes'
run: |
set -eux
cd "$GITHUB_WORKSPACE"
make image
- name: Verify
if: steps.decide.outputs.build == 'yes'
run: |
set -eux
cd "$GITHUB_WORKSPACE"
make verify
- name: Assemble release artifacts
if: steps.decide.outputs.build == 'yes'
run: |
set -eu
cd "$GITHUB_WORKSPACE"
./scripts/ci/assemble-artifacts.sh "${{ steps.decide.outputs.version }}"
- name: Publish
if: steps.decide.outputs.build == 'yes'
run: |
set -eu
cd "$GITHUB_WORKSPACE"
./scripts/ci/publish-release.sh \
"${{ steps.decide.outputs.tag }}" \
"${{ steps.decide.outputs.version }}" \
dist
- name: Prune old releases
if: steps.decide.outputs.build == 'yes'
run: |
set -eu
cd "$GITHUB_WORKSPACE"
./scripts/ci/prune-releases.sh "$KEEP_RELEASES"
- name: Clean up
if: always()
run: |
# A 1.5 GB artifact per run would fill the builder otherwise.
# Runs even when an earlier step left the workspace missing, so cd out first.
cd /
rm -rf "$GITHUB_WORKSPACE/dist" "$GITHUB_WORKSPACE/build" || true
df -h / | tail -1