Files
RedBear-OS/local/scripts/patch-status.sh
T
vasilito 850a08b368 phase 7.5: add patch-status.sh — top-level operator-facing status report
New script produces a human-readable report in 3 modes:

  ./local/scripts/patch-status.sh           full text report
  ./local/scripts/patch-status.sh --brief    one-line summary
  ./local/scripts/patch-status.sh --json     machine-readable JSON

Runs 5 verifications (sync-versions, verify-patch-content,
verify-collision-detection, verify-fork-versions, cargo check)
and aggregates the results into a single status report.

The report shows:
  - Active patches (total/preserved/orphaned)
  - Collision detection status
  - Version sync / fork compliance
  - Diverged fork advisory count
  - Cookbook build status
  - Legacy archive counts
  - Fork push deadlock status

Parsing fix: uses per-check temp files (not a single overwritten
tmp file) and sed-based TOTAL line extraction to avoid the
grep -oE first-number trap that returned the wrong patch count.

Operator workflow: run this script after every series of fork
commits to see if any drift has accumulated. Takes 30-120s.
2026-07-12 11:52:28 +03:00

147 lines
5.9 KiB
Bash
Executable File

#!/usr/bin/env bash
# patch-status.sh — Top-level status report for the Red Bear OS patch system.
# Runs all 7+ verifications and produces a single human-readable report.
#
# Usage:
# ./local/scripts/patch-status.sh # Full report
# ./local/scripts/patch-status.sh --brief # One-line summary
# ./local/scripts/patch-status.sh --json # Machine-readable
#
# Operates in the RedBear-OS repo root.
# Takes about 30-120 seconds depending on network/caching.
set -uo pipefail
cd "$(dirname "$0")/../.."
brief=0
json=0
for arg in "$@"; do
case "$arg" in
--brief) brief=1 ;;
--json) json=1 ;;
esac
done
# ---- helpers ----
run_quiet() {
# run a command, capture output to a named temp file
local outfile="/tmp/patch-status-$1.tmp"
shift
"$@" >"$outfile" 2>&1
echo $?
}
grep_val() {
# extract a numeric value from a tmp file
local outfile="/tmp/patch-status-$1.tmp"
local pattern="$2"
grep "$pattern" "$outfile" 2>/dev/null | head -1 | grep -oE '[0-9]+' | head -1
}
grep_line() {
# extract a matching line from a tmp file
local outfile="/tmp/patch-status-$1.tmp"
local pattern="$2"
grep "$pattern" "$outfile" 2>/dev/null | head -1
}
# ---- run the checks ----
echo ">>> Running verifications..." >&2
# 1. sync-versions
rc_sync=$(run_quiet "sync" bash local/scripts/sync-versions.sh --check)
# 2. verify-patch-content
rc_pc=$(run_quiet "pc" python3 local/scripts/verify-patch-content.py)
# 3. verify-collision-detection
rc_cd=$(run_quiet "cd" python3 local/scripts/verify-collision-detection.py)
# 4. verify-fork-versions
rc_fv=$(run_quiet "fv" bash local/scripts/verify-fork-versions.sh)
# 5. cargo check
rc_cargo=$(run_quiet "cargo" cargo check --bin repo --message-format=short 2>&1)
# ---- extract values ----
# Parse the TOTAL line once for all patch numbers
total_line=$(grep "TOTAL:" /tmp/patch-status-pc.tmp 2>/dev/null | head -1)
total_patches=$(echo "$total_line" | sed -nE 's/.*TOTAL: +([0-9]+) patches.*/\1/p')
preserved=$(echo "$total_line" | sed -nE 's/.*, +([0-9]+) preserved.*/\1/p')
orphaned=$(echo "$total_line" | sed -nE 's/.*, +([0-9]+) orphaned.*/\1/p')
sync_status=$(grep_line "sync" "All crates are at" || echo "")
collision_count=$(grep "Total collisions:" /tmp/patch-status-cd.tmp 2>/dev/null | head -1 | grep -oE '[0-9]+' | head -1)
fork_status=$(grep_line "fv" "All Cat 2 forks pass" || echo "")
diverged_count=$(grep "diverged.*mode" /tmp/patch-status-fv.tmp 2>/dev/null | wc -l)
cargo_ok=$(grep_line "cargo" "Finished")
# ---- JSON output ----
if [ "$json" = "1" ]; then
cat <<EOF
{
"total_patches": ${total_patches:-?},
"preserved": ${preserved:-?},
"orphaned": ${orphaned:-?},
"collisions": ${collision_count:-0},
"forks_diverged": ${diverged_count:-0},
"sync_ok": "$([ "$sync_status" != "DRIFT" ] && echo true || echo false)",
"forks_ok": "$([ "$fork_status" != "FAIL" ] && echo true || echo false)",
"cargo_ok": "$([ "$cargo_ok" != "FAIL" ] && echo true || echo false)",
"branch": "$(git branch --show-current 2>/dev/null)",
"cookbook_version": "$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/')"
}
EOF
exit 0
fi
# ---- brief output ----
if [ "$brief" = "1" ]; then
printf "[orph=%s col=%s sync=%s fork=%s cargo=%s]\n" \
"${orphaned:-?}/${total_patches:-?}" \
"${collision_count:-0}" \
"$([ "$sync_status" != "DRIFT" ] && echo OK || echo DRIFT)" \
"$([ "$fork_status" != "FAIL" ] && echo OK || echo FAIL)" \
"$([ "$cargo_ok" != "FAIL" ] && echo OK || echo FAIL)"
exit 0
fi
# ---- full report ----
cat <<EOF
======================================================================
RED BEAR OS — PATCH SYSTEM STATUS
======================================================================
Active patches: ${total_patches:-?} (${preserved:-?}/${total_patches:-?} preserved)
Orphans: ${orphaned:-0} ($([ "${orphaned:-0}" = "0" ] && echo "ALL FORKS: patches preserved in fork HEAD" || echo "ACTION REQUIRED: orphaned patches"))"
Collision detection: ${collision_count:-0} collisions (config [[files]] vs recipe installs/files)
Version sync: $([ "$sync_status" != "DRIFT" ] && echo "OK (Cat 0+1+2 match branch)" || echo "DRIFT — run sync-versions.sh")"
Fork versions: $([ "$fork_status" != "FAIL" ] && echo "OK (all Cat 2 forks pass)" || echo "FAIL — see verify-fork-versions.sh")"
Diverged forks: ${diverged_count:-0} advisory warnings (bootloader, installer — known divergence)"
Cookbook: $([ "$cargo_ok" != "FAIL" ] && echo "Compiles OK (cargo check passed)" || echo "FAIL — cargo check failed")"
Branch: $(git branch --show-current 2>/dev/null)
Cookbook version: $(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/')
======================================================================
Legacy archives: legacy-superseded-2026-07-12/ ($(find local/patches/legacy-superseded-2026-07-12 -name '*.patch' | wc -l) patches)
legacy-absorbed-2026-07-12/ ($(find local/patches/legacy-absorbed-2026-07-12 -name '*.patch' | wc -l) patches)
Fork push status: base (DEADLOCKED — receive.shallowUpdate on gitea)
bootloader (diverged — 927 vs 77 files)
installer,kernel,syscall,libredox,relibc,userutils
(pushed in past rounds; operator may have
reverted; local work preserved in
local/sources/<fork> regardless of origin)
Tooling:
./local/scripts/pre-push-checks.sh (7-check pre-push safety net)
./local/scripts/push-fork-branches.sh (operator-reviewed fork push)
./local/scripts/unblock-base-push.sh (base fork deadlock resolver)
======================================================================
$(date +%Y-%m-%d\ %H:%M:%S)
======================================================================
EOF