From 0d3941eb912d0de94ed7c940a961a70b14a199ae Mon Sep 17 00:00:00 2001 From: Mathias Beaulieu-Duncan Date: Fri, 13 Feb 2026 15:05:46 -0500 Subject: [PATCH] Add daily auto-update workflow and fix overlay dirty tag - Rewrite check-upstream.sh to parse RPi kernel version from patch file - Add auto-update.sh for automated version bumps with patch smoke test - Rewrite check-updates.yaml as daily auto-build with issue fallback - Update build.yaml release body to show Talos + kernel versions from tag - Fix overlay dirty tag: remove --dirty from SBCOVERLAY_TAG git describe (the sed rewrite of pkg.yaml is intentional, not an accidental change) Tag strategy: v{TALOS}-k{KERNEL}-{BUILD} (e.g. v1.12.3-k6.12.47-1) Co-Authored-By: Claude Opus 4.6 --- .gitea/workflows/build.yaml | 8 +- .gitea/workflows/check-updates.yaml | 181 ++++++++++++++-------------- Makefile | 2 +- scripts/auto-update.sh | 107 ++++++++++++++++ scripts/check-upstream.sh | 61 +++++----- 5 files changed, 234 insertions(+), 125 deletions(-) create mode 100755 scripts/auto-update.sh diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index bb1d6a3..6a6a3d3 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -96,10 +96,14 @@ jobs: SCOUT_SECTION=$(cat _out/scout-report.md) fi + # Extract component versions from tag (format: v1.12.3-k6.12.47-1) + TALOS_VER=$(echo "$TAG" | sed -E 's/^(v[0-9]+\.[0-9]+\.[0-9]+)-.*/\1/') + KERNEL_VER=$(echo "$TAG" | sed -E 's/.*-k([0-9]+\.[0-9]+\.[0-9]+)-.*/\1/') + RELEASE_BODY="Custom Talos Linux image for Raspberry Pi 5 / CM5 (Compute Blade) - **Talos version**: ${TAG} - **Kernel**: RPi downstream (CM5/RP1 support) + **Talos**: ${TALOS_VER} + **Kernel**: RPi downstream ${KERNEL_VER} (CM5/RP1 support) **Extensions**: iscsi-tools, util-linux-tools **Overclock**: 2.6GHz (arm_freq=2600) diff --git a/.gitea/workflows/check-updates.yaml b/.gitea/workflows/check-updates.yaml index 6a2a004..decd0d2 100644 --- a/.gitea/workflows/check-updates.yaml +++ b/.gitea/workflows/check-updates.yaml @@ -1,25 +1,32 @@ -# Check for upstream Talos and RPi kernel updates +# Daily upstream update check with auto-build # -# Runs on a schedule and creates a Gitea issue when new versions are found. -# This is notification-only — builds require manual tag push after verifying -# patches still apply. +# Detects new Talos OS and RPi kernel versions, applies updates, +# smoke-tests patches, and pushes a release tag (which triggers build.yaml). +# Falls back to creating a Gitea issue if patches fail to apply. name: Check Upstream Updates on: schedule: - # Run weekly on Monday at 08:00 UTC - - cron: '0 8 * * 1' + - cron: '0 8 * * *' # Daily at 08:00 UTC workflow_dispatch: jobs: - check-updates: + check-and-build: runs-on: [self-hosted, macos] - timeout-minutes: 10 + timeout-minutes: 15 steps: - name: Checkout uses: actions/checkout@v4 + with: + fetch-depth: 0 # Need full history for tag-based build numbering + + - name: Install dependencies + run: | + for pkg in make gnu-sed crane jq; do + brew list --formula "$pkg" &>/dev/null || brew install "$pkg" + done - name: Check for upstream updates id: check @@ -27,95 +34,83 @@ jobs: chmod +x scripts/check-upstream.sh scripts/check-upstream.sh >> "$GITHUB_OUTPUT" - - name: Create issue for Talos update - if: steps.check.outputs.talos_update == 'true' - uses: actions/github-script@v7 - with: - script: | - const currentVersion = '${{ steps.check.outputs.talos_current }}'; - const latestVersion = '${{ steps.check.outputs.talos_latest }}'; - const title = `Talos update available: ${currentVersion} → ${latestVersion}`; + - name: Run auto-update + if: steps.check.outputs.talos_update == 'true' || steps.check.outputs.rpi_update == 'true' + id: update + env: + TALOS_UPDATE: ${{ steps.check.outputs.talos_update }} + RPI_UPDATE: ${{ steps.check.outputs.rpi_update }} + LATEST_TALOS: ${{ steps.check.outputs.talos_latest }} + LATEST_RPI_TAG: ${{ steps.check.outputs.rpi_latest }} + run: | + chmod +x scripts/auto-update.sh + scripts/auto-update.sh >> "$GITHUB_OUTPUT" - // Check if an open issue already exists - const issues = await github.rest.issues.listForRepo({ - owner: context.repo.owner, - repo: context.repo.repo, - state: 'open', - labels: 'upstream-update', - }); + - name: Commit and tag + if: steps.update.outputs.patch_failed != 'true' && steps.update.outputs.new_tag != '' + env: + NEW_TAG: ${{ steps.update.outputs.new_tag }} + run: | + git config user.name "Gitea Actions" + git config user.email "actions@openharbor.io" + git add -A + git commit -m "Bump upstream: ${NEW_TAG}" + git tag "$NEW_TAG" + git push origin main --tags - const existing = issues.data.find(i => i.title.includes('Talos update')); - if (existing) { - console.log(`Issue already exists: #${existing.number}`); - return; - } + - name: Create issue on patch failure + if: steps.update.outputs.patch_failed == 'true' + env: + GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TALOS_CURRENT: ${{ steps.check.outputs.talos_current }} + TALOS_LATEST: ${{ steps.check.outputs.talos_latest }} + TALOS_UPDATE: ${{ steps.check.outputs.talos_update }} + RPI_CURRENT: ${{ steps.check.outputs.rpi_current }} + RPI_LATEST: ${{ steps.check.outputs.rpi_latest }} + RPI_UPDATE: ${{ steps.check.outputs.rpi_update }} + run: | + GITEA_URL="${GITHUB_SERVER_URL}" + REPO="${GITHUB_REPOSITORY}" + API="${GITEA_URL}/api/v1" - await github.rest.issues.create({ - owner: context.repo.owner, - repo: context.repo.repo, - title: title, - body: [ - `## Talos Update Available`, - ``, - `| | Version |`, - `|---|---|`, - `| Current | \`${currentVersion}\` |`, - `| Latest | \`${latestVersion}\` |`, - ``, - `### Steps`, - `1. Update \`TALOS_VERSION\` in \`Makefile\``, - `2. Verify patches still apply: \`make checkouts patches\``, - `3. If patches fail, port them to the new version`, - `4. Push a version tag to trigger the build pipeline`, - ``, - `### Links`, - `- [Talos Release Notes](https://github.com/siderolabs/talos/releases/tag/${latestVersion})`, - ].join('\n'), - labels: ['upstream-update', 'talos'], - }); + BODY="## Upstream update requires manual patch porting - - name: Create issue for RPi kernel update - if: steps.check.outputs.rpi_update == 'true' - uses: actions/github-script@v7 - with: - script: | - const currentVersion = '${{ steps.check.outputs.rpi_current }}'; - const latestVersion = '${{ steps.check.outputs.rpi_latest }}'; - const title = `RPi kernel update available: ${currentVersion} → ${latestVersion}`; + Automated patch application failed. Manual intervention needed. - const issues = await github.rest.issues.listForRepo({ - owner: context.repo.owner, - repo: context.repo.repo, - state: 'open', - labels: 'upstream-update', - }); + | Component | Current | Latest | Update? | + |-----------|---------|--------|---------| + | Talos | \`${TALOS_CURRENT}\` | \`${TALOS_LATEST}\` | ${TALOS_UPDATE} | + | RPi kernel | \`${RPI_CURRENT}\` | \`${RPI_LATEST}\` | ${RPI_UPDATE} | - const existing = issues.data.find(i => i.title.includes('RPi kernel update')); - if (existing) { - console.log(`Issue already exists: #${existing.number}`); - return; - } + ### Steps + 1. Check out this repo and run \`scripts/auto-update.sh\` to see what fails + 2. Port patches to the new upstream version + 3. Verify: \`gmake checkouts patches && gmake checkouts-clean\` + 4. Push changes — the next scheduled run will pick them up - await github.rest.issues.create({ - owner: context.repo.owner, - repo: context.repo.repo, - title: title, - body: [ - `## RPi Kernel Update Available`, - ``, - `| | Version |`, - `|---|---|`, - `| Current (in pkgs patch) | \`${currentVersion}\` |`, - `| Latest stable | \`${latestVersion}\` |`, - ``, - `### Steps`, - `1. Update the kernel version in the pkgs patch`, - `2. Verify the patch still applies: \`make checkouts patches\``, - `3. Test build: \`make kernel\``, - `4. Push a version tag to trigger the full build pipeline`, - ``, - `### Links`, - `- [RPi Linux Releases](https://github.com/raspberrypi/linux/tags)`, - ].join('\n'), - labels: ['upstream-update', 'kernel'], - }); + ### Links + - [Talos Releases](https://github.com/siderolabs/talos/releases) + - [RPi Linux Tags](https://github.com/raspberrypi/linux/tags)" + + # Strip leading whitespace from heredoc-style indentation + BODY=$(echo "$BODY" | sed 's/^ //') + BODY_JSON=$(jq -Rs '.' <<< "$BODY") + + # Check for existing open issue to avoid duplicates + EXISTING=$(curl -sf \ + -H "Authorization: token ${GITEA_TOKEN}" \ + "${API}/repos/${REPO}/issues?state=open&type=issues&labels=upstream-update" \ + | jq -r '[.[] | select(.title | contains("manual patch"))][0].id // empty') + + if [ -n "$EXISTING" ]; then + echo "Issue already exists (id: $EXISTING), skipping creation" + exit 0 + fi + + curl -sf -X POST \ + -H "Authorization: token ${GITEA_TOKEN}" \ + -H "Content-Type: application/json" \ + -d "{\"title\":\"Upstream update requires manual patch porting\",\"body\":${BODY_JSON},\"labels\":[\"upstream-update\"]}" \ + "${API}/repos/${REPO}/issues" + + echo "Created issue for manual patch porting" diff --git a/Makefile b/Makefile index f2985f0..887da70 100644 --- a/Makefile +++ b/Makefile @@ -45,7 +45,7 @@ PATCHES_DIRECTORY := $(PWD)/patches PKGS_TAG = $(shell cd $(CHECKOUTS_DIRECTORY)/pkgs && git describe --tag --always --dirty --match v[0-9]\*) TALOS_TAG = $(shell cd $(CHECKOUTS_DIRECTORY)/talos && git describe --tag --always --dirty --match v[0-9]\*) -SBCOVERLAY_TAG = $(shell cd $(CHECKOUTS_DIRECTORY)/sbc-raspberrypi5 && git describe --tag --always --dirty)-$(PKGS_TAG) +SBCOVERLAY_TAG = $(shell cd $(CHECKOUTS_DIRECTORY)/sbc-raspberrypi5 && git describe --tag --always)-$(PKGS_TAG) # Build the --system-extension-image flags from the EXTENSIONS list EXTENSION_FLAGS = $(foreach ext,$(EXTENSIONS),--system-extension-image=$(ext)) diff --git a/scripts/auto-update.sh b/scripts/auto-update.sh new file mode 100755 index 0000000..c5c5621 --- /dev/null +++ b/scripts/auto-update.sh @@ -0,0 +1,107 @@ +#!/usr/bin/env bash +# Auto-update upstream versions, verify patches, and generate a release tag +# +# Expects environment variables from check-upstream.sh: +# TALOS_UPDATE, RPI_UPDATE, LATEST_TALOS, LATEST_RPI_TAG +# +# Outputs (for GitHub Actions): +# patch_failed=true — if patches fail to apply (caller should create issue) +# new_tag= — the computed release tag (e.g. v1.12.3-k6.12.47-1) +# +# Usage: +# TALOS_UPDATE=true LATEST_TALOS=v1.13.0 ./scripts/auto-update.sh >> "$GITHUB_OUTPUT" + +set -euo pipefail + +TALOS_UPDATE=${TALOS_UPDATE:-false} +RPI_UPDATE=${RPI_UPDATE:-false} +LATEST_TALOS=${LATEST_TALOS:-} +LATEST_RPI_TAG=${LATEST_RPI_TAG:-} + +MAKEFILE="Makefile" +PATCH_FILE="patches/siderolabs/pkgs/0001-Patched-for-Raspberry-Pi-5.patch" + +# Helper: extract kernel semver (e.g. 6.12.47) from the RPi repo Makefile +get_kernel_version() { + local tag="$1" + curl -sf "https://raw.githubusercontent.com/raspberrypi/linux/${tag}/Makefile" \ + | awk ' + /^VERSION/ { version=$3 } + /^PATCHLEVEL/ { patchlevel=$3 } + /^SUBLEVEL/ { sublevel=$3 } + END { print version "." patchlevel "." sublevel } + ' +} + +# ── RPi kernel update ─────────────────────────────────────────────── +if [ "$RPI_UPDATE" = "true" ] && [ -n "$LATEST_RPI_TAG" ]; then + echo "Updating RPi kernel to $LATEST_RPI_TAG ..." >&2 + + # Download tarball and compute checksums + TARBALL_URL="https://github.com/raspberrypi/linux/archive/refs/tags/${LATEST_RPI_TAG}.tar.gz" + TMP=$(mktemp) + curl -sL "$TARBALL_URL" -o "$TMP" + NEW_SHA256=$(shasum -a 256 "$TMP" | awk '{print $1}') + NEW_SHA512=$(shasum -a 512 "$TMP" | awk '{print $1}') + rm -f "$TMP" + + echo " SHA256: $NEW_SHA256" >&2 + echo " SHA512: $NEW_SHA512" >&2 + + # Get actual kernel version for the config header + KERNEL_VERSION=$(get_kernel_version "$LATEST_RPI_TAG") + echo " Kernel version: $KERNEL_VERSION" >&2 + + # Update patch file + sed -i "s/+ linux_version: .*/+ linux_version: ${LATEST_RPI_TAG}/" "$PATCH_FILE" + sed -i "s/+ linux_sha256: .*/+ linux_sha256: ${NEW_SHA256}/" "$PATCH_FILE" + sed -i "s/+ linux_sha512: .*/+ linux_sha512: ${NEW_SHA512}/" "$PATCH_FILE" + sed -i "s|+# Linux/arm64 .* Kernel Configuration|+# Linux/arm64 ${KERNEL_VERSION} Kernel Configuration|" "$PATCH_FILE" +fi + +# ── Talos update ──────────────────────────────────────────────────── +if [ "$TALOS_UPDATE" = "true" ] && [ -n "$LATEST_TALOS" ]; then + echo "Updating Talos to $LATEST_TALOS ..." >&2 + + # Update TALOS_VERSION in Makefile + sed -i "s/^TALOS_VERSION = .*/TALOS_VERSION = ${LATEST_TALOS}/" "$MAKEFILE" + + # Derive matching PKG_VERSION (same major.minor as Talos) + PKG_MINOR=$(echo "$LATEST_TALOS" | sed -E 's/^(v[0-9]+\.[0-9]+)\..*/\1/') + LATEST_PKG=$(curl -sf "https://api.github.com/repos/siderolabs/pkgs/tags?per_page=20" \ + | jq -r "[.[] | select(.name | startswith(\"${PKG_MINOR}\"))][0].name") + + if [ -n "$LATEST_PKG" ] && [ "$LATEST_PKG" != "null" ]; then + echo " Updating PKG_VERSION to $LATEST_PKG" >&2 + sed -i "s/^PKG_VERSION = .*/PKG_VERSION = ${LATEST_PKG}/" "$MAKEFILE" + else + echo " WARNING: No matching pkgs tag for $PKG_MINOR — keeping current PKG_VERSION" >&2 + fi +fi + +# ── Smoke test — verify patches apply ─────────────────────────────── +echo "Running patch smoke test ..." >&2 +if ! gmake checkouts patches; then + echo "Patches failed to apply!" >&2 + gmake checkouts-clean 2>/dev/null || true + echo "patch_failed=true" + exit 0 +fi +gmake checkouts-clean + +# ── Generate tag ──────────────────────────────────────────────────── +TALOS_VER=$(grep '^TALOS_VERSION' "$MAKEFILE" | awk '{print $NF}') +RPI_TAG=$(grep '+ linux_version:' "$PATCH_FILE" | awk '{print $NF}') +KERNEL_VER=$(get_kernel_version "$RPI_TAG") + +# Find next build number for this component combination +TAG_PREFIX="${TALOS_VER}-k${KERNEL_VER}" +LAST_BUILD=$(git tag -l "${TAG_PREFIX}-*" \ + | sed "s|${TAG_PREFIX}-||" \ + | sort -n \ + | tail -1) +NEXT_BUILD=$(( ${LAST_BUILD:-0} + 1 )) +NEW_TAG="${TAG_PREFIX}-${NEXT_BUILD}" + +echo "Generated tag: $NEW_TAG" >&2 +echo "new_tag=$NEW_TAG" diff --git a/scripts/check-upstream.sh b/scripts/check-upstream.sh index 1f31139..81786c4 100755 --- a/scripts/check-upstream.sh +++ b/scripts/check-upstream.sh @@ -1,54 +1,57 @@ #!/usr/bin/env bash # Check for upstream Talos and RPi kernel updates # -# Compares current versions in Makefile against the latest GitHub releases. -# Outputs GitHub Actions-compatible variables for use in CI workflows. +# Compares current versions (from Makefile + pkgs patch) against the +# latest GitHub releases/tags. Outputs GitHub Actions-compatible variables. # # Usage: -# ./scripts/check-upstream.sh # Print results +# ./scripts/check-upstream.sh # Print results to stdout/stderr # ./scripts/check-upstream.sh >> "$GITHUB_OUTPUT" # For CI set -euo pipefail MAKEFILE="${MAKEFILE:-Makefile}" +PATCH_FILE="${PATCH_FILE:-patches/siderolabs/pkgs/0001-Patched-for-Raspberry-Pi-5.patch}" -# Extract current versions from Makefile -CURRENT_TALOS=$(grep '^TALOS_VERSION' "$MAKEFILE" | head -1 | awk '{print $NF}') -CURRENT_PKG=$(grep '^PKG_VERSION' "$MAKEFILE" | head -1 | awk '{print $NF}') +# ── Current versions ──────────────────────────────────────────────── +CURRENT_TALOS=$(grep '^TALOS_VERSION' "$MAKEFILE" | awk '{print $NF}') +CURRENT_RPI_TAG=$(grep '+ linux_version:' "$PATCH_FILE" | awk '{print $NF}') -echo "Current Talos version: $CURRENT_TALOS" -echo "Current PKG version: $CURRENT_PKG" +echo "Current Talos version: $CURRENT_TALOS" >&2 +echo "Current RPi kernel tag: $CURRENT_RPI_TAG" >&2 -# Check latest Talos stable release +# ── Latest versions from GitHub API ───────────────────────────────── LATEST_TALOS=$(curl -sf "https://api.github.com/repos/siderolabs/talos/releases/latest" \ - | grep '"tag_name"' | sed -E 's/.*"tag_name": *"([^"]+)".*/\1/') + | jq -r '.tag_name') -echo "Latest Talos release: $LATEST_TALOS" +LATEST_RPI_TAG=$(curl -sf "https://api.github.com/repos/raspberrypi/linux/tags?per_page=20" \ + | jq -r '[.[] | select(.name | startswith("stable_"))][0].name') -# Check latest RPi kernel stable tag (format: stable_YYYYMMDD) -LATEST_RPI_KERNEL=$(curl -sf "https://api.github.com/repos/raspberrypi/linux/tags?per_page=10" \ - | grep '"name"' | grep 'stable_' | head -1 | sed -E 's/.*"name": *"([^"]+)".*/\1/') +echo "Latest Talos release: $LATEST_TALOS" >&2 +echo "Latest RPi kernel tag: $LATEST_RPI_TAG" >&2 -echo "Latest RPi kernel tag: $LATEST_RPI_KERNEL" - -# Output for GitHub Actions -echo "talos_current=$CURRENT_TALOS" -echo "talos_latest=$LATEST_TALOS" +# ── Determine what needs updating ─────────────────────────────────── +TALOS_UPDATE=false +RPI_UPDATE=false if [ "$CURRENT_TALOS" != "$LATEST_TALOS" ]; then - echo "talos_update=true" + TALOS_UPDATE=true echo ">> Talos update available: $CURRENT_TALOS -> $LATEST_TALOS" >&2 else - echo "talos_update=false" echo ">> Talos is up to date" >&2 fi -# For RPi kernel, we output what we found — the actual version tracking -# depends on the pkgs patch content which references a specific kernel tag -echo "rpi_current=check-patch" -echo "rpi_latest=$LATEST_RPI_KERNEL" +if [ "$CURRENT_RPI_TAG" != "$LATEST_RPI_TAG" ]; then + RPI_UPDATE=true + echo ">> RPi kernel update available: $CURRENT_RPI_TAG -> $LATEST_RPI_TAG" >&2 +else + echo ">> RPi kernel is up to date" >&2 +fi -# We always flag RPi kernel for review since we can't easily parse the -# patch to extract the exact pinned version -echo "rpi_update=true" -echo ">> RPi kernel latest stable: $LATEST_RPI_KERNEL (review patch manually)" >&2 +# ── Output for GitHub Actions ─────────────────────────────────────── +echo "talos_current=$CURRENT_TALOS" +echo "talos_latest=$LATEST_TALOS" +echo "talos_update=$TALOS_UPDATE" +echo "rpi_current=$CURRENT_RPI_TAG" +echo "rpi_latest=$LATEST_RPI_TAG" +echo "rpi_update=$RPI_UPDATE"