Files
RedBear-OS/local/scripts/verify-fork-functions.sh
T
vasilito 2153bf5f6e build-system: add function-level fork verification + fix critical bugs
CRITICAL: Add verify-fork-functions.sh — detects silent upstream code loss
from bad merges that file-level verification cannot catch. This is the
verification that would have caught the kfdwrite drop bug.

Wire it into build-preflight.sh (pre-build gate) and upgrade-forks.sh
(post-upgrade gate with automatic rollback on failure).

Fix upgrade-forks.sh:
- Fetch failures now abort instead of being silently swallowed
- Backup branch names include fork name + PID to prevent collisions
- Post-upgrade verification runs verify-fork-functions.sh and rolls
  back if upstream functions are missing

Fix bump-fork.sh:
- Replace wrong 'git tag -e' with 'git rev-parse --verify refs/tags/'
- Clone failures now error explicitly instead of silent fallback
- Version sed uses proper regex escaping for + characters
- Atomic swap has rollback recovery if mv fails
- Instructions now mention fork-upstream-map.toml update

Fix sync-upstream.sh:
- Define PROJECT_ROOT before use (was crashing under set -u)

Fix build-preflight.sh:
- Add REDBEAR_SKIP_FUNCTION_CHECK gate for verify-fork-functions.sh

Kernel submodule pointer updated to 0.3.1 branch with kfdwrite restored.
2026-07-11 23:18:41 +03:00

166 lines
6.1 KiB
Bash
Executable File

