Files
RedBear-OS/local/scripts/sync-upstream.sh
T
vasilito 7c7399e0a6 feat: recipe durability guard — prevents build system from deleting local recipes
Add guard-recipes.sh with four modes:
- --verify: check all local/recipes have correct symlinks into recipes/
- --fix: repair broken symlinks (run before builds)
- --save-all: snapshot all recipe.toml into local/recipes/
- --restore: recreate all symlinks from local/recipes/ (run after sync-upstream)

Wired into apply-patches.sh (post-patch) and sync-upstream.sh (post-sync).
This prevents the build system from deleting recipe files during
cargo cook, make distclean, or upstream source refresh.
2026-04-30 18:47:03 +01:00

285 lines
12 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# sync-upstream.sh — Update from upstream Redox and reapply Red Bear OS overlays.
#
# Usage:
# ./local/scripts/sync-upstream.sh # Rebase onto upstream master
# ./local/scripts/sync-upstream.sh --dry-run # Preview what would change
# ./local/scripts/sync-upstream.sh --no-merge # Only fetch + check for conflicts
#
# Strategy: git rebase (preserves Red Bear OS commits, replays on new upstream).
# Fallback: if rebase fails, patches in local/patches/build-system/ can be
# applied from scratch via: ./local/scripts/apply-patches.sh --force
#
# IMPORTANT: upstream WIP recipes are not treated as durable shipping inputs by Red Bear.
# After upstream sync, Red Bear-owned WIP work still needs to come from local/recipes/ and
# local/patches/, not from trust in recipes/wip/ alone.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
UPSTREAM_URL="${UPSTREAM_URL:-https://github.com/redox-os/redox.git}"
UPSTREAM_REMOTE="upstream-redox"
UPSTREAM_BRANCH="${UPSTREAM_BRANCH:-master}"
DRY_RUN=0
NO_MERGE=0
FORCE=0
usage() {
echo "Usage: $0 [--dry-run] [--no-merge] [--force]"
echo " --dry-run Show what would happen without making changes"
echo " --no-merge Only fetch and check patch conflicts"
echo " --force Skip safety checks (uncommitted local/ changes)"
}
for arg in "$@"; do
case "$arg" in
--dry-run) DRY_RUN=1 ;;
--no-merge) NO_MERGE=1 ;;
--force) FORCE=1 ;;
--help|-h)
usage
exit 0
;;
*)
echo "Unknown argument: $arg"
usage >&2
exit 1
;;
esac
done
cd "$REPO_ROOT"
# ── 1. Ensure upstream remote ───────────────────────────────────────
if ! git remote get-url "$UPSTREAM_REMOTE" &>/dev/null; then
echo "==> Adding upstream remote: $UPSTREAM_URL"
[ "$DRY_RUN" = "0" ] && git remote add "$UPSTREAM_REMOTE" "$UPSTREAM_URL"
fi
echo "==> Fetching $UPSTREAM_REMOTE/$UPSTREAM_BRANCH..."
[ "$DRY_RUN" = "0" ] && git fetch "$UPSTREAM_REMOTE" "$UPSTREAM_BRANCH"
UPSTREAM_REF="${UPSTREAM_REMOTE}/${UPSTREAM_BRANCH}"
# ── 2. Check patch conflicts with upstream changes ──────────────────
MERGE_BASE=$(git merge-base HEAD "$UPSTREAM_REF" 2>/dev/null || echo "")
if [ -n "$MERGE_BASE" ]; then
CHANGED_FILES=$(git diff --name-only "$MERGE_BASE" "$UPSTREAM_REF" 2>/dev/null || true)
CHANGE_COUNT=$(echo "$CHANGED_FILES" | grep -c . 2>/dev/null || echo "0")
echo " $CHANGE_COUNT files changed upstream since common ancestor"
if [ -n "$CHANGED_FILES" ] && [ -d local/patches ]; then
echo ""
echo "==> Checking patch conflict risks..."
for patch_file in local/patches/build-system/[0-9]*.patch; do
[ -f "$patch_file" ] || continue
PATCH_NAME=$(basename "$patch_file")
PATCHED_FILES=$(grep '^--- a/' "$patch_file" 2>/dev/null | sed 's|^--- a/||' | sort -u || true)
for pf in $PATCHED_FILES; do
if echo "$CHANGED_FILES" | grep -q "$pf" 2>/dev/null; then
echo " ⚠ CONFLICT RISK: $PATCH_NAME modifies $pf (also changed upstream)"
fi
done
done
for patch_dir in local/patches/kernel local/patches/base; do
[ -f "$patch_dir/redox.patch" ] || continue
echo " $patch_dir/redox.patch — check manually if kernel/base changed upstream"
done
fi
else
echo " WARNING: Could not find common ancestor with upstream"
fi
# ── 3. Summary ─────────────────────────────────────────────────────
AHEAD=$(git rev-list --count "$UPSTREAM_REF..HEAD" 2>/dev/null || echo "?")
BEHIND=$(git rev-list --count "HEAD..$UPSTREAM_REF" 2>/dev/null || echo "?")
echo ""
echo "=== Sync Summary ==="
echo "Upstream: $UPSTREAM_REF"
echo "Local: HEAD ($(git rev-parse --short HEAD))"
echo "Ahead: $AHEAD Red Bear OS commits"
echo "Behind: $BEHIND upstream commits"
if [ "$NO_MERGE" = 1 ]; then
echo ""
echo "To merge manually:"
echo " git rebase $UPSTREAM_REF"
exit 0
fi
if [ "$DRY_RUN" = "1" ]; then
echo ""
echo " [dry-run] Would rebase onto $UPSTREAM_REF"
exit 0
fi
# ── 3.5. Check for uncommitted local/ changes ──────────────────────
if [ "$NO_MERGE" = "0" ] && [ "$DRY_RUN" = "0" ]; then
LOCAL_CHANGES=""
LOCAL_UNTRACKED=""
if [ -d "local" ]; then
LOCAL_CHANGES=$(cd local && git diff --name-only HEAD 2>/dev/null || true)
LOCAL_UNTRACKED=$(cd local && git ls-files --others --exclude-standard 2>/dev/null || true)
fi
# Also check for uncommitted changes to tracked local/ files from repo root
ROOT_LOCAL_CHANGES=$(git diff --name-only HEAD -- local/ 2>/dev/null || true)
if [ -n "$LOCAL_CHANGES" ] || [ -n "$LOCAL_UNTRACKED" ] || [ -n "$ROOT_LOCAL_CHANGES" ]; then
echo ""
echo "!! WARNING: Uncommitted changes detected in local/"
if [ -n "$ROOT_LOCAL_CHANGES" ]; then
echo " Modified files:"
echo "$ROOT_LOCAL_CHANGES" | head -10 | while read -r f; do echo " $f"; done
TOTAL=$(echo "$ROOT_LOCAL_CHANGES" | grep -c .)
[ "$TOTAL" -gt 10 ] && echo " ... and $((TOTAL - 10)) more"
fi
if [ -n "$LOCAL_UNTRACKED" ]; then
echo " Untracked files (NOT protected by stash):"
echo "$LOCAL_UNTRACKED" | head -5 | while read -r f; do echo " $f"; done
TOTAL=$(echo "$LOCAL_UNTRACKED" | grep -c .)
[ "$TOTAL" -gt 5 ] && echo " ... and $((TOTAL - 5)) more"
fi
echo ""
echo " git stash does NOT protect untracked files."
echo " Commit your local/ changes before syncing, or use --force to proceed anyway."
if [ "$FORCE" = "0" ]; then
echo ""
echo " ABORT: Uncommitted local/ changes detected."
echo " Commit your changes first: git add local/ && git commit -m 'WIP'"
echo " Or use --force if you understand the risks (untracked files will be LOST)."
exit 1
else
# --force with untracked files requires explicit confirmation
if [ -n "$LOCAL_UNTRACKED" ]; then
echo ""
echo "!! DANGER: --force with untracked files will DELETE them permanently. !!"
echo " git stash does NOT protect untracked files."
echo " Untracked files found:"
echo "$LOCAL_UNTRACKED" | head -10 | while read -r f; do echo " $f"; done
TOTAL=$(echo "$LOCAL_UNTRACKED" | grep -c .)
[ "$TOTAL" -gt 10 ] && echo " ... and $((TOTAL - 10)) more"
echo ""
read -p " Type 'YES_DELETE' to confirm destruction of untracked local/ files: " CONFIRM
if [ "$CONFIRM" != "YES_DELETE" ]; then
echo " Aborted. Your untracked files are safe."
exit 1
fi
echo " Proceeding with --force — untracked files WILL be deleted..."
else
echo " --force specified, proceeding (tracked changes will be stashed)..."
fi
fi
fi
fi
# ── 4. Stash uncommitted changes ────────────────────────────────────
STASHED=0
if ! git diff --quiet 2>/dev/null || ! git diff --cached --quiet 2>/dev/null; then
echo "==> Stashing uncommitted changes..."
git stash push -u -m "redbear-sync-$(date +%Y%m%d-%H%M%S)"
STASHED=1
fi
PREV_HEAD=$(git rev-parse HEAD)
# ── 4.5. Verify overlay integrity before rebase ────────────────────
echo "==> Verifying Red Bear overlay integrity before rebase..."
BROKEN_SYMLINKS=""
while IFS= read -r link; do
if [ ! -e "$link" ]; then
BROKEN_SYMLINKS="$BROKEN_SYMLINKS
$link -> $(readlink "$link")"
fi
done < <(find recipes -maxdepth 3 -type l 2>/dev/null)
if [ -n "$BROKEN_SYMLINKS" ]; then
echo "!! WARNING: Broken symlinks detected in recipes/:"
echo "$BROKEN_SYMLINKS" | head -20
TOTAL=$(echo "$BROKEN_SYMLINKS" | grep -c .)
[ "$TOTAL" -gt 20 ] && echo " ... and $((TOTAL - 20)) more"
echo ""
echo " These symlinks may break further during rebase."
echo " Run ./local/scripts/apply-patches.sh after rebase to recreate them."
fi
# Check that key local/patches exist
for patch_file in local/patches/kernel/redox.patch local/patches/base/redox.patch local/patches/relibc/redox.patch; do
if [ ! -f "$patch_file" ]; then
echo "!! CRITICAL: Missing patch file: $patch_file"
echo " Cannot recover from rebase failure without this patch."
if [ "$FORCE" = "0" ]; then
exit 1
fi
fi
done
# ── 5. Rebase ───────────────────────────────────────────────────────
echo ""
echo "==> Rebasing Red Bear OS commits onto $UPSTREAM_REF..."
echo " (this replays our $AHEAD commits on top of updated upstream)"
if git rebase "$UPSTREAM_REF"; then
echo ""
echo "==> Rebase successful."
else
echo ""
echo "!! Rebase conflict. Options:"
echo " 1. Resolve conflicts: edit files, git add, git rebase --continue"
echo " 2. Abort: git rebase --abort"
echo " 3. Nuclear option (DESTRUCTIVE — loses uncommitted work):"
echo " git rebase --abort"
echo " git reset --hard $UPSTREAM_REF"
echo " ./local/scripts/apply-patches.sh --force"
echo ""
echo " Patches for recovery: local/patches/build-system/"
echo " Previous HEAD: $PREV_HEAD"
echo ""
echo " IMPORTANT: Before using the nuclear option, ensure all local/ changes"
echo " are committed. The nuclear option does NOT preserve uncommitted work."
echo " To recover to previous state: git reset --hard $PREV_HEAD"
exit 1
fi
# ── 6. Restore stash ────────────────────────────────────────────────
if [ "$STASHED" = 1 ]; then
echo "==> Restoring stashed changes..."
if git stash pop; then
echo " Stash restored successfully."
else
echo "!! Stash pop had conflicts."
echo " Your changes are preserved in the stash."
echo " Options:"
echo " 1. Resolve conflicts in the working tree"
echo " 2. git checkout --theirs . && git stash drop"
echo " 3. git reset --hard && git stash pop (try again on clean tree)"
echo " List stashes: git stash list"
fi
fi
# ── 7. Verify symlinks ─────────────────────────────────────────────
echo "==> Verifying recipe patch symlinks..."
if [ -f local/scripts/apply-patches.sh ]; then
bash local/scripts/apply-patches.sh
else
echo " apply-patches.sh not found — verify symlinks manually"
ls -la recipes/core/kernel/redox.patch recipes/core/base/redox.patch
fi
if [ -x local/scripts/verify-overlay-integrity.sh ]; then
echo "==> Verifying overlay integrity..."
local/scripts/verify-overlay-integrity.sh --repair
fi
echo ""
echo "==> Sync complete."
echo "==> Guarding recipe durability..."
./local/scripts/guard-recipes.sh --restore 2>/dev/null || echo " (guard-recipes.sh not found — run manually)"
echo " Previous HEAD: $PREV_HEAD"
echo " New HEAD: $(git rev-parse HEAD)"
echo ""
echo "Next: make all CONFIG_NAME=redbear-full"