Stop the flatpak build updating runtimes it already has #370

Merged
enricobuehler merged 2 commits from worktree-flatpak-deps-no-update into main 2026-08-22 21:16:08 +00:00
2 changed files with 164 additions and 9 deletions
+53 -9
View File
@@ -50,7 +50,10 @@ on:
- 'crates/pf-vaadec/**'
- 'packaging/flatpak/**'
- 'Cargo.lock'
# Both halves of this job's correctness, not of the bundle's content: a change to either
# can only be proven by a real run, and there is no other trigger that would give it one.
- '.gitea/workflows/flatpak.yml'
- 'scripts/ci/flatpak-deps-present.sh'
tags: ['v*']
workflow_dispatch:
@@ -270,12 +273,13 @@ jobs:
- name: Prefetch deps + sources (retried — the network phase, split off the build)
run: |
set -euo pipefail
# All of the job's heavy network I/O happens HERE, retried, so a dropped DNS lookup
# or TCP dial costs a backoff-retry instead of the whole (long) compile:
# 1) --install-deps-only pulls everything the manifest declares from Flathub: the
# GNOME 50 runtime/SDK + the rust-stable (//25.08, rustc 1.96) and llvm20 SDK
# extensions. (No codec extension: the client links no FFmpeg — see the
# manifest header.)
# 1) the Flathub deps the manifest declares — the GNOME 50 runtime/SDK + the
# rust-stable (//25.08, rustc 1.96) and llvm20 SDK extensions — but ONLY the ones
# genuinely MISSING; see the block below. (No codec extension: the client links no
# FFmpeg — see the manifest header.)
# 2) --download-only fetches every source (all crates in cargo-sources.json) into
# the .flatpak-builder state dir. Both are resumable/idempotent, so re-running
# after a partial failure is safe and cheap.
@@ -288,9 +292,40 @@ jobs:
# for the mechanism.
# 10 attempts (~9min budget), matching the remote-add bootstrap above — same shared,
# load-sensitive runner, same flathub.org resolution path.
bash scripts/ci/retry.sh 10 flatpak-builder --user --force-clean --disable-rofiles-fuse \
--install-deps-from=flathub --install-deps-only \
"$PWD/build-dir" "$MANIFEST"
#
# WHY THIS IS NOT AN UNCONDITIONAL `--install-deps-only` ANY MORE (2026-08-22):
# that flag does not install what is missing, it UPDATES what is present.
# builder_manifest_install_dep() branches on `flatpak info --show-commit <ref>` succeeding
# and runs `flatpak update` for every dep already installed — with no fallback to a
# plain install when that update fails — and ci/flatpak-ci.Dockerfile bakes
# the entire runtime set, so on a healthy run it was a pure no-op that nonetheless made
# every build depend on Flathub being healthy at that minute. It bit on 2026-08-22:
# Updating runtime/org.freedesktop.Sdk.Extension.rust-stable/x86_64/25.08
# Error: Failed to update org.freedesktop.Sdk.Extension.rust-stable: While pulling …
# .filez: Server returned HTTP 404
# dl.flathub.org served a 404 for one object of the then-current rust-stable//25.08
# commit, deterministically — all 10 retry.sh attempts died on the SAME object over
# ~9 min — and flatpak-builder SEGFAULTED on its own error path (rc=139), so retry.sh
# saw a crash rather than a clean "this will never work" either. The build never wanted
# that newer commit: the manifest pins a runtime VERSION, not a commit, and the baked
# one satisfies it. Updating bought nothing and imported an upstream outage.
#
# So: assert what the image already has, and reach for Flathub only on a real miss —
# the same "guard, don't install on top of a stale image" doctrine as the Tooling step.
# The check lives in scripts/ci/flatpak-deps-present.sh (run its --self-test after
# touching it): a bug in it that reports "satisfied" when it is not would build against
# whatever runtime happened to be lying around, which is worth more than an inline
# if-statement. It deliberately fails OPEN — anything it cannot parse takes the slow
# install path below.
if bash scripts/ci/flatpak-deps-present.sh "$MANIFEST"; then
echo "deps satisfied by the baked image — not touching Flathub"
flatpak list --user --columns=ref
else
echo "::warning::$MANIFEST declares deps punktfunk-flatpak-ci does not have — pulling from Flathub (~1.5 GB). Bump GNOME_VERSION/FREEDESKTOP_VERSION in ci/flatpak-ci.Dockerfile so this stays off the hot path."
bash scripts/ci/retry.sh 10 flatpak-builder --user --force-clean --disable-rofiles-fuse \
--install-deps-from=flathub --install-deps-only \
"$PWD/build-dir" "$MANIFEST"
fi
bash scripts/ci/retry.sh 10 flatpak-builder --user --force-clean --disable-rofiles-fuse \
--download-only --disable-updates \
"$PWD/build-dir" "$MANIFEST"
@@ -298,7 +333,17 @@ jobs:
- name: Build the flatpak (offline — deps + sources prefetched above)
run: |
# Everything is already local (state dir warmed by the prefetch step), so this long
# step needs no network; --install-deps-from stays as a no-op safety net.
# step needs no network.
#
# --install-deps-from=flathub USED to sit here, commented as "a no-op safety net". It
# was neither. builder-main.c calls builder_manifest_install_deps() whenever that flag
# is set — --install-deps-only only decides whether it EXITS afterwards — so this step
# re-ran the same `flatpak update` of the runtimes that killed the prefetch step on
# 2026-08-22 (Flathub HTTP 404 on a rust-stable//25.08 object; see there). A live pull
# of multi-GB runtimes is a strange thing to call a safety net in the step whose whole
# design is to be offline, and it could only ever fire if the prefetch step above had
# already failed the job. Dropped: the prefetch step is the one place that talks to
# Flathub, and it is the one place with retries.
#
# --disable-updates is LOAD-BEARING, not tidiness: without it this step was never
# actually offline. flatpak-builder runs the DOWNLOAD PHASE again as part of every
@@ -326,7 +371,6 @@ jobs:
flatpak-builder --user --force-clean --disable-rofiles-fuse \
--default-branch="$FLATPAK_BRANCH" \
--disable-updates \
--install-deps-from=flathub \
--repo="$PWD/repo" \
"$PWD/build-dir" "$MANIFEST"
+111
View File
@@ -0,0 +1,111 @@
# shellcheck shell=bash
# Does this box already have every Flathub dep a flatpak manifest declares?
# bash scripts/ci/flatpak-deps-present.sh <manifest.yml> -> exit 0 = yes, 1 = no
# bash scripts/ci/flatpak-deps-present.sh --self-test -> run the asserts below
#
# WHY THIS EXISTS: flatpak.yml used to prefetch deps with `flatpak-builder --install-deps-only`,
# which does NOT mean "install what is missing". builder_manifest_install_dep() branches on
# `flatpak info --show-commit <ref>` succeeding and runs `flatpak update` for every dep that IS
# installed (a failed update is fatal there — it never falls back to install) — and
# ci/flatpak-ci.Dockerfile bakes the whole runtime set, so on a healthy run that flag did nothing
# except make the build depend on Flathub being up at that minute. On 2026-08-22 it took the job
# down: dl.flathub.org returned HTTP 404 for one .filez object of the then-current
# rust-stable//25.08 commit, identically on all 10 retry.sh attempts (~9 min), and flatpak-builder
# segfaulted on its own error path (rc=139) so the retry wrapper could not tell a dead end from a
# blip. Nothing about the build wanted that newer commit: the manifest pins a runtime VERSION, not
# a commit, and the baked one satisfies it.
#
# So the workflow asks this first and only reaches for Flathub on a real miss.
#
# FAILS OPEN, deliberately: an unreadable/unexpected manifest reports "not present" (1), so the
# caller does the full install. Silently skipping the install on a manifest we stopped
# understanding is how you build against the wrong runtime.
set -uo pipefail
deps_present() {
local manifest="$1" runtime rt_ver sdk exts e
runtime=$(sed -n 's/^runtime: *//p' "$manifest" | head -1)
rt_ver=$(sed -n 's/^runtime-version: *//p' "$manifest" | tr -d "\"'" | head -1)
sdk=$(sed -n 's/^sdk: *//p' "$manifest" | head -1)
exts=$(sed -n '/^sdk-extensions:/,/^[^ #-]/p' "$manifest" | sed -n 's/^ *- *//p')
[ -n "$runtime" ] && [ -n "$rt_ver" ] && [ -n "$sdk" ] && [ -n "$exts" ] || return 1
flatpak info --user "$runtime//$rt_ver" >/dev/null 2>&1 || return 1
flatpak info --user "$sdk//$rt_ver" >/dev/null 2>&1 || return 1
# Extensions are checked for PRESENCE, not version: flatpak-builder resolves their version from
# the SDK's own metadata (it prints "Dependency Extension: … 25.08"), never from the manifest.
# Any bump that moves them moves runtime-version too, which the two checks above already catch.
for e in $exts; do
flatpak info --user "$e" >/dev/null 2>&1 || return 1
done
}
self_test() {
local rc fails=0 full
# NOT `local`: the EXIT trap fires after this function has returned.
SELFTEST_TMP=$(mktemp -d) || return 1
trap 'rm -rf "$SELFTEST_TMP"' EXIT
local tmp="$SELFTEST_TMP"
cat > "$tmp/ok.yml" <<'YML'
runtime: org.gnome.Platform
runtime-version: '50'
sdk: org.gnome.Sdk
sdk-extensions:
- org.freedesktop.Sdk.Extension.rust-stable
- org.freedesktop.Sdk.Extension.llvm20
command: punktfunk-client
YML
# A manifest this script cannot read (the fail-open case).
printf 'app-id: io.unom.Punktfunk\n' > "$tmp/unparseable.yml"
# Stub `flatpak`: $INSTALLED is the newline-separated set of refs it admits to having.
mkdir -p "$tmp/bin"
cat > "$tmp/bin/flatpak" <<'STUB'
#!/usr/bin/env bash
# only `flatpak info --user <ref>` is exercised here
# args are: info --user <ref>
[ "$1" = info ] || exit 0
printf '%s\n' "$INSTALLED" | grep -qxF "$3"
STUB
chmod +x "$tmp/bin/flatpak"
PATH="$tmp/bin:$PATH"
check() { # <expected rc> <label> <installed set> <manifest>
INSTALLED="$3" deps_present "$4"; rc=$?
if [ "$rc" != "$1" ]; then
echo "FAIL: $2 (expected rc=$1, got $rc)" >&2; fails=$((fails + 1))
else
echo "ok: $2"
fi
}
full='org.gnome.Platform//50
org.gnome.Sdk//50
org.freedesktop.Sdk.Extension.rust-stable
org.freedesktop.Sdk.Extension.llvm20'
check 0 "everything baked -> skip Flathub" "$full" "$tmp/ok.yml"
check 1 "cold box -> install" "" "$tmp/ok.yml"
check 1 "runtime missing -> install" "${full/org.gnome.Platform\/\/50/x}" "$tmp/ok.yml"
check 1 "sdk missing -> install" "${full/org.gnome.Sdk\/\/50/x}" "$tmp/ok.yml"
# The regression that started all this: llvm20 fine, rust-stable not.
check 1 "one sdk-extension missing -> install" "${full/*.rust-stable/x}" "$tmp/ok.yml"
# A runtime installed at ANOTHER version must not pass just because the name matches.
check 1 "runtime at the wrong version" 'org.gnome.Platform//51
org.gnome.Sdk//51
org.freedesktop.Sdk.Extension.rust-stable
org.freedesktop.Sdk.Extension.llvm20' "$tmp/ok.yml"
check 1 "unreadable manifest -> fail open" "$full" "$tmp/unparseable.yml"
[ "$fails" = 0 ] || { echo "$fails check(s) failed" >&2; return 1; }
echo "all checks passed"
}
case "${1:---help}" in
--self-test) self_test ;;
--help|-h) sed -n '2,4p' "$0"; exit 2 ;;
*) deps_present "$1" ;;
esac