From d2ac27eeb27f926cb8006310a94273dbfc82ce5a Mon Sep 17 00:00:00 2001 From: Vasilito Date: Wed, 22 Apr 2026 22:00:52 +0100 Subject: [PATCH] Add verify-overlay-integrity.sh and remove stale rbos-info symlink Create overlay integrity verification script that checks all recipe symlinks, patch symlinks, circular references, critical local/patches/ files, and config/redbear-*.toml files. Supports --repair (calls apply-patches.sh) and --quiet (CI). Fix config name: redbear-minimal.toml not redbear-mini.toml. Remove stale dangling symlink recipes/system/rbos-info (correct name is redbear-info). --- local/scripts/verify-overlay-integrity.sh | 335 +++++++++++++++------- recipes/system/rbos-info | 1 - 2 files changed, 225 insertions(+), 111 deletions(-) delete mode 120000 recipes/system/rbos-info diff --git a/local/scripts/verify-overlay-integrity.sh b/local/scripts/verify-overlay-integrity.sh index 7ab48456..039a00cd 100755 --- a/local/scripts/verify-overlay-integrity.sh +++ b/local/scripts/verify-overlay-integrity.sh @@ -1,143 +1,258 @@ #!/usr/bin/env bash -# verify-overlay-integrity.sh — ensure Red Bear overlay is present and repairable. +# verify-overlay-integrity.sh — Verify the Red Bear OS overlay structure is intact. +# +# Checks: +# 1. All recipe symlinks (recipes/ → local/recipes/) resolve correctly +# 2. All patch symlinks (recipes/ → local/patches/) resolve correctly +# 3. No circular symlink references +# 4. Critical local/patches/ files exist +# 5. Critical config/redbear-*.toml files exist # # Usage: -# ./local/scripts/verify-overlay-integrity.sh -# ./local/scripts/verify-overlay-integrity.sh --repair +# ./local/scripts/verify-overlay-integrity.sh # Check and report +# ./local/scripts/verify-overlay-integrity.sh --repair # Re-run apply-patches.sh on failures +# ./local/scripts/verify-overlay-integrity.sh --quiet # Exit code only (for CI) +# +# Exit codes: +# 0 — all checks pass +# 1 — one or more checks failed set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" REPAIR=0 +QUIET=0 -if [ "${1:-}" = "--repair" ]; then - REPAIR=1 -fi +for arg in "$@"; do + case "$arg" in + --repair) REPAIR=1 ;; + --quiet) QUIET=1 ;; + --help|-h) + echo "Usage: $0 [--repair] [--quiet]" + echo " --repair Re-run apply-patches.sh if overlay checks fail" + echo " --quiet Exit code only, no output" + exit 0 + ;; + *) + echo "Unknown argument: $arg" >&2 + exit 1 + ;; + esac +done cd "$REPO_ROOT" -fail() { - echo "overlay-integrity: ERROR: $*" >&2 - exit 1 +ERRORS=0 +WARNINGS=0 + +log() { + [ "$QUIET" = "0" ] && echo "$@" } -need_file() { - local path="$1" - [ -f "$path" ] || fail "missing file: $path" +log_error() { + [ "$QUIET" = "0" ] && echo " ERROR: $*" >&2 + ERRORS=$((ERRORS + 1)) } -need_symlink_target() { - local link="$1" - local expected="$2" - local current +log_warn() { + [ "$QUIET" = "0" ] && echo " WARN: $*" + WARNINGS=$((WARNINGS + 1)) +} - if [ ! -L "$link" ]; then - return 1 +# ── 1. Recipe symlinks (recipes/ → local/recipes/) ────────────────── +log "==> Checking recipe symlinks (recipes/ → local/recipes/)..." +RECIPE_SYMLINK_COUNT=0 +BROKEN_RECIPE_SYMLINKS=0 + +while IFS= read -r link; do + RECIPE_SYMLINK_COUNT=$((RECIPE_SYMLINK_COUNT + 1)) + target="$(readlink -f "$link" 2>/dev/null || true)" + + if [ -z "$target" ]; then + log_error "broken symlink: $link (cannot resolve target)" + BROKEN_RECIPE_SYMLINKS=$((BROKEN_RECIPE_SYMLINKS + 1)) + continue fi - current="$(readlink "$link")" - [ "$current" = "$expected" ] -} - -ensure_link() { - local link="$1" - local target="$2" - if ! need_symlink_target "$link" "$target"; then - if [ "$REPAIR" -eq 1 ] && [ -x "$REPO_ROOT/local/scripts/apply-patches.sh" ]; then - "$REPO_ROOT/local/scripts/apply-patches.sh" >/dev/null - fi - need_symlink_target "$link" "$target" || fail "bad symlink: $link -> expected $target" - fi -} - -has_marker() { - local file="$1" - local pattern="$2" - [ -f "$file" ] && grep -q "$pattern" "$file" -} - -ensure_marker() { - local file="$1" - local pattern="$2" - if ! has_marker "$file" "$pattern"; then - if [ "$REPAIR" -eq 1 ]; then - if [ -x "$REPO_ROOT/local/scripts/apply-patches.sh" ]; then - "$REPO_ROOT/local/scripts/apply-patches.sh" >/dev/null || true - fi - apply_patch_dir "$REPO_ROOT/local/patches/base" "$REPO_ROOT/recipes/core/base/source" - apply_patch_dir "$REPO_ROOT/local/patches/kernel" "$REPO_ROOT/recipes/core/kernel/source" - apply_patch_dir "$REPO_ROOT/local/patches/relibc" "$REPO_ROOT/recipes/core/relibc/source" + # Check if target is inside local/recipes/ + if echo "$target" | grep -q "/local/recipes/"; then + if [ ! -e "$target" ]; then + log_error "dangling symlink: $link -> $target (target does not exist)" + BROKEN_RECIPE_SYMLINKS=$((BROKEN_RECIPE_SYMLINKS + 1)) fi fi - has_marker "$file" "$pattern" || fail "missing marker in $file: $pattern" -} -check_local_over_wip_priority() { - local local_dir - local rel - local name - local wip_match - local active_path - - for local_dir in "$REPO_ROOT"/local/recipes/*/*; do - [ -d "$local_dir" ] || continue - rel="${local_dir#"$REPO_ROOT/local/recipes/"}" - name="${local_dir##*/}" - wip_match="$(find "$REPO_ROOT/recipes/wip" -mindepth 2 -maxdepth 2 -type d -name "$name" 2>/dev/null | head -n1 || true)" - - # Policy: if local package conflicts with an upstream WIP package of the same name, - # the active recipe must be our local overlay (symlink in recipes/*/). - [ -n "$wip_match" ] || continue - - active_path="$(find "$REPO_ROOT/recipes" -mindepth 2 -maxdepth 2 -type l -name "$name" ! -path "$REPO_ROOT/recipes/wip/*" 2>/dev/null | head -n1 || true)" - # Only enforce for actively mounted local overlays. - [ -n "$active_path" ] || continue - - if [ "$(readlink "$active_path")" != "../../local/recipes/${rel}" ]; then - fail "local-over-wip policy violated: '$active_path' does not point to ../../local/recipes/${rel}" + # Check for circular symlinks + if [ -L "$target" ]; then + # The target itself is a symlink — could be circular + resolved_inner="$(readlink -f "$target" 2>/dev/null || true)" + if [ -n "$resolved_inner" ] && [ "$resolved_inner" = "$(readlink -f "$link" 2>/dev/null)" ]; then + log_error "circular symlink: $link -> $target -> (circular)" + BROKEN_RECIPE_SYMLINKS=$((BROKEN_RECIPE_SYMLINKS + 1)) fi - done -} + fi +done < <(find recipes -maxdepth 3 -type l 2>/dev/null | grep -v '/target$\|/stage\|/sysroot$\|/source$' | sort) -apply_patch_dir() { - local patch_dir="$1" - local target_dir="$2" - local patch_file +log " $RECIPE_SYMLINK_COUNT recipe symlinks checked, $BROKEN_RECIPE_SYMLINKS broken" - [ -d "$patch_dir" ] || return 0 - [ -d "$target_dir" ] || return 0 +# ── 2. Patch symlinks (recipes/ → local/patches/) ─────────────────── +log "==> Checking patch symlinks (recipes/ → local/patches/)..." +PATCH_SYMLINK_COUNT=0 +BROKEN_PATCH_SYMLINKS=0 - for patch_file in "$patch_dir"/*.patch; do - [ -f "$patch_file" ] || continue - if patch --dry-run -p1 -d "$target_dir" < "$patch_file" >/dev/null 2>&1; then - patch -p1 -d "$target_dir" < "$patch_file" >/dev/null 2>&1 || true +EXPECTED_PATCH_SYMLINKS=( + "recipes/core/kernel/redox.patch" + "recipes/core/base/redox.patch" + "recipes/core/base/P2-boot-runtime-fixes.patch" + "recipes/core/relibc/redox.patch" + "recipes/core/installer/redox.patch" + "recipes/core/bootloader/redox.patch" + "recipes/core/bootloader/P2-live-preload-guard.patch" + "recipes/core/bootloader/P3-uefi-live-image-safe-read.patch" + "recipes/gui/orbutils/redox.patch" +) + +for patch_link in "${EXPECTED_PATCH_SYMLINKS[@]}"; do + PATCH_SYMLINK_COUNT=$((PATCH_SYMLINK_COUNT + 1)) + if [ ! -L "$patch_link" ]; then + if [ -f "$patch_link" ]; then + log_warn "$patch_link exists as regular file (not a symlink to local/patches/)" + else + log_error "$patch_link missing (should be symlink to local/patches/)" + BROKEN_PATCH_SYMLINKS=$((BROKEN_PATCH_SYMLINKS + 1)) fi - done -} + continue + fi + target="$(readlink -f "$patch_link" 2>/dev/null || true)" + if [ -z "$target" ] || [ ! -f "$target" ]; then + log_error "dangling patch symlink: $patch_link -> $target" + BROKEN_PATCH_SYMLINKS=$((BROKEN_PATCH_SYMLINKS + 1)) + fi +done -need_file "local/patches/kernel/redox.patch" -need_file "local/patches/base/redox.patch" -need_file "local/patches/relibc/redox.patch" +log " $PATCH_SYMLINK_COUNT patch symlinks checked, $BROKEN_PATCH_SYMLINKS broken" -ensure_link "recipes/core/kernel/redox.patch" "../../../local/patches/kernel/redox.patch" -ensure_link "recipes/core/base/redox.patch" "../../../local/patches/base/redox.patch" -ensure_link "recipes/core/bootloader/P2-live-preload-guard.patch" "../../../local/patches/bootloader/P2-live-preload-guard.patch" -ensure_link "recipes/core/bootloader/P3-uefi-live-image-safe-read.patch" "../../../local/patches/bootloader/P3-uefi-live-image-safe-read.patch" -ensure_link "recipes/core/grub" "../../local/recipes/core/grub" -check_local_over_wip_priority +# ── 3. Critical local/patches/ files ──────────────────────────────── +log "==> Checking critical local/patches/ files..." +CRITICAL_PATCHES=( + "local/patches/kernel/redox.patch" + "local/patches/base/redox.patch" + "local/patches/relibc/redox.patch" + "local/patches/installer/redox.patch" + "local/patches/bootloader/redox.patch" + "local/patches/build-system/001-rebrand-and-build.patch" + "local/patches/build-system/002-cookbook-fixes.patch" + "local/patches/build-system/003-config.patch" + "local/patches/build-system/004-docs-and-cleanup.patch" +) -# Critical runtime markers in source trees (if sources are present locally). -if [ -d "recipes/core/base/source" ]; then - ensure_marker \ - "recipes/core/base/source/drivers/acpid/src/acpi.rs" \ - "AML interpreter requires PCI registration before initialization" - ensure_marker \ - "recipes/core/base/source/drivers/acpid/src/scheme.rs" \ - "wait_for_pci_ready" - ensure_marker \ - "recipes/core/base/source/drivers/input/ps2d/src/controller.rs" \ - "continuing without second port" +MISSING_PATCHES=0 +for patch_file in "${CRITICAL_PATCHES[@]}"; do + if [ ! -f "$patch_file" ]; then + log_error "missing critical patch: $patch_file" + MISSING_PATCHES=$((MISSING_PATCHES + 1)) + fi +done + +if [ "$MISSING_PATCHES" = "0" ]; then + log " All critical patches present" fi -echo "overlay-integrity: OK" +# ── 4. Config files ───────────────────────────────────────────────── +log "==> Checking config/redbear-*.toml files..." +CRITICAL_CONFIGS=( + "config/redbear-full.toml" + "config/redbear-live-full.toml" + "config/redbear-minimal.toml" + "config/redbear-live-minimal.toml" + "config/redbear-desktop.toml" + "config/redbear-device-services.toml" + "config/redbear-legacy-base.toml" + "config/redbear-legacy-desktop.toml" + "config/redbear-netctl.toml" + "config/redbear-greeter-services.toml" +) + +MISSING_CONFIGS=0 +for config_file in "${CRITICAL_CONFIGS[@]}"; do + if [ ! -f "$config_file" ]; then + log_error "missing config: $config_file" + MISSING_CONFIGS=$((MISSING_CONFIGS + 1)) + fi +done + +TOTAL_REDBEAR_CONFIGS=$(find config -maxdepth 1 -name 'redbear-*.toml' 2>/dev/null | wc -l) +log " $TOTAL_REDBEAR_CONFIGS redbear-*.toml files found, $MISSING_CONFIGS critical configs missing" + +# ── 5. local/ directory structure ─────────────────────────────────── +log "==> Checking local/ directory structure..." +REQUIRED_LOCAL_DIRS=( + "local/recipes" + "local/recipes/drivers" + "local/recipes/gpu" + "local/recipes/system" + "local/recipes/core" + "local/recipes/libs" + "local/recipes/branding" + "local/recipes/kde" + "local/patches" + "local/patches/kernel" + "local/patches/base" + "local/patches/relibc" + "local/patches/bootloader" + "local/patches/installer" + "local/patches/build-system" + "local/docs" + "local/scripts" + "local/Assets" +) + +MISSING_DIRS=0 +for dir in "${REQUIRED_LOCAL_DIRS[@]}"; do + if [ ! -d "$dir" ]; then + log_error "missing directory: $dir" + MISSING_DIRS=$((MISSING_DIRS + 1)) + fi +done + +if [ "$MISSING_DIRS" = "0" ]; then + log " All required local/ directories present" +fi + +# ── Summary ───────────────────────────────────────────────────────── +log "" +log "=== Overlay Integrity Summary ===" +log " Recipe symlinks: $RECIPE_SYMLINK_COUNT ($BROKEN_RECIPE_SYMLINKS broken)" +log " Patch symlinks: $PATCH_SYMLINK_COUNT ($BROKEN_PATCH_SYMLINKS broken)" +log " Critical patches: ${#CRITICAL_PATCHES[@]} ($MISSING_PATCHES missing)" +log " Critical configs: ${#CRITICAL_CONFIGS[@]} ($MISSING_CONFIGS missing)" +log " Required dirs: ${#REQUIRED_LOCAL_DIRS[@]} ($MISSING_DIRS missing)" +log " Warnings: $WARNINGS" +log " Errors: $ERRORS" + +if [ "$ERRORS" -gt 0 ]; then + log "" + log "!! Overlay integrity check FAILED ($ERRORS errors)" + if [ "$REPAIR" = "1" ]; then + log "==> Attempting repair via apply-patches.sh..." + if [ -f local/scripts/apply-patches.sh ]; then + bash local/scripts/apply-patches.sh + log "==> Repair complete. Re-running verification..." + exec "$0" --quiet + else + log_error "apply-patches.sh not found — cannot repair" + exit 1 + fi + else + log " Run with --repair to attempt automatic fix, or:" + log " ./local/scripts/apply-patches.sh" + exit 1 + fi +else + if [ "$QUIET" = "0" ]; then + log "" + log " Overlay integrity check PASSED" + fi + exit 0 +fi diff --git a/recipes/system/rbos-info b/recipes/system/rbos-info deleted file mode 120000 index 8b7e25f6..00000000 --- a/recipes/system/rbos-info +++ /dev/null @@ -1 +0,0 @@ -../../local/recipes/system/rbos-info \ No newline at end of file