Files
RedBear-OS/local/scripts/check-prefix-freshness.sh
T
vasilito 6f8dd561e2 scripts: extend check-prefix-freshness.sh with dead-symlink scan
The verify-overlay-integrity.sh script has two blind spots for stale
symlink detection:
  1. find recipes -maxdepth 3 misses paths at depth 4+
  2. only checks targets under /local/recipes/, misses /local/patches/

This adds a parallel scan that finds all dangling symlinks under recipes/
without maxdepth, classifies them by target path, and reports each with a
suggested action. Pure diagnostic — does not modify any files.

Enables preventive detection of the recipes/wip/x11/mesa-x11/source
orphan and recipes/core/base/redox.patch pointer-rot that triggered the
build integrity warnings on this session's rebuilds.

(NO AI attribution in commit message.)
2026-07-13 19:25:14 +03:00

166 lines
5.7 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
project_root="$(git -C "$script_dir" rev-parse --show-toplevel)"
if [[ -t 1 ]]; then
c_reset=$'\033[0m'
c_red=$'\033[31m'
c_green=$'\033[32m'
c_yellow=$'\033[33m'
else
c_reset=''
c_red=''
c_green=''
c_yellow=''
fi
ok() { printf '%sOK%s: %s\n' "$c_green" "$c_reset" "$*"; }
warn() { printf '%sWARN%s: %s\n' "$c_yellow" "$c_reset" "$*"; }
fail() { printf '%sFAIL%s: %s\n' "$c_red" "$c_reset" "$*" >&2; }
usage() {
cat <<EOF
Usage: $(basename "$0") [--doctor] [--simulate-staleness] [--scan-dead-symlinks]
Check whether the compiled prefix libc.a is older than local fork commits.
Options:
--doctor Print verbose component/file diagnostics
--simulate-staleness Force an artificial stale-prefix result for testing
--scan-dead-symlinks Scan recipes/ for dangling symlinks and missing gitlinks
-h, --help Show this help
EOF
}
doctor=0
simulate_staleness=0
scan_dead_symlinks=0
for arg in "$@"; do
case "$arg" in
--doctor) doctor=1 ;;
--simulate-staleness) simulate_staleness=1 ;;
--scan-dead-symlinks) scan_dead_symlinks=1 ;;
-h|--help) usage; exit 0 ;;
*) fail "unknown argument: $arg"; usage; exit 2 ;;
esac
done
scan_dead_symlinks_mode() {
local broken=0 total=0 gitlink_count=0 path classification action target linkpath canonical
local stale_recipe_overlay=0 stale_patch_symlink=0 stale_fork_symlink=0 stale_unclassified=0 stale_submodule=0
while IFS= read -r linkpath; do
[[ -n "$linkpath" ]] || continue
(( total++ ))
target=$(readlink "$project_root/$linkpath" || true)
canonical=$(readlink -f "$project_root/$linkpath" 2>/dev/null || true)
if [[ -z "$canonical" ]]; then
canonical="$(python3 - <<'PY' "$project_root" "$linkpath" "$target"
import os, sys
root, linkpath, target = sys.argv[1:4]
print(os.path.normpath(os.path.join(os.path.dirname(os.path.join(root, linkpath)), target)))
PY
)"
fi
if [[ "$canonical" == "$project_root/local/recipes/"* ]]; then
classification="stale recipe overlay"
action="recreate or refresh the overlay under local/recipes/"
elif [[ "$canonical" == "$project_root/local/patches/"* ]]; then
classification="stale patch symlink"
action="restore the patch file and rewire the recipe symlink"
elif [[ "$canonical" == "$project_root/local/sources/"* ]]; then
classification="stale fork symlink"
action="restore the local fork source tree"
else
classification="stale symlink (unclassified)"
action="inspect the target and repair the symlink"
fi
if [[ ! -e "$project_root/$linkpath" ]]; then
printf '%s%s:1%s target=%s class=%s action=%s\n' "$c_red" "$linkpath" "$c_reset" "$target" "$classification" "$action"
case "$classification" in
"stale recipe overlay") (( stale_recipe_overlay++ )) ;;
"stale patch symlink") (( stale_patch_symlink++ )) ;;
"stale fork symlink") (( stale_fork_symlink++ )) ;;
*) (( stale_unclassified++ )) ;;
esac
(( broken++ ))
fi
done < <(find "$project_root/recipes" -type l ! -exec test -e {} \; -print | sed "s#^$project_root/##")
while IFS= read -r linkpath; do
[[ -n "$linkpath" ]] || continue
if [[ ! -e "$project_root/$linkpath" ]]; then
printf '%s%s:1%s target=%s class=%s action=update submodule path or initialize submodule\n' "$c_red" "$linkpath" "$c_reset" "missing" "missing submodule path"
(( stale_submodule++ ))
(( broken++ ))
fi
(( gitlink_count++ ))
done < <(git -C "$project_root" ls-tree -r --full-tree HEAD | awk '$1 == "160000" {print $4}')
if (( broken )); then
warn "dead symlink scan found $broken issue(s) across $total dangling link(s) and $gitlink_count gitlink(s)"
printf '%sSummary:%s recipe overlay=%s patch symlink=%s fork symlink=%s unclassified=%s submodule=%s\n' \
"$c_yellow" "$c_reset" "$stale_recipe_overlay" "$stale_patch_symlink" "$stale_fork_symlink" "$stale_unclassified" "$stale_submodule"
return 1
fi
ok "dead symlink scan clean ($total dangling links checked, $gitlink_count gitlinks checked)"
}
latest_prefix_mtime() {
local latest=0 path ts
for path in \
"$project_root/prefix/x86_64-unknown-redox/sysroot/x86_64-unknown-redox/lib/libc.a" \
"$project_root/prefix/x86_64-unknown-redox/relibc-install/x86_64-unknown-redox/lib/libc.a"; do
if [[ -e "$path" ]]; then
ts=$(stat -c %Y "$path")
(( ts > latest )) && latest=$ts
fi
done
printf '%s' "$latest"
}
fork_mtime() {
local path="$1"
git -C "$project_root" log -1 --format=%ct -- "$path" 2>/dev/null || printf '0'
}
components=(relibc kernel base)
stale=0
prefix_ts="$(latest_prefix_mtime)"
if (( simulate_staleness )); then
prefix_ts=0
fi
for component in "${components[@]}"; do
path="local/sources/$component"
fork_ts="$(fork_mtime "$path")"
delta=$((fork_ts - prefix_ts))
if (( doctor )); then
printf '%s: fork=%s prefix=%s delta=%s\n' "$path" "$fork_ts" "$prefix_ts" "$delta"
fi
if (( fork_ts > prefix_ts )); then
printf '%s: lib too old by %s seconds\n' "$component" "$delta"
stale=1
else
ok "$component: prefix fresh"
fi
done
if (( scan_dead_symlinks )); then
if scan_dead_symlinks_mode; then
exit 0
fi
exit 1
fi
if (( stale )); then
warn "prefix is stale — run ./local/scripts/build-redbear.sh --upstream"
exit 1
fi
ok "prefix is fresh"