diff --git a/local/scripts/apply-patches.sh b/local/scripts/apply-patches.sh index f8fef08e..0720f3a0 100755 --- a/local/scripts/apply-patches.sh +++ b/local/scripts/apply-patches.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # apply-patches.sh — Apply all Red Bear OS overlays on top of upstream Redox build system. # -# Usage: ./local/scripts/apply-patches.sh [--force] +# Usage: ./local/scripts/apply-patches.sh [--force] [--dry-run] # # This script: # 1. Applies build-system patches (rebranding, cookbook fixes, config, docs) @@ -13,6 +13,7 @@ # instead. This script therefore treats the local overlay as the durable source of truth. # # With --force: reapplies even if patches appear already applied. +# With --dry-run: shows what would change without making changes. # # SAFE: does not touch local/ directory. Only modifies upstream files. @@ -21,11 +22,37 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" PATCHES_DIR="$REPO_ROOT/local/patches" -FORCE="${1:-}" +FORCE="" +DRY_RUN=0 cd "$REPO_ROOT" # ── Helper ────────────────────────────────────────────────────────── +usage() { + cat <<'EOF' +Usage: ./local/scripts/apply-patches.sh [--force] [--dry-run] + +Options: + --force attempt re-application even when checks fail + --dry-run show what would change without modifying files +EOF +} + +for arg in "$@"; do + case "$arg" in + --force) FORCE="--force" ;; + --dry-run) DRY_RUN=1 ;; + -h|--help) + usage + exit 0 + ;; + *) + usage + exit 1 + ;; + esac +done + symlink() { local target="$1" link="$2" if [ -L "$link" ]; then @@ -34,32 +61,56 @@ symlink() { return 0 # already correct fi fi + if [ "$DRY_RUN" = "1" ]; then + dry_run_msg "would remove and relink $link -> $target" + return 0 + fi rm -f "$link" ln -s "$target" "$link" echo " linked $link -> $target" } +dry_run_msg() { + if [ "$DRY_RUN" = "1" ]; then + echo " [dry-run] $*" + fi +} + # ── 1. Build-system patches ───────────────────────────────────────── echo "==> Applying build-system patches..." for patch_file in "$PATCHES_DIR"/build-system/[0-9]*.patch; do [ -f "$patch_file" ] || continue patch_name="$(basename "$patch_file")" - # Check if already applied (skip unless --force) - if [ "$FORCE" != "--force" ]; then - if git apply --check "$patch_file" 2>/dev/null; then - : # patch applies cleanly, apply it - else - echo " SKIP $patch_name (already applied or conflicts)" - echo " Use --force to attempt re-application" + # Check if patch applies cleanly + if ! git apply --check "$patch_file" 2>/dev/null; then + # Patch does NOT apply cleanly — check if it's already applied + if git apply --reverse --check "$patch_file" 2>/dev/null; then + echo " SKIP $patch_name (already applied)" continue fi + # Patch conflicts and is NOT already applied — this is a real problem + echo " FAIL $patch_name — conflicts with current tree" + echo " This likely means upstream has changed the target files." + echo " Patch file: $patch_file" + echo " Options:" + echo " 1. Resolve conflicts: edit the patch file to match new upstream" + echo " 2. Use --force to attempt re-application (may fail)" + if [ "$FORCE" != "--force" ]; then + echo " ABORT: unresolved patch conflict (use --force to override)" + exit 1 + fi + fi + + if [ "$DRY_RUN" = "1" ]; then + dry_run_msg "would apply $patch_name" + continue fi if git apply --whitespace=nowarn "$patch_file"; then echo " OK $patch_name" else - echo " FAIL $patch_name — resolve conflicts manually" + echo " FAIL $patch_name — apply failed (check conflicts above)" echo " Patch file: $patch_file" exit 1 fi @@ -138,7 +189,13 @@ symlink "../../local/recipes/core/grub" "recipes/core/grub" # so redirect the entire directory to our local overlay to ensure # COOKBOOK_RECIPE resolves to a directory that contains grub.cfg if [ -d "recipes/wip/services/grub" ] && [ ! -L "recipes/wip/services/grub" ]; then - rm -rf "recipes/wip/services/grub" + backup_name="recipes/wip/services/grub.upstream-backup-$(date +%Y%m%d-%H%M%S)" + if [ "$DRY_RUN" = "1" ]; then + dry_run_msg "would backup recipes/wip/services/grub to $backup_name and replace with symlink" + else + echo " backing up upstream recipes/wip/services/grub -> $backup_name" + mv "recipes/wip/services/grub" "$backup_name" + fi fi if [ ! -e "recipes/wip/services/grub" ]; then symlink "../../../local/recipes/core/grub" "recipes/wip/services/grub" @@ -200,24 +257,31 @@ echo "==> Ensuring Red Bear OS-specific files exist..." # redbear.ipxe (network boot) if [ ! -f redbear.ipxe ] && [ ! -L redbear.ipxe ]; then - cat > redbear.ipxe <<'IPXE' + if [ "$DRY_RUN" = "1" ]; then + dry_run_msg "would create redbear.ipxe" + else + cat > redbear.ipxe <<'IPXE' #!ipxe kernel bootloader-live.efi initrd http://${next-server}:8080/redbear-live.iso boot IPXE - echo " created redbear.ipxe" + echo " created redbear.ipxe" + fi fi # redbear-full config (not in upstream) if [ ! -f config/redbear-full.toml ] && [ ! -L config/redbear-full.toml ]; then - cat > config/redbear-full.toml <<'TOML' + if [ "$DRY_RUN" = "1" ]; then + dry_run_msg "would create config/redbear-full.toml" + else + cat > config/redbear-full.toml <<'TOML' # Red Bear OS Full Configuration # Complete desktop + all Red Bear OS custom drivers and tools # # Build: make all CONFIG_NAME=redbear-full -# Live: make live CONFIG_NAME=redbear-full +# Live: make live CONFIG_NAME=redbear-live-full include = ["desktop.toml"] @@ -249,9 +313,15 @@ amdgpu = {} # Red Bear OS meta-package (dependencies, default config) redbear-meta = {} TOML - echo " created config/redbear-full.toml" + echo " created config/redbear-full.toml" + fi +else + echo " config/redbear-full.toml already exists — not overwriting" fi echo "" echo "==> All Red Bear OS patches applied. Ready to build." +if [ "$DRY_RUN" = "1" ]; then + echo " [dry-run mode — no changes were made]" +fi echo " make all CONFIG_NAME=redbear-full" diff --git a/local/scripts/sync-upstream.sh b/local/scripts/sync-upstream.sh index 1982037c..923cea41 100755 --- a/local/scripts/sync-upstream.sh +++ b/local/scripts/sync-upstream.sh @@ -23,17 +23,20 @@ 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]" + 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 @@ -112,16 +115,88 @@ if [ "$DRY_RUN" = "1" ]; then 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. Use --force to override." + exit 1 + else + echo " --force specified, proceeding anyway..." + 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 -m "redbear-sync-$(date +%Y%m%d-%H%M%S)" + 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..." @@ -135,20 +210,34 @@ else 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:" + 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..." - git stash pop || echo " (stash pop had conflicts — resolve manually)" + 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 ─────────────────────────────────────────────