#!/usr/bin/env bash
# verify-fork-functions.sh — Verify that local forks retain ALL upstream functions.
#
# This is the FUNCTION-LEVEL verification that prevents the silent code-loss bug
# that occurred when git merge conflict resolution dropped upstream functions
# (kfdwrite, bulk_insert_files, resize, read_with_timeout, etc.) from the kernel fork.
#
# For each fork with an upstream remote, this script:
# 1. Extracts all function definitions (fn <name>) from upstream source files
# 2. Checks that each function exists in the local fork
# 3. Reports any missing functions with file paths and line numbers
#
# This catches the class of bugs that file-level verification CANNOT detect:
# when a file is touched by RB patches, file-level diff allows ANY difference,
# including silently dropped upstream functions.
#
# Usage:
# ./local/scripts/verify-fork-functions.sh # Check all forks
# ./local/scripts/verify-fork-functions.sh kernel relibc # Check specific forks
# ./local/scripts/verify-fork-functions.sh --no-fetch # Skip network fetch
# ./local/scripts/verify-fork-functions.sh --quiet # Only print violations
#
# Exit codes:
# 0 = All upstream functions present in all forks
# 1 = One or more upstream functions are missing from forks
set -uo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
NO_FETCH=0
QUIET=0
declare -a TARGET_FORKS=()
while [[ $# -gt 0 ]]; do
case "$1" in
--no-fetch) NO_FETCH=1 ;;
--quiet) QUIET=1 ;;
-h|--help)
echo "Usage: $0 [--no-fetch] [--quiet] [fork1 fork2 ...]"
echo ""
echo "Verifies that local forks retain ALL upstream functions."
echo "Catches silent code loss from bad merge conflict resolution."
exit 0
;;
--*) echo "Unknown: $1" >&2; exit 1 ;;
*) TARGET_FORKS+=("$1") ;;
esac
shift
done
cd "$PROJECT_ROOT"
# Discover forks if not specified
if [[ ${#TARGET_FORKS[@]} -eq 0 ]]; then
while IFS= read -r line; do
TARGET_FORKS+=("$line")
done < <(for d in local/sources/*/; do
[[ -d "$d/.git" ]] || continue
name=$(basename "$d")
git -C "$d" remote get-url upstream >/dev/null 2>&1 && echo "$name"
done | sort)
fi
VIOLATIONS=0
TOTAL_MISSING=0
for fork in "${TARGET_FORKS[@]}"; do
fork_dir="local/sources/${fork}"
if [[ ! -d "$fork_dir/.git" ]]; then
continue
fi
upstream_url=$(cd "$fork_dir" && git remote get-url upstream 2>/dev/null || true)
if [[ -z "$upstream_url" ]]; then
continue
fi
# Determine upstream branch
upstream_branch="master"
if ! (cd "$fork_dir" && git rev-parse --verify upstream/master >/dev/null 2>&1); then
if (cd "$fork_dir" && git rev-parse --verify upstream/main >/dev/null 2>&1); then
upstream_branch="main"
else
if [[ "$NO_FETCH" -eq 0 ]]; then
(cd "$fork_dir" && git fetch upstream --depth=200 --quiet 2>/dev/null) || true
fi
if (cd "$fork_dir" && git rev-parse --verify upstream/master >/dev/null 2>&1); then
upstream_branch="master"
elif (cd "$fork_dir" && git rev-parse --verify upstream/main >/dev/null 2>&1); then
upstream_branch="main"
else
continue
fi
fi
fi
# Fetch if needed
if [[ "$NO_FETCH" -eq 0 ]]; then
(cd "$fork_dir" && git fetch upstream --depth=200 --quiet 2>/dev/null) || true
fi
upstream_ref="upstream/$upstream_branch"
# Get list of source files that exist in BOTH upstream and our fork
upstream_files=$(cd "$fork_dir" && git ls-tree -r --name-only "$upstream_ref" 2>/dev/null | grep '\.rs$' | sort)
local_files=$(cd "$fork_dir" && find . -name '*.rs' -not -path './.git/*' -not -path './target/*' -not -path './stage/*' | sed 's|^\./||' | sort)
mapfile -t shared_files < <(comm -12 <(echo "$upstream_files") <(echo "$local_files"))
fork_missing=0
declare -a missing_details=()
for f in "${shared_files[@]}"; do
# Extract function names from upstream version of the file
upstream_fns=$(cd "$fork_dir" && git show "${upstream_ref}:${f}" 2>/dev/null | \
grep -oP '(?:pub )?(?:async )?(?:unsafe )?fn \w+' | \
sed 's/fn //' | sed 's/ *$//' | sort -u)
[[ -z "$upstream_fns" ]] && continue
# Extract function names from our version of the file
local_fns=$(cd "$fork_dir" && grep -oP '(?:pub )?(?:async )?(?:unsafe )?fn \w+' "$f" 2>/dev/null | \
sed 's/fn //' | sed 's/ *$//' | sort -u)
# Find functions in upstream but not in our fork
missing=$(comm -23 <(echo "$upstream_fns") <(echo "$local_fns") 2>/dev/null)
if [[ -n "$missing" ]]; then
while IFS= read -r fn; do
[[ -z "$fn" ]] && continue
missing_details+=(" $f: fn $fn")
fork_missing=$((fork_missing + 1))
TOTAL_MISSING=$((TOTAL_MISSING + 1))
done <<< "$missing"
fi
done
if [[ "$fork_missing" -gt 0 ]]; then
if [[ "$QUIET" -eq 0 ]] || [[ "$fork_missing" -gt 0 ]]; then
echo ""
echo -e "\033[1;31mFAIL: $fork is missing $fork_missing upstream function(s):\033[0m"
for detail in "${missing_details[@]}"; do
echo "$detail"
done
echo ""
echo " These functions exist in upstream $upstream_ref but are MISSING from the local fork."
echo " This likely means a bad merge or cherry-pick dropped them silently."
echo " Fix: ./local/scripts/upgrade-forks.sh $fork"
fi
VIOLATIONS=$((VIOLATIONS + 1))
elif [[ "$QUIET" -eq 0 ]]; then
echo -e "\033[1;32mOK: $fork — all upstream functions present\033[0m"
fi
done
echo ""
if [[ "$VIOLATIONS" -gt 0 ]]; then
echo -e "\033[1;31mFUNCTION VERIFICATION FAILED: $VIOLATIONS fork(s) missing $TOTAL_MISSING upstream function(s).\033[0m"
exit 1
fi
echo -e "\033[1;32mAll forks retain all upstream functions.\033[0m"
exit 0