Files
saphid--frame-control/scripts/compat-db-backup.sh
T
saphidandClaude Opus 5.5 d4486a7681 Frame Control: Mac app, web UI, Android and Steam tooling
Package the Frame Control web UI as an installable Electron Mac app and
bring in the tooling built alongside it.

- app/: Electron wrapper that starts ui/server.py on a free loopback port,
  hardened window (sandbox, no navigation, runAsNode fuse off), login-shell
  PATH so Homebrew tools work from Finder, first-run offer to run
  connect.sh, ad-hoc signed DMG/zip via electron-builder.
- ui/: headset view (OpenVR screenshots), device status, library, Steam
  "Get games" (owned games, install, store search), Android apps as
  persistent Lepton instances with a rated F-Droid catalogue and a private
  compatibility database, Android display controls over ADB, file and
  clipboard transfer, Flatpaks, remote and power actions.
- apk-catalog/, compat-db/, frame/: catalogue build pipeline, Lakebed
  capsule for compatibility reports, Frame-side launchers.
- tests/ and CI: server guard and validation tests plus Steam helper tests,
  run on Python 3.9 with script and app syntax checks.
- Docs: README leads with the Mac app; new Android, panels, Steam games and
  field-notes docs; security notes on LAN-exposed ADB ports.

Screenshot values for the headset's IP and Wi-Fi name are placeholders.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 22:21:20 +10:00

69 lines
3.1 KiB
Bash
Executable File

#!/usr/bin/env zsh
# Mac-side: back up Frame Control's compatibility database (the private
# Lakebed capsule at https://frame-compat.lakebed.app).
#
# Exports every report through the app's own key (ui/frame_compat_db.py), keeps
# dated copies in ~/Library/Application Support/Frame Control/compat-db/backups (newest
# 60), and uploads to Google Drive (the backup folder) when
# the data changed since the last upload. Run daily by the LaunchAgent
# frame-compat-backup (see docs/apks.md).
#
# Usage: scripts/compat-db-backup.sh [--no-upload] [--force-upload] [--accept-shrink]
# Env: DRIVE_FOLDER_ID, GOG_WRAPPER
set -euo pipefail
ROOT="${0:A:h}/.."
DEST="$HOME/Library/Application Support/Frame Control/compat-db/backups"
DRIVE_FOLDER_ID=${DRIVE_FOLDER_ID:-<drive-folder-id>}
GOG_WRAPPER=${GOG_WRAPPER:-$HOME/bin/gog-with-keyring.sh}
upload=1 force=0 accept_shrink=0
for arg in "$@"; do
case "$arg" in
--no-upload) upload=0 ;;
--force-upload) force=1 ;;
--accept-shrink) accept_shrink=1 ;;
-h|--help) sed -n '2,12p' "$0"; exit 0 ;;
*) print -u2 "unknown option $arg"; exit 2 ;;
esac
done
mkdir -p "$DEST"
stamp=$(date -u +%Y%m%dT%H%M%SZ)
out="$DEST/frame-compat-$stamp.json"
python3 "$ROOT/ui/frame_compat_db.py" export "$out"
# A backup that lost data is worse than none: refuse to shrink. Compare with the
# last backup that passed this check (.last-good), never with a refused one, so a
# loss keeps failing every day until someone looks and passes --accept-shrink.
count=$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1]))["count"])' "$out")
good="$DEST/.last-good"
if [[ -f "$good" ]]; then
read -r good_count good_file < "$good"
if (( count < good_count )) && (( ! accept_shrink )); then
mv "$out" "$DEST/refused-${out:t}"
print -u2 "!! export has $count reports; the last good backup ($good_file) had $good_count."
print -u2 "!! Not uploading. Kept it as refused-${out:t}. If the loss is expected, rerun with --accept-shrink."
exit 1
fi
fi
print -r -- "$count ${out:t}" > "$good"
# Only the reports decide whether anything changed (not the export timestamp).
digest=$(python3 -c 'import json,sys,hashlib; r=json.load(open(sys.argv[1]))["reports"]; print(hashlib.sha256(json.dumps(sorted(r, key=lambda x: x["id"]), sort_keys=True).encode()).hexdigest())' "$out")
shasum -a 256 "$out" > "$out.sha256"
ls -1t "$DEST"/frame-compat-*.json | tail -n +61 | while read -r old; do rm -f "$old" "$old.sha256"; done
print "==> $count reports backed up to $out"
if (( upload )); then
last="$DEST/.last-uploaded-digest"
if (( ! force )) && [[ -f "$last" && "$(cat "$last")" == "$digest" ]]; then
print "==> Unchanged since the last Drive upload; skipped"
exit 0
fi
[[ -x "$GOG_WRAPPER" ]] || { print -u2 "gog wrapper not found at $GOG_WRAPPER"; exit 1; }
"$GOG_WRAPPER" drive upload "$out" --parent "$DRIVE_FOLDER_ID" --json --no-input >/dev/null
"$GOG_WRAPPER" drive upload "$out.sha256" --parent "$DRIVE_FOLDER_ID" --json --no-input >/dev/null
print -r -- "$digest" > "$last"
print "==> Uploaded to Google Drive (the backup folder)"
fi