#!/usr/bin/env bash # verify-external-source-versions.sh — detect recipe-vs-source VERSION divergence # for tar-based (external upstream) recipes. # # Why this exists # --------------- # Several recipes under local/recipes/ vendor their upstream `source/` tree in # git (the fork model) while ALSO declaring a `[source] tar = ` + `blake3`. # A version bump that only rewrites `tar=`/`blake3=` (e.g. via # bump-graphics-recipes.sh, or a manual edit) does NOT propagate into the # committed source/ tree, nor does cookbook re-extract an existing source/ or # re-verify an existing source.tar. The build then silently compiles the STALE # version — exactly the Qt 6.11.0-vs-6.11.1 divergence that broke the desktop # build (qtshadertools required 6.11.1 but qtbase was cooked from a 6.11.0 tree). # # This is the tar-recipe analogue of verify-fork-functions.sh (which guards the # local/sources/ forks). It FAILS LOUDLY rather than letting a stale tree build. # # Checks per tar recipe (only when the relevant artifact is present): # 1. source.tar blake3 == recipe blake3 (stale downloaded tarball) # 2. source.tar top-dir version == recipe URL version (URL bumped, tar not) # 3. vendored source/ version marker == recipe URL version # (recipe bumped, committed tree not — the Qt class). Marker files: # - .cmake.conf : QT_REPO_MODULE_VERSION "X.Y.Z" (Qt modules) # - .redbear-src-version : plain "X.Y.Z" (generic stamp) # # Exit 0 = all consistent (or nothing to check). Exit 1 = divergence found. # Gate: REDBEAR_SKIP_EXTERNAL_SOURCE_CHECK=1 bypasses (handled by caller). set -u ROOT="$(cd "$(dirname "$0")/../.." && pwd)" QUIET=0 for a in "$@"; do case "$a" in --quiet) QUIET=1 ;; esac; done have_b3() { command -v b3sum >/dev/null 2>&1; } # Extract a dotted version (X.Y or X.Y.Z...) from a string; prints the LAST one, # which for "qtbase-everywhere-src-6.11.1.tar.xz" is the upstream version. ver_of() { grep -oE '[0-9]+\.[0-9]+(\.[0-9]+)*' <<<"$1" | tail -1; } FAIL=0 CHECKED=0 declare -a PROBLEMS=() while IFS= read -r recipe; do dir="$(dirname "$recipe")" # recipe-declared tar URL + blake3 (first [source] tar= wins) url="$(grep -m1 -E '^[[:space:]]*tar[[:space:]]*=' "$recipe" 2>/dev/null | sed -E 's/^[^"]*"([^"]+)".*/\1/')" [ -n "$url" ] || continue rblake="$(grep -m1 -E '^[[:space:]]*blake3[[:space:]]*=' "$recipe" 2>/dev/null | grep -oE '[0-9a-f]{64}')" rver="$(ver_of "$(basename "$url")")" [ -n "$rver" ] || continue name="$(basename "$dir")" CHECKED=$((CHECKED + 1)) # 1) cached source.tar blake3 must match recipe blake3 if [ -f "$dir/source.tar" ] && [ -n "$rblake" ] && have_b3; then dblake="$(b3sum "$dir/source.tar" 2>/dev/null | awk '{print $1}')" if [ -n "$dblake" ] && [ "$dblake" != "$rblake" ]; then PROBLEMS+=("$name: cached source.tar blake3 ($dblake) != recipe blake3 ($rblake) — stale downloaded tarball; rm '$dir/source.tar' to re-fetch") FAIL=1 fi fi # 2) source.tar top-dir version must match recipe URL version if [ -f "$dir/source.tar" ]; then tver="$(ver_of "$(tar tf "$dir/source.tar" 2>/dev/null | head -1)")" if [ -n "$tver" ] && [ "$tver" != "$rver" ]; then PROBLEMS+=("$name: source.tar is v$tver but recipe tar= is v$rver — URL bumped, tarball not; rm '$dir/source.tar' to re-fetch") FAIL=1 fi fi # 3) vendored source/ version marker must match recipe URL version. # ACTUAL code markers (.cmake.conf, KDE KF_VERSION) win over the generic # .redbear-src-version stamp — a stale stamp must never mask the real code # version and let a divergent tree pass. if [ -d "$dir/source" ]; then sver="" if [ -f "$dir/source/.cmake.conf" ]; then sver="$(ver_of "$(grep -m1 QT_REPO_MODULE_VERSION "$dir/source/.cmake.conf" 2>/dev/null)")" fi if [ -z "$sver" ] && [ -f "$dir/source/CMakeLists.txt" ]; then # KF (KF_VERSION), Plasma (PROJECT_VERSION), literal project(... VERSION x) sver="$(grep -m1 -E 'set\((KF_VERSION|PROJECT_VERSION)|project\([A-Za-z0-9_]+ VERSION [0-9]' "$dir/source/CMakeLists.txt" 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)" # Extra CMake Modules / some KF modules: bare set(VERSION "X.Y.Z") if [ -z "$sver" ]; then sver="$(grep -m1 -E '^set\(VERSION "[0-9]+\.[0-9]+\.[0-9]+"' "$dir/source/CMakeLists.txt" 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)" fi # KDE Gear split RELEASE_SERVICE_VERSION_* (konsole etc.) if [ -z "$sver" ] && grep -q RELEASE_SERVICE_VERSION_MAJOR "$dir/source/CMakeLists.txt" 2>/dev/null; then a="$(grep -m1 RELEASE_SERVICE_VERSION_MAJOR "$dir/source/CMakeLists.txt" | grep -oE '[0-9]+' | head -1)" b="$(grep -m1 RELEASE_SERVICE_VERSION_MINOR "$dir/source/CMakeLists.txt" | grep -oE '[0-9]+' | head -1)" c="$(grep -m1 RELEASE_SERVICE_VERSION_MICRO "$dir/source/CMakeLists.txt" | grep -oE '[0-9]+' | head -1)" [ -n "$a" ] && sver="$a.$b.$c" fi fi if [ -z "$sver" ] && [ -f "$dir/source/.redbear-src-version" ]; then sver="$(ver_of "$(cat "$dir/source/.redbear-src-version" 2>/dev/null)")" fi if [ -n "$sver" ] && [ "$sver" != "$rver" ]; then PROBLEMS+=("$name: vendored source/ is v$sver but recipe declares v$rver — the committed tree was never bumped; rebase source/ onto v$rver (see qt bump commits for the pattern)") FAIL=1 fi fi done < <(find "$ROOT/local/recipes" -name recipe.toml -not -path '*/wip/*' 2>/dev/null) if [ "$FAIL" != "0" ]; then echo "FAIL: external source version divergence ($CHECKED tar recipes checked):" >&2 for p in "${PROBLEMS[@]}"; do echo " - $p" >&2; done exit 1 fi [ "$QUIET" = "1" ] || echo ">>> Preflight: external tar-recipe source versions consistent ($CHECKED checked)." exit 0