diff --git a/README.md b/README.md index 68f554b..c4d599e 100644 --- a/README.md +++ b/README.md @@ -321,6 +321,25 @@ Frontend test files live alongside source (`src/**/*.test.ts`) and run in jsdom by default. See [vite.config.js](vite.config.js) for the vitest configuration. +#### Rebrand-migration dogfood drill + +End-to-end probe that launches the real `target/debug/lumotia` binary +against synthetic legacy magnotia state planted on disk, then verifies +both migration paths produced the expected on-disk outcome. + +```bash +cargo build -p lumotia # need the binary first +scripts/dogfood-rebrand-drill.sh # sandbox mode (Linux only) +scripts/dogfood-rebrand-drill.sh --keep # leave sandbox dir for inspection +scripts/dogfood-rebrand-drill.sh --against-real-home # run against real $HOME +``` + +Sandbox mode is faithful only on Linux — Tauri 2 on macOS uses +`NSSearchPathForDirectoriesInDomains` which ignores `HOME` overrides. The +drill refuses to start in sandbox mode on macOS. Real-home mode refuses +to start if any lumotia data already exists at your real paths, so it +can roll back cleanly on exit. + --- ## Project documentation diff --git a/scripts/dogfood-rebrand-drill.sh b/scripts/dogfood-rebrand-drill.sh new file mode 100755 index 0000000..b378475 --- /dev/null +++ b/scripts/dogfood-rebrand-drill.sh @@ -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=, 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'