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>
117 lines
3.8 KiB
YAML
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
|