Files
RedBear-OS/local/scripts/guard-recipes.sh
T
vasilito e26c8e1ef6 review-fixes: harden sessiond power, notifications capabilities, fork verifier, guard-recipes
Companion to the prior 'address 5-lane review blocking findings' commit
(which only contained the restored dbus symlink). This commit bundles the
remaining review-driven fixes for the implementation scope.

- sessiond power_off/reboot/suspend: propagate write_all errors. The
  'let _ = f.write_all(...)' pattern meant a successful open followed
  by a failed write was reported to the caller as success. Now checked
  with explicit error propagation: write failure resets
  preparing_for_shutdown/sleep to false and returns a D-Bus error.
- notifications: drop un-implemented capability advertisement
  ('actions', 'persistence'). Real capabilities now: ['body', 'body-markup'].
  Body and app_name no longer printed to stderr verbatim (length only).
  Server info version bumped 0.3.0 -> 0.3.1 to match Cargo.toml.
- wifictl: enable dbus-nm feature by default. Previously
  default = [] made the 656-line NM interface dead code behind a
  feature gate that the recipe never enabled.
- guard-recipes.sh --restore: add parent-symlink guard (same as --fix).
  Without it, restoring a recipe whose parent directory is a symlink
  into local/recipes/ deletes the real file from disk.
- verify-fork-functions.sh: narrow blanket exclusion to fmt+eq only.
  drop/deref/hash/clone/etc. must go through fork-specific exclude file.

Verified:
  redbear-notifications:  8/8 tests pass
  redbear-sessiond:      51/52 tests pass (1 pre-existing failure
                         from later commit a9e1c34e27 outside scope)
  redbear-wifictl:       compiles with dbus-nm default
2026-07-27 20:43:32 +09:00

181 lines
6.5 KiB
Bash
Executable File

#!/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/}"
main="$MAIN_RECIPES/${rel%/*}/recipe.toml"
# Relative symlink target from the link's directory to the real
# recipe under local/recipes/. Computed with realpath so the depth
# is always correct. The previous "$up$local_recipe" form glued a
# relative prefix onto an ABSOLUTE path (local_recipe comes from
# `find "$LOCAL_RECIPES"`, which is absolute), producing malformed
# dangling links like "../..//mnt/.../recipe.toml".
target="$(realpath -m --relative-to="$(dirname "$main")" "$local_recipe")"
if [ ! -L "$main" ] || [ "$(readlink "$main")" != "$target" ]; then
# If the parent directory is a symlink into local/recipes/, the
# recipe.toml is already accessible through it. Creating a file-level
# symlink here would resolve through the dir symlink and corrupt
# the real file in local/recipes/.
main_dir="$(dirname "$main")"
if [ -L "$main_dir" ]; then
dir_target="$(readlink "$main_dir")"
if [[ "$dir_target" == *local/recipes/* ]]; then
# Already covered by directory symlink — skip
continue
fi
fi
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/}"
main="$MAIN_RECIPES/${rel%/*}/recipe.toml"
# Same parent-symlink guard as --fix: a directory symlink into
# local/recipes/ already exposes this recipe; replacing it with
# a file symlink would resolve through the dir symlink and the
# subsequent rm -f would delete the real local/recipes/ file.
main_dir="$(dirname "$main")"
if [ -L "$main_dir" ]; then
dir_target="$(readlink "$main_dir")"
if [[ "$dir_target" == *local/recipes/* ]]; then
continue
fi
fi
target="$(realpath -m --relative-to="$(dirname "$main")" "$local_recipe")"
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