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.
This commit is contained in:
2026-04-30 18:47:03 +01:00
parent 34360e1e4f
commit 7c7399e0a6
126 changed files with 13145 additions and 178 deletions
+3
View File
@@ -353,4 +353,7 @@ 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 ""
echo "==> Guarding recipe durability..."
./local/scripts/guard-recipes.sh --fix 2>/dev/null || echo " (guard-recipes.sh not found — run manually)"
echo " make all CONFIG_NAME=redbear-full"
+158
View File
@@ -0,0 +1,158 @@
#!/usr/bin/env bash
# Red Bear OS — Recipe Durability Guard
#
# PROBLEM: The build system ("cargo cook", "make distclean", "sync-upstream.sh")
# can delete or overwrite recipe.toml files in recipes/*/. This script
# ensures ALL custom recipes are backed in local/recipes/ and symlinked
# into the recipes/ tree properly.
#
# USAGE:
# ./local/scripts/guard-recipes.sh # Verify all recipes
# ./local/scripts/guard-recipes.sh --fix # Fix broken symlinks
# ./local/scripts/guard-recipes.sh --save-all # Save ALL recipe.toml files to local/
# ./local/scripts/guard-recipes.sh --restore # Restore all symlinks from local/
#
# RECOMMENDED: Run --fix before every build, --restore after every sync-upstream.
set -euo pipefail
REDOX_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
LOCAL_RECIPES="$REDOX_ROOT/local/recipes"
MAIN_RECIPES="$REDOX_ROOT/recipes"
MODE="${1:-}"
if [ -z "$MODE" ]; then
echo "Usage: $0 [--fix|--save-all|--restore|--verify]"
exit 1
fi
fix_symlink() {
local local_path="$1"
local target_recipe="$2"
local rel_path="${local_path#$LOCAL_RECIPES/}"
local recipe_name="$(dirname "$rel_path")"
# Remove leading category/name/recipe.toml to get just category/name
local package_dir="$(dirname "$rel_path")"
local main_recipe="$MAIN_RECIPES/$package_dir/recipe.toml"
if [ ! -d "$(dirname "$main_recipe")" ]; then
echo " SKIP: $main_recipe — target dir does not exist"
return
fi
if [ -L "$main_recipe" ]; then
local existing_target="$(readlink "$main_recipe")"
if [ "$existing_target" == "$target_recipe" ]; then
# echo " OK: $main_recipe"
return
fi
fi
if [ "$MODE" == "--fix" ]; then
rm -f "$main_recipe"
ln -sf "$target_recipe" "$main_recipe"
echo " FIXED: $main_recipe$target_recipe"
else
echo " BROKEN: $main_recipe (would fix with --fix)"
fi
}
echo "=== Red Bear OS Recipe Durability Guard ==="
echo "Local recipes: $LOCAL_RECIPES"
echo "Main recipes: $MAIN_RECIPES"
echo "Mode: $MODE"
echo ""
case "$MODE" in
--verify|--fix)
echo "Checking all local recipes..."
BROKEN=0
FIXED=0
find "$LOCAL_RECIPES" -name "recipe.toml" -type f | while read -r local_recipe; do
rel="${local_recipe#$LOCAL_RECIPES/}"
# Compute relative symlink path
depth=$(echo "$rel" | tr -cd '/' | wc -c)
up=""
for ((i=0; i<depth; i++)); do up="../$up"; done
target="$up$local_recipe"
main="$MAIN_RECIPES/${rel%/*}/recipe.toml"
if [ ! -L "$main" ] || [ "$(readlink "$main")" != "$target" ]; then
if [ "$MODE" == "--fix" ]; then
mkdir -p "$(dirname "$main")"
rm -f "$main"
ln -sf "$target" "$main"
echo " FIXED: $main"
FIXED=$((FIXED+1))
else
echo " BROKEN: $main → should link to $target"
BROKEN=$((BROKEN+1))
fi
fi
done
echo ""
if [ "$MODE" == "--fix" ]; then
echo "Symlinks fixed. Run after every sync-upstream."
else
echo "$BROKEN broken symlink(s). Run with --fix to repair."
fi
;;
--save-all)
echo "Saving ALL recipe.toml files from recipes/ to local/..."
SAVED=0
find "$MAIN_RECIPES" -name "recipe.toml" -type f -not -path "*/source/*" | while read -r recipe; do
rel="${recipe#$MAIN_RECIPES/}"
local_dest="$LOCAL_RECIPES/$rel"
# Skip if already in local/
if [ "$recipe" == "$local_dest" ]; then
continue
fi
if [ -f "$local_dest" ]; then
continue # Already saved
fi
if [ -L "$recipe" ]; then
# It's a symlink — already backed
continue
fi
mkdir -p "$(dirname "$local_dest")"
cp "$recipe" "$local_dest"
echo " SAVED: $rel"
SAVED=$((SAVED+1))
done
echo ""
echo "$SAVED recipe(s) saved to local/recipes/."
echo "Now run: $0 --fix to replace with symlinks."
;;
--restore)
echo "Restoring all symlinks from local/recipes/..."
find "$LOCAL_RECIPES" -name "recipe.toml" -type f | while read -r local_recipe; do
rel="${local_recipe#$LOCAL_RECIPES/}"
depth=$(echo "$rel" | tr -cd '/' | wc -c)
up=""
for ((i=0; i<depth; i++)); do up="../$up"; done
target="$up$local_recipe"
main="$MAIN_RECIPES/${rel%/*}/recipe.toml"
mkdir -p "$(dirname "$main")"
rm -f "$main"
ln -sf "$target" "$main"
echo " RESTORED: $main"
done
echo "All symlinks restored."
;;
*)
echo "Unknown mode: $MODE"
echo "Use: --verify, --fix, --save-all, or --restore"
exit 1
;;
esac
+2
View File
@@ -276,6 +276,8 @@ 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 ""