agent: lumotia — Phase A.6 dogfood drill for rebrand migration on real OS paths
scripts/dogfood-rebrand-drill.sh — end-to-end probe that launches the real
lumotia binary against synthetic legacy magnotia state on disk, then
verifies both migration paths produced the expected outcome:
1. paths.rs: ~/.local/share/magnotia/ -> ~/.local/share/lumotia/, including
magnotia.db -> lumotia.db rename + non-DB companion files carried along
by the directory rename.
2. tauri_app_data_migration.rs: ~/.local/share/uk.co.corbel.magnotia/
copied via atomic staging to ~/.local/share/consulting.corbel.lumotia/,
with legacy preserved as a backup and staging dir cleaned up.
Closes the last gap in Phase A: every other test (paths::tests + storage
integration test + localStorageMigration.test.ts) uses synthetic in-process
state. The drill is the only verification that the real binary's startup
hook calls migrate_legacy_data_dir + migrate_tauri_app_data_dir_with_paths
against real OS path resolution.
Two modes:
(default) Sandbox: HOME=<tempdir>, faithful on Linux. NOT
faithful on macOS — Tauri 2 uses
NSSearchPathForDirectoriesInDomains which ignores
HOME overrides. Drill refuses to start in sandbox
mode on macOS rather than silently writing to the
user's real Application Support tree.
--against-real-home Real $HOME. Refuses to start if any lumotia data
already exists at the real paths (no clobbering
real user data). Cleans up planted state on exit
unless --keep is passed.
Eight probes covering: data-dir rename outcome, db file rename, legacy
removal, companion file survival, Tauri app_data_dir copy, legacy-backup
preservation, staging-dir cleanup, and lumotia_startup log line presence.
README: documents the drill alongside cargo test + npm test in the
Testing section, with the macOS caveat clearly flagged.
Not run as part of this commit — the drill launches a Tauri WebView
window for a few seconds. Jake to invoke when ready to dogfood.
This commit is contained in:
272
scripts/dogfood-rebrand-drill.sh
Executable file
272
scripts/dogfood-rebrand-drill.sh
Executable file
@@ -0,0 +1,272 @@
|
||||
#!/usr/bin/env bash
|
||||
# Lumotia rebrand-migration dogfood drill.
|
||||
#
|
||||
# Launches the real lumotia binary against synthetic legacy magnotia state
|
||||
# planted on disk, then probes the post-startup state to confirm both
|
||||
# migration paths ran:
|
||||
# 1. paths.rs: ~/.local/share/magnotia/ -> ~/.local/share/lumotia/
|
||||
# magnotia.db -> lumotia.db
|
||||
# 2. tauri_app_data_migration.rs:
|
||||
# ~/.local/share/uk.co.corbel.magnotia/ copied via staging to
|
||||
# ~/.local/share/consulting.corbel.lumotia/ (legacy preserved
|
||||
# as a backup)
|
||||
#
|
||||
# Modes:
|
||||
# (default) Sandbox. Sets HOME=<tempdir>, plants legacy state
|
||||
# inside, launches the binary, verifies, tears down.
|
||||
# Faithful on Linux. NOT faithful on macOS (Tauri 2
|
||||
# uses NSSearchPathForDirectoriesInDomains which
|
||||
# ignores HOME overrides and would write to your
|
||||
# real Application Support tree).
|
||||
# --against-real-home Real $HOME. Refuses to run if any lumotia data
|
||||
# already exists at the real paths. Backs up the
|
||||
# legacy planting before running so cleanup can
|
||||
# restore your real-home tree to its pre-drill
|
||||
# state.
|
||||
#
|
||||
# Flags:
|
||||
# --keep Keep the sandbox dir / preserved backups after the
|
||||
# run for manual inspection.
|
||||
# --timeout SECS How long to wait for the binary to come up and run
|
||||
# the migration. Default 20s. Bump on slow hardware
|
||||
# or when running under a debugger.
|
||||
# --binary PATH Override the default ./target/debug/lumotia.
|
||||
#
|
||||
# Exit codes:
|
||||
# 0 all probes passed
|
||||
# 1 a probe failed (migration did not produce the expected on-disk state)
|
||||
# 2 argument error
|
||||
# 3 preflight check failed (real-home already has lumotia data, binary
|
||||
# missing, unsupported platform for sandbox mode, ...)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
MODE="sandbox"
|
||||
KEEP=false
|
||||
TIMEOUT_SECS=20
|
||||
BINARY="./target/debug/lumotia"
|
||||
|
||||
# ---- arg parsing -----------------------------------------------------------
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--against-real-home) MODE="real-home"; shift ;;
|
||||
--keep) KEEP=true; shift ;;
|
||||
--timeout) TIMEOUT_SECS="$2"; shift 2 ;;
|
||||
--binary) BINARY="$2"; shift 2 ;;
|
||||
-h|--help)
|
||||
sed -n '2,33p' "$0" | sed 's/^# \{0,1\}//'
|
||||
exit 0 ;;
|
||||
*) echo "Unknown option: $1" >&2; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# ---- preflight -------------------------------------------------------------
|
||||
|
||||
if [[ ! -x "$BINARY" ]]; then
|
||||
echo "FAIL: binary not found at $BINARY. Run 'cargo build -p lumotia' first." >&2
|
||||
exit 3
|
||||
fi
|
||||
|
||||
PLATFORM="$(uname -s)"
|
||||
if [[ "$MODE" == "sandbox" && "$PLATFORM" == "Darwin" ]]; then
|
||||
echo "FAIL: sandbox mode is not faithful on macOS." >&2
|
||||
echo " Tauri 2 uses NSSearchPathForDirectoriesInDomains which ignores HOME overrides." >&2
|
||||
echo " Either run on Linux or use --against-real-home (with backup discipline)." >&2
|
||||
exit 3
|
||||
fi
|
||||
|
||||
# Real-home mode: refuse to run if the user already has lumotia data on
|
||||
# disk. We'd risk merging fake planted state into real data on cleanup.
|
||||
if [[ "$MODE" == "real-home" ]]; then
|
||||
REAL_HOME="$HOME"
|
||||
REAL_LUMOTIA_DATA="${XDG_DATA_HOME:-$REAL_HOME/.local/share}/lumotia"
|
||||
REAL_LUMOTIA_TAURI="${XDG_DATA_HOME:-$REAL_HOME/.local/share}/consulting.corbel.lumotia"
|
||||
for p in "$REAL_LUMOTIA_DATA" "$REAL_LUMOTIA_TAURI" "$REAL_HOME/.lumotia"; do
|
||||
if [[ -e "$p" ]]; then
|
||||
echo "FAIL: $p already exists. Refusing to run in real-home mode against existing data." >&2
|
||||
echo " Either back it up manually and remove the original, or run in sandbox mode." >&2
|
||||
exit 3
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# ---- sandbox setup ---------------------------------------------------------
|
||||
|
||||
if [[ "$MODE" == "sandbox" ]]; then
|
||||
SANDBOX="$(mktemp -d -t lumotia-dogfood-XXXXXX)"
|
||||
export HOME="$SANDBOX"
|
||||
unset XDG_DATA_HOME
|
||||
PLANT_ROOT="$SANDBOX/.local/share"
|
||||
echo "Sandbox: $SANDBOX (HOME overridden)"
|
||||
else
|
||||
SANDBOX="" # signal: don't tear down a sandbox at the end
|
||||
PLANT_ROOT="${XDG_DATA_HOME:-$HOME/.local/share}"
|
||||
echo "Real home: planting under $PLANT_ROOT"
|
||||
fi
|
||||
|
||||
LOG_FILE="$(mktemp -t lumotia-dogfood-binary-XXXXXX.log)"
|
||||
|
||||
cleanup() {
|
||||
if [[ -n "${BINARY_PID:-}" ]] && kill -0 "$BINARY_PID" 2>/dev/null; then
|
||||
kill -TERM "$BINARY_PID" 2>/dev/null || true
|
||||
sleep 0.5
|
||||
kill -KILL "$BINARY_PID" 2>/dev/null || true
|
||||
fi
|
||||
if [[ "$KEEP" == "false" && -n "$SANDBOX" && -d "$SANDBOX" ]]; then
|
||||
rm -rf "$SANDBOX"
|
||||
fi
|
||||
if [[ "$KEEP" == "false" && "$MODE" == "real-home" ]]; then
|
||||
# Real-home cleanup: remove planted legacy state AND any
|
||||
# post-migration artefacts we created. We refused to start if
|
||||
# any of these existed, so removing them now is safe.
|
||||
rm -rf \
|
||||
"$PLANT_ROOT/magnotia" \
|
||||
"$PLANT_ROOT/uk.co.corbel.magnotia" \
|
||||
"$PLANT_ROOT/lumotia" \
|
||||
"$PLANT_ROOT/consulting.corbel.lumotia"
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
# ---- plant synthetic legacy state -----------------------------------------
|
||||
|
||||
mkdir -p "$PLANT_ROOT/magnotia/recordings/2026-05-13"
|
||||
# Plant a sentinel non-DB file inside the legacy data dir to confirm the
|
||||
# migration sweeps the whole tree, not just magnotia.db.
|
||||
echo "fake-wav-bytes-for-dogfood-drill" > "$PLANT_ROOT/magnotia/recordings/2026-05-13/clip.wav"
|
||||
# An empty file at magnotia.db is enough for the file-level rename probe.
|
||||
# The Rust integration test (crates/storage/tests/legacy_db_migration.rs)
|
||||
# covers the "DB still openable after rename" path with a real SQLite;
|
||||
# this drill is about the LIVE BINARY at startup, not schema integrity.
|
||||
: > "$PLANT_ROOT/magnotia/magnotia.db"
|
||||
|
||||
mkdir -p "$PLANT_ROOT/uk.co.corbel.magnotia/localStorage/leveldb"
|
||||
echo "sentinel-leveldb-bytes" > "$PLANT_ROOT/uk.co.corbel.magnotia/localStorage/leveldb/000003.log"
|
||||
echo '{"main":{"x":100,"y":200,"width":1280,"height":720}}' > "$PLANT_ROOT/uk.co.corbel.magnotia/window-state.json"
|
||||
|
||||
echo "Planted synthetic legacy state. Launching $BINARY..."
|
||||
|
||||
# ---- launch binary in background, give it time to run migrations ----------
|
||||
|
||||
# RUST_LOG forces lumotia_startup tracing events to disk regardless of any
|
||||
# default filter. The setup hook logs `migrated legacy magnotia data dir`
|
||||
# at info level on a successful rename.
|
||||
RUST_LOG="lumotia_startup=info,lumotia=info" \
|
||||
"$BINARY" >"$LOG_FILE" 2>&1 &
|
||||
BINARY_PID=$!
|
||||
|
||||
# Wait for either: the migration log line to appear, OR the timeout.
|
||||
DEADLINE=$(( $(date +%s) + TIMEOUT_SECS ))
|
||||
MIGRATED_DATA_DIR_SEEN=false
|
||||
MIGRATED_TAURI_DIR_SEEN=false
|
||||
while (( $(date +%s) < DEADLINE )); do
|
||||
if grep -q "migrated legacy magnotia data dir to lumotia" "$LOG_FILE" 2>/dev/null; then
|
||||
MIGRATED_DATA_DIR_SEEN=true
|
||||
fi
|
||||
if grep -qE "Migrated|migrated.*tauri.*app_data|copied legacy Tauri" "$LOG_FILE" 2>/dev/null; then
|
||||
MIGRATED_TAURI_DIR_SEEN=true
|
||||
fi
|
||||
if [[ "$MIGRATED_DATA_DIR_SEEN" == "true" ]]; then
|
||||
# Give the Tauri side another moment after the data-dir migration.
|
||||
sleep 1
|
||||
break
|
||||
fi
|
||||
sleep 0.5
|
||||
done
|
||||
|
||||
# SIGTERM the binary cleanly. We don't need it running for the probes —
|
||||
# all probes inspect the post-migration filesystem state.
|
||||
if kill -0 "$BINARY_PID" 2>/dev/null; then
|
||||
kill -TERM "$BINARY_PID" || true
|
||||
fi
|
||||
wait "$BINARY_PID" 2>/dev/null || true
|
||||
|
||||
# ---- probes ----------------------------------------------------------------
|
||||
|
||||
PROBES_PASSED=0
|
||||
PROBES_FAILED=0
|
||||
report_probe() {
|
||||
local name="$1"; local outcome="$2"; local detail="${3:-}"
|
||||
if [[ "$outcome" == "PASS" ]]; then
|
||||
printf ' PASS %s\n' "$name"
|
||||
PROBES_PASSED=$(( PROBES_PASSED + 1 ))
|
||||
else
|
||||
printf ' FAIL %s %s\n' "$name" "$detail"
|
||||
PROBES_FAILED=$(( PROBES_FAILED + 1 ))
|
||||
fi
|
||||
}
|
||||
|
||||
printf '\nProbe results:\n'
|
||||
|
||||
# 1. Data-dir migration: target exists at the new path.
|
||||
if [[ -d "$PLANT_ROOT/lumotia" ]]; then
|
||||
report_probe "data-dir rename produced ~/.local/share/lumotia/" PASS
|
||||
else
|
||||
report_probe "data-dir rename produced ~/.local/share/lumotia/" FAIL "(missing)"
|
||||
fi
|
||||
|
||||
# 2. Data-dir migration: lumotia.db is at the new path.
|
||||
if [[ -f "$PLANT_ROOT/lumotia/lumotia.db" ]]; then
|
||||
report_probe "magnotia.db renamed to lumotia.db at new path" PASS
|
||||
else
|
||||
report_probe "magnotia.db renamed to lumotia.db at new path" FAIL "(missing)"
|
||||
fi
|
||||
|
||||
# 3. Data-dir migration: legacy magnotia tree is gone (rename moved it).
|
||||
if [[ ! -d "$PLANT_ROOT/magnotia" ]]; then
|
||||
report_probe "legacy ~/.local/share/magnotia/ removed by rename" PASS
|
||||
else
|
||||
report_probe "legacy ~/.local/share/magnotia/ removed by rename" FAIL "(still on disk)"
|
||||
fi
|
||||
|
||||
# 4. Data-dir migration: non-DB companion file carried along.
|
||||
if [[ -f "$PLANT_ROOT/lumotia/recordings/2026-05-13/clip.wav" ]]; then
|
||||
report_probe "non-DB companion file carried along by directory rename" PASS
|
||||
else
|
||||
report_probe "non-DB companion file carried along by directory rename" FAIL "(missing)"
|
||||
fi
|
||||
|
||||
# 5. Tauri migration: webview/localStorage copied to new bundle id path.
|
||||
if [[ -f "$PLANT_ROOT/consulting.corbel.lumotia/localStorage/leveldb/000003.log" ]]; then
|
||||
report_probe "Tauri app_data_dir copied to consulting.corbel.lumotia/" PASS
|
||||
else
|
||||
report_probe "Tauri app_data_dir copied to consulting.corbel.lumotia/" FAIL "(localStorage missing)"
|
||||
fi
|
||||
|
||||
# 6. Tauri migration: legacy uk.co.corbel.magnotia preserved as backup.
|
||||
if [[ -f "$PLANT_ROOT/uk.co.corbel.magnotia/localStorage/leveldb/000003.log" ]]; then
|
||||
report_probe "legacy Tauri dir preserved as backup" PASS
|
||||
else
|
||||
report_probe "legacy Tauri dir preserved as backup" FAIL "(missing — migration should NOT delete legacy)"
|
||||
fi
|
||||
|
||||
# 7. Tauri migration: staging dir cleaned up.
|
||||
if [[ ! -d "$PLANT_ROOT/consulting.corbel.lumotia.migrating" ]]; then
|
||||
report_probe "staging dir consulting.corbel.lumotia.migrating cleaned up" PASS
|
||||
else
|
||||
report_probe "staging dir consulting.corbel.lumotia.migrating cleaned up" FAIL "(staging leaked)"
|
||||
fi
|
||||
|
||||
# 8. Log line: lumotia_startup migration event appeared.
|
||||
if [[ "$MIGRATED_DATA_DIR_SEEN" == "true" ]]; then
|
||||
report_probe "lumotia_startup logged the data-dir migration" PASS
|
||||
else
|
||||
report_probe "lumotia_startup logged the data-dir migration" FAIL "(no log line within ${TIMEOUT_SECS}s)"
|
||||
fi
|
||||
|
||||
# ---- summary --------------------------------------------------------------
|
||||
|
||||
printf '\nLog file: %s\n' "$LOG_FILE"
|
||||
if [[ "$KEEP" == "true" && -n "$SANDBOX" ]]; then
|
||||
printf 'Sandbox preserved at: %s\n' "$SANDBOX"
|
||||
fi
|
||||
|
||||
printf '\nPassed: %s / Failed: %s\n' "$PROBES_PASSED" "$PROBES_FAILED"
|
||||
|
||||
if (( PROBES_FAILED > 0 )); then
|
||||
printf '\nDrill FAILED. Inspect %s for clues.\n' "$LOG_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf '\nDrill PASSED. Rebrand migration runs end-to-end against real OS paths.\n'
|
||||
Reference in New Issue
Block a user