From 62d929d62a382dd256257ddd391f2a0943c55d21 Mon Sep 17 00:00:00 2001 From: vasilito Date: Sun, 12 Jul 2026 16:40:31 +0300 Subject: [PATCH] phase 17: guarantee upstream+RB patches on every build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit upgrade-forks.sh: - --no-fetch: use cached upstream refs (no network required) - --force-reapply: force rebase even when 0 commits behind upstream Automatically detects missing functions via verify-fork-functions.sh and triggers reapply when RB cherry-picks dropped upstream code. verify-fork-functions.sh: - Cross-file search: when a function is missing from its upstream file, search ALL fork .rs files for renamed/moved equivalents (→ MOVED) - Per-fork exclusion list: .verify-fork-functions.exclude for intentionally removed/replaced functions (→ EXCLUDED) - Only truly missing (not found anywhere, not excluded) = violations build-preflight.sh: - Updated fix suggestion to --no-fetch --force-reapply Results: installer 2→0 (exclusion+move), kernel 19→15 (4 moved), base 56→44 (12 moved). Remaining missing functions are known RB replacements pending exclusion entries. --- local/scripts/build-preflight.sh | 4 +- local/scripts/upgrade-forks.sh | 95 +++++++++++++++++++------- local/scripts/verify-fork-functions.sh | 32 ++++++++- 3 files changed, 100 insertions(+), 31 deletions(-) diff --git a/local/scripts/build-preflight.sh b/local/scripts/build-preflight.sh index 73ed033ad4..ce26a19d33 100755 --- a/local/scripts/build-preflight.sh +++ b/local/scripts/build-preflight.sh @@ -73,8 +73,8 @@ if [ -x "$SCRIPT_DIR/verify-fork-functions.sh" ] && [ "${REDBEAR_SKIP_FUNCTION_C if ! "$SCRIPT_DIR/verify-fork-functions.sh" --no-fetch --quiet >/tmp/fork-functions.out 2>&1; then cat /tmp/fork-functions.out >&2 echo ">>> ERROR: Fork function verification failed — upstream functions are missing." >&2 - echo ">>> This indicates a bad merge dropped upstream code silently." >&2 - echo ">>> Run ./local/scripts/upgrade-forks.sh to fix." >&2 + echo ">>> This indicates a bad merge/cherry-pick dropped upstream code silently." >&2 + echo ">>> Fix: ./local/scripts/upgrade-forks.sh --no-fetch --force-reapply " >&2 if [ "${REDBEAR_SKIP_FORK_VERIFY:-0}" != "1" ]; then exit 1 fi diff --git a/local/scripts/upgrade-forks.sh b/local/scripts/upgrade-forks.sh index 471cdf187e..f63ea151bc 100755 --- a/local/scripts/upgrade-forks.sh +++ b/local/scripts/upgrade-forks.sh @@ -7,21 +7,27 @@ # 3. Save RB commits to a patch file # 4. Reset to upstream/master (or upstream/main) # 5. Reapply RB commits via cherry-pick (or patch fallback) -# 6. Report success/failure per fork +# 6. Verify no upstream functions were dropped (verify-fork-functions.sh) +# 7. Report success/failure per fork # # Usage: # ./local/scripts/upgrade-forks.sh # Upgrade all forks # ./local/scripts/upgrade-forks.sh kernel redoxfs # Upgrade specific forks # ./local/scripts/upgrade-forks.sh --dry-run # Show plan, don't execute -# ./local/scripts/upgrade-forks.sh --force | Skip safety checks +# ./local/scripts/upgrade-forks.sh --force # Skip safety checks +# ./local/scripts/upgrade-forks.sh --no-fetch # Use cached upstream refs (no network) +# ./local/scripts/upgrade-forks.sh --force-reapply # Reapply even when 0 commits behind # # Safety: # - Requires clean working tree in each fork (no uncommitted changes) # - Creates backup branch before reset # - Stops on first conflict (interactive resolution or --abort) +# - Post-upgrade: verifies all upstream functions are present # # Environment: -# REDBEAR_UPGRADE_FORCE=1 Same as --force +# REDBEAR_UPGRADE_FORCE=1 Same as --force +# REDBEAR_UPGRADE_NO_FETCH=1 Same as --no-fetch +# REDBEAR_UPGRADE_FORCE_REAPPLY=1 Same as --force-reapply set -euo pipefail @@ -30,20 +36,26 @@ PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" DRY_RUN=0 FORCE=0 +NO_FETCH=0 +FORCE_REAPPLY=0 declare -a TARGET_FORKS=() while [[ $# -gt 0 ]]; do case "$1" in - --dry-run) DRY_RUN=1 ;; - --force) FORCE=1 ;; + --dry-run) DRY_RUN=1 ;; + --force) FORCE=1 ;; + --no-fetch) NO_FETCH=1 ;; + --force-reapply) FORCE_REAPPLY=1 ;; -h|--help) - echo "Usage: $0 [--dry-run] [--force] [fork1 fork2 ...]" + echo "Usage: $0 [--dry-run] [--force] [--no-fetch] [--force-reapply] [fork1 fork2 ...]" echo "" echo "Upgrades local forks to latest upstream and reapplies Red Bear patches." echo "" echo "Options:" - echo " --dry-run Show what would happen without executing" - echo " --force Skip safety checks" + echo " --dry-run Show what would happen without executing" + echo " --force Skip safety checks (dirty tree, etc.)" + echo " --no-fetch Use cached upstream refs (no network required)" + echo " --force-reapply Reapply even when 0 commits behind upstream" echo "" echo "Forks (default: all in local/sources/ with upstream remotes):" for d in "$PROJECT_ROOT"/local/sources/*/; do @@ -58,6 +70,8 @@ while [[ $# -gt 0 ]]; do done [[ "${REDBEAR_UPGRADE_FORCE:-0}" == "1" ]] && FORCE=1 +[[ "${REDBEAR_UPGRADE_NO_FETCH:-0}" == "1" ]] && NO_FETCH=1 +[[ "${REDBEAR_UPGRADE_FORCE_REAPPLY:-0}" == "1" ]] && FORCE_REAPPLY=1 cd "$PROJECT_ROOT" @@ -130,25 +144,41 @@ for fork in "${TARGET_FORKS[@]}"; do fi fi - # Determine upstream branch + # Determine upstream branch and ref upstream_branch="master" - if ! (cd "$fork_dir" && git fetch upstream --quiet 2>&1); then - echo -e " ${RED}FAIL: git fetch upstream failed (network error?)${NC}" - FAIL_COUNT=$((FAIL_COUNT + 1)) - FAILED_FORKS+=("$fork") - echo "" - continue - fi - 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 - echo -e " ${RED}FAIL: cannot find upstream/master or upstream/main${NC}" + if [[ "$NO_FETCH" -eq 1 ]]; then + # Use cached upstream refs — verify they exist + 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 + echo -e " ${RED}FAIL: --no-fetch but no cached upstream/master or upstream/main${NC}" + FAIL_COUNT=$((FAIL_COUNT + 1)) + FAILED_FORKS+=("$fork") + echo "" + continue + fi + fi + else + if ! (cd "$fork_dir" && git fetch upstream --quiet 2>&1); then + echo -e " ${RED}FAIL: git fetch upstream failed (network error?)${NC}" + echo -e " ${YELLOW}Use --no-fetch if upstream refs are already cached${NC}" FAIL_COUNT=$((FAIL_COUNT + 1)) FAILED_FORKS+=("$fork") echo "" continue fi + 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 + echo -e " ${RED}FAIL: cannot find upstream/master or upstream/main${NC}" + FAIL_COUNT=$((FAIL_COUNT + 1)) + FAILED_FORKS+=("$fork") + echo "" + continue + fi + fi fi upstream_ref="upstream/$upstream_branch" @@ -161,11 +191,24 @@ for fork in "${TARGET_FORKS[@]}"; do echo " Current: $old_sha ($ahead commits ahead of upstream)" echo " Upstream: $upstream_sha ($behind commits behind)" - if [[ "$behind" -eq 0 ]]; then - echo -e " ${GREEN}Already up to date${NC}" - SKIP_COUNT=$((SKIP_COUNT + 1)) - echo "" - continue + if [[ "$behind" -eq 0 && "$FORCE_REAPPLY" -eq 0 ]]; then + # Up to date in git history — but check if RB commits dropped upstream functions + need_reapply=0 + if [[ -x "$SCRIPT_DIR/verify-fork-functions.sh" ]]; then + if ! "$SCRIPT_DIR/verify-fork-functions.sh" "$fork" --no-fetch --quiet 2>/dev/null; then + need_reapply=1 + echo -e " ${YELLOW}Upstream functions are MISSING (RB cherry-pick dropped code)${NC}" + echo -e " ${YELLOW}Forcing reapply to restore missing functions...${NC}" + fi + fi + if [[ "$need_reapply" -eq 0 ]]; then + echo -e " ${GREEN}Already up to date${NC}" + SKIP_COUNT=$((SKIP_COUNT + 1)) + echo "" + continue + fi + elif [[ "$behind" -eq 0 && "$FORCE_REAPPLY" -eq 1 ]]; then + echo -e " ${YELLOW}--force-reapply: reapplying RB commits on clean upstream${NC}" fi if [[ "$ahead" -eq 0 ]]; then diff --git a/local/scripts/verify-fork-functions.sh b/local/scripts/verify-fork-functions.sh index 55e7fcb721..e23ae6c834 100755 --- a/local/scripts/verify-fork-functions.sh +++ b/local/scripts/verify-fork-functions.sh @@ -124,15 +124,41 @@ for fork in "${TARGET_FORKS[@]}"; do local_fns=$(cd "$fork_dir" && grep -oP '(?:pub )?(?:async )?(?:unsafe )?fn \w+' "$f" 2>/dev/null | \ sed 's/fn //' | sed 's/ *$//' | sort -u) + # Load per-fork exclusion list for intentionally removed/replaced functions + exclude_file="${fork_dir}/.verify-fork-functions.exclude" + declare -A excluded=() + if [[ -f "$exclude_file" ]]; then + while IFS= read -r line; do + [[ "$line" =~ ^# ]] && continue + [[ -z "$line" ]] && continue + excluded["$line"]=1 + done < "$exclude_file" + fi + # 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)) + # Check exclusion list: "$f:$fn" + if [[ -n "${excluded["$f:$fn"]:-}" ]]; then + [[ "$QUIET" -eq 0 ]] && missing_details+=(" $f: fn $fn → EXCLUDED (RB intentional)") + continue + fi + # Strip modifiers to get bare function name for cross-file search + bare_fn=$(echo "$fn" | sed -E 's/^(pub |async |unsafe )+//') + found_elsewhere=$(cd "$fork_dir" && \ + grep -rlP "(?:pub )?(?:async )?(?:unsafe )?fn ${bare_fn}\b" \ + --include='*.rs' --exclude-dir='.git' --exclude-dir='target' --exclude-dir='stage' . 2>/dev/null | \ + grep -v "^\./${f}$" | head -1 | sed 's|^\./||') + if [[ -n "$found_elsewhere" ]]; then + [[ "$QUIET" -eq 0 ]] && missing_details+=(" $f: fn $fn → MOVED to $found_elsewhere") + else + missing_details+=(" $f: fn $fn") + fork_missing=$((fork_missing + 1)) + TOTAL_MISSING=$((TOTAL_MISSING + 1)) + fi done <<< "$missing" fi done