feat: restore state/resume/report + single Spotlight index after mas installs
lib/brew.sh:
Set MAS_NO_AUTO_INDEX=1 before brew bundle install to suppress the
per-app mdimport call that mas otherwise runs after each App Store
installation. Run one mdimport /Applications batch pass afterwards.
lib/restore-state.sh (new):
Lightweight key=value state file (~/.local/state/ssr/restore-state).
ssr::rs::init / ssr::rs::run / ssr::rs::set/get / ssr::rs::error /
ssr::rs::report — tracks step outcomes and accumulated errors.
bin/ssr-restore:
--resume flag: skip steps already marked ok in state file.
Each step (brew_install, brew_restore, mackup_restore, macos_defaults,
prefs_import) wrapped in ssr::rs::run; errors accumulate without abort.
Formatted report printed at end of every run.
bin/ssr-status:
ssr status restore — full restore step report + error log
ssr status backup — Brewfile lines, unmanaged apps, prefs snapshot
ssr status — general view now shows one-line last-restore
summary and hint to run 'ssr status restore'
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
#!/usr/bin/env bash
|
||||
# lib/restore-state.sh — tracks restore step outcomes and accumulated errors.
|
||||
#
|
||||
# State file: $SSR_STATE_DIR/restore-state (key=value, one per line)
|
||||
# Error log: $SSR_STATE_DIR/restore-errors.log
|
||||
#
|
||||
# Step values: ok | warn | fail | skipped | pending
|
||||
|
||||
_ssr_rs_state_file() {
|
||||
printf '%s/restore-state' "${SSR_STATE_DIR:-$HOME/.local/state/ssr}"
|
||||
}
|
||||
_ssr_rs_error_file() {
|
||||
printf '%s/restore-errors.log' "${SSR_STATE_DIR:-$HOME/.local/state/ssr}"
|
||||
}
|
||||
|
||||
# Initialise (or reset) the state file for a new restore run.
|
||||
# $1 = brewfile path used for this run.
|
||||
ssr::rs::init() {
|
||||
local brewfile="${1:-}"
|
||||
local state_dir="${SSR_STATE_DIR:-$HOME/.local/state/ssr}"
|
||||
ssr::ensure_dir "$state_dir"
|
||||
|
||||
# Archive previous state so `ssr status restore` can show it even after a
|
||||
# new run starts.
|
||||
local sf; sf="$(_ssr_rs_state_file)"
|
||||
[[ -f "$sf" ]] && cp -f "$sf" "${sf}.prev" 2>/dev/null || true
|
||||
|
||||
# Truncate error log for the new run.
|
||||
: > "$(_ssr_rs_error_file)"
|
||||
|
||||
# Write fresh state header.
|
||||
printf 'started=%s\nbrewfile=%s\n' "$(ssr::ts)" "$brewfile" > "$sf"
|
||||
}
|
||||
|
||||
# Set a key in the state file (upsert).
|
||||
ssr::rs::set() {
|
||||
local key="$1" val="$2"
|
||||
local sf; sf="$(_ssr_rs_state_file)"
|
||||
local tmp; tmp="$(mktemp)"
|
||||
grep -v "^${key}=" "$sf" 2>/dev/null > "$tmp" || true
|
||||
printf '%s=%s\n' "$key" "$val" >> "$tmp"
|
||||
mv -f "$tmp" "$sf"
|
||||
}
|
||||
|
||||
# Get a value from the state file (empty string if absent).
|
||||
ssr::rs::get() {
|
||||
local key="$1"
|
||||
grep "^${key}=" "$(_ssr_rs_state_file)" 2>/dev/null | cut -d= -f2- || true
|
||||
}
|
||||
|
||||
# Append a timestamped error line to the error log.
|
||||
ssr::rs::error() {
|
||||
printf '[%s] %s\n' "$(ssr::ts)" "$1" >> "$(_ssr_rs_error_file)"
|
||||
}
|
||||
|
||||
# Run a named restore step.
|
||||
# Usage: ssr::rs::run <step_key> <description> <function> [args…]
|
||||
#
|
||||
# If --resume is active and the step is already 'ok', it is skipped.
|
||||
# Exit code of the function is captured; non-zero sets step to 'warn' (not
|
||||
# fatal) and logs the error. The outer script keeps running.
|
||||
ssr::rs::run() {
|
||||
local key="$1" desc="$2"; shift 2
|
||||
local fn="$1"; shift
|
||||
|
||||
# Resume: skip steps that already completed successfully.
|
||||
if [[ "${SSR_RESTORE_RESUME:-false}" == "true" ]]; then
|
||||
local prev; prev="$(ssr::rs::get "step_${key}")"
|
||||
if [[ "$prev" == "ok" ]]; then
|
||||
ssr::log "SKIP [$desc] (already completed in previous run)"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
ssr::log "STEP [$desc]"
|
||||
ssr::rs::set "step_${key}" "running"
|
||||
|
||||
local rc=0
|
||||
"$fn" "$@" || rc=$?
|
||||
|
||||
if [[ $rc -eq 0 ]]; then
|
||||
ssr::rs::set "step_${key}" "ok"
|
||||
else
|
||||
ssr::rs::set "step_${key}" "warn:rc=$rc"
|
||||
ssr::rs::error "[$desc] exited rc=$rc"
|
||||
ssr::warn "[$desc] finished with errors (rc=$rc) — continuing restore"
|
||||
fi
|
||||
}
|
||||
|
||||
# Print a formatted restore report to stdout.
|
||||
ssr::rs::report() {
|
||||
local sf; sf="$(_ssr_rs_state_file)"
|
||||
local ef; ef="$(_ssr_rs_error_file)"
|
||||
|
||||
if [[ ! -f "$sf" ]]; then
|
||||
printf 'No restore state found (%s).\n' "$sf"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local started brewfile
|
||||
started="$(grep '^started=' "$sf" | cut -d= -f2-)"
|
||||
brewfile="$(grep '^brewfile=' "$sf" | cut -d= -f2-)"
|
||||
|
||||
printf '\n%s\n' "── Restore Report ──────────────────────────────"
|
||||
printf ' started : %s\n' "${started:-unknown}"
|
||||
printf ' brewfile : %s\n' "${brewfile:-(unknown)}"
|
||||
printf '\n Steps:\n'
|
||||
|
||||
local -a step_keys=(brew_install brew_restore mackup_restore macos_defaults prefs_import)
|
||||
local -A step_labels=(
|
||||
[brew_install]="Homebrew install"
|
||||
[brew_restore]="Brew bundle install"
|
||||
[mackup_restore]="Mackup restore"
|
||||
[macos_defaults]="macOS defaults"
|
||||
[prefs_import]="Prefs snapshot import"
|
||||
)
|
||||
local key val icon
|
||||
for key in "${step_keys[@]}"; do
|
||||
val="$(grep "^step_${key}=" "$sf" 2>/dev/null | cut -d= -f2- || true)"
|
||||
case "$val" in
|
||||
ok) icon="✓" ;;
|
||||
skipped) icon="–" ;;
|
||||
warn*) icon="⚠" ;;
|
||||
fail*) icon="✗" ;;
|
||||
running) icon="…" ;;
|
||||
*) icon="?" ;;
|
||||
esac
|
||||
printf ' %s %-28s %s\n' "$icon" "${step_labels[$key]}" "${val:-(not run)}"
|
||||
done
|
||||
|
||||
if [[ -s "$ef" ]]; then
|
||||
printf '\n Errors:\n'
|
||||
sed 's/^/ /' "$ef"
|
||||
else
|
||||
printf '\n No errors recorded.\n'
|
||||
fi
|
||||
|
||||
printf '%s\n\n' "────────────────────────────────────────────────"
|
||||
|
||||
# Resume hint if any step failed.
|
||||
if grep -qE '^step_[^=]+=(warn|fail)' "$sf" 2>/dev/null; then
|
||||
printf ' To retry failed steps: ssr restore --resume\n\n'
|
||||
fi
|
||||
}
|
||||
Reference in New Issue
Block a user