mirror of
https://github.com/block/buzz.git
synced 2026-10-31 14:31:02 +00:00
Unsigned dev binaries get a new code identity on every rebuild, which invalidates the keychain item's Always-Allow ACL and made macOS demand the login password on every tauri dev relaunch. Debug builds now default SecretStore to a file backend: secrets.<service>.json (0o600, atomic tmp+rename writes) in the app-data dir, guarded by the existing per-service flock. Release builds are keyring-only by construction (the File variant is cfg(debug_assertions)). BUZZ_DEV_USE_KEYCHAIN=1 opts a debug build back into the keychain. The file store deliberately starts empty — no migration from the old buzz-desktop-dev keychain item (dev keys are cheap to re-import, and a one-shot migration would live on as dead code). Every legacy-keychain path short-circuits in file mode, including the prod->dev agent-key copy, so the OS keychain is never touched. First launch after this change shows the identity-recovery screen once; re-import the dev nsec or use BUZZ_PRIVATE_KEY. BUZZ_SHARE_IDENTITY now reads the secrets file first (skipped when BUZZ_DEV_USE_KEYCHAIN=1 to avoid exporting a stale identity), and the in-app reset wipes the file. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Max Lampert <maxwell@squareup.com>
109 lines
6.0 KiB
Bash
Executable File
109 lines
6.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Computes the full multi-instance desktop dev environment.
|
|
# Source this file from desktop dev commands; it exports:
|
|
# BUZZ_VITE_PORT, BUZZ_HMR_PORT, VITE_PORT, VITE_HMR_PORT
|
|
# BUZZ_RELAY_PORT, BUZZ_RELAY_URL
|
|
# BUZZ_INSTANCE_SLUG, BUZZ_WORKTREE_LABEL, VITE_DEV_BRANCH (worktrees only)
|
|
# BUZZ_TAURI_CONFIG
|
|
# BUZZ_PRIVATE_KEY (worktrees only, when BUZZ_SHARE_IDENTITY=1)
|
|
|
|
WORKTREE_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
|
|
|
|
# Derive a stable base port from the worktree root so the same worktree always
|
|
# gets the same ports. This keeps the Tauri dev config stable between runs and
|
|
# preserves Cargo's build cache.
|
|
BASE_PORT=$(python3 -c "import hashlib,sys; h=int(hashlib.sha256(sys.argv[1].encode()).hexdigest(), 16); print(10000 + h % 55000)" "$WORKTREE_ROOT")
|
|
export BUZZ_VITE_PORT=$BASE_PORT
|
|
export BUZZ_HMR_PORT=$((BASE_PORT + 1))
|
|
export BUZZ_RELAY_PORT=3000
|
|
export VITE_PORT="$BUZZ_VITE_PORT"
|
|
export VITE_HMR_PORT="$BUZZ_HMR_PORT"
|
|
export BUZZ_RELAY_URL="${BUZZ_RELAY_URL:-ws://localhost:3000}"
|
|
|
|
DEV_URL="http://localhost:${BUZZ_VITE_PORT}"
|
|
if [[ "${BUZZ_RESET_WEBVIEW_STATE:-0}" == "1" ]]; then
|
|
DEV_URL="${DEV_URL}?resetDevState=1"
|
|
fi
|
|
|
|
BUZZ_TAURI_CONFIG="{\"build\":{\"devUrl\":\"${DEV_URL}\",\"beforeDevCommand\":\"exec ./node_modules/.bin/vite --port ${BUZZ_VITE_PORT} --strictPort\"},\"identifier\":\"xyz.block.buzz.app.dev\",\"productName\":\"Buzz Dev\"}"
|
|
unset VITE_DEV_BRANCH
|
|
|
|
# In worktrees, extract a label from the branch name and derive a unique app
|
|
# identity and icon so multiple local desktop instances can run side by side.
|
|
#
|
|
# Worktree detection: compare --git-dir to --git-common-dir. In the main
|
|
# working tree these are identical; in any worktree (whether under .worktrees/,
|
|
# .claude/worktrees/, or elsewhere on disk) they differ.
|
|
if git rev-parse --is-inside-work-tree &>/dev/null; then
|
|
GIT_DIR=$(git rev-parse --git-dir)
|
|
GIT_COMMON_DIR=$(git rev-parse --git-common-dir 2>/dev/null)
|
|
if [[ -n "$GIT_COMMON_DIR" && "$GIT_DIR" != "$GIT_COMMON_DIR" ]]; then
|
|
BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD)
|
|
export BUZZ_WORKTREE_LABEL="${BRANCH_NAME##*/}"
|
|
export BUZZ_INSTANCE_SLUG=$(echo "$BRANCH_NAME" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//' | sed 's/-$//')
|
|
|
|
# BUZZ_SHARE_IDENTITY=1: reuse the main dev checkout's Nostr key so
|
|
# worktrees skip onboarding and share the same identity. The per-worktree
|
|
# identifier is kept so concurrent instances don't collide on
|
|
# tauri-plugin-single-instance or the app data directory.
|
|
if [[ "${BUZZ_SHARE_IDENTITY:-0}" == "1" ]]; then
|
|
KEYRING_SERVICE="buzz-desktop-dev"
|
|
# Debug builds store secrets in a per-service file (see
|
|
# desktop/src-tauri/src/secret_store.rs); prefer it over the
|
|
# legacy OS keychain item, which dev builds no longer write.
|
|
SECRETS_FILE=""
|
|
KEYRING_BLOB=""
|
|
case "$(uname -s)" in
|
|
Darwin)
|
|
SECRETS_FILE="$HOME/Library/Application Support/xyz.block.buzz.app.dev/secrets.${KEYRING_SERVICE}.json"
|
|
if [[ ! -f "$SECRETS_FILE" ]] && command -v security &>/dev/null; then
|
|
KEYRING_BLOB="$(security find-generic-password -s "$KEYRING_SERVICE" -a secrets -w 2>/dev/null || true)"
|
|
fi
|
|
;;
|
|
Linux)
|
|
SECRETS_FILE="${XDG_DATA_HOME:-$HOME/.local/share}/xyz.block.buzz.app.dev/secrets.${KEYRING_SERVICE}.json"
|
|
if [[ ! -f "$SECRETS_FILE" ]] && command -v secret-tool &>/dev/null; then
|
|
KEYRING_BLOB="$(secret-tool lookup service "$KEYRING_SERVICE" username secrets target default 2>/dev/null || true)"
|
|
fi
|
|
;;
|
|
esac
|
|
# BUZZ_DEV_USE_KEYCHAIN=1 puts the app on the keychain, so the
|
|
# file may hold a stale identity — only prefer it in file mode.
|
|
if [[ "${BUZZ_DEV_USE_KEYCHAIN:-0}" != "1" && -f "$SECRETS_FILE" ]]; then
|
|
KEYRING_BLOB="$(cat "$SECRETS_FILE")"
|
|
fi
|
|
|
|
KEYRING_IDENTITY="$(printf '%s' "$KEYRING_BLOB" | python3 -c 'import json, sys; value = json.load(sys.stdin).get("identity", ""); print(value if isinstance(value, str) else "")' 2>/dev/null || true)"
|
|
CANONICAL_KEY="$HOME/Library/Application Support/xyz.block.buzz.app.dev/identity.key"
|
|
LEGACY_CANONICAL_KEY="$HOME/Library/Application Support/xyz.block.sprout.app.dev/identity.key"
|
|
|
|
SHARED_IDENTITY="$KEYRING_IDENTITY"
|
|
if [[ -z "$SHARED_IDENTITY" && -f "$CANONICAL_KEY" ]]; then
|
|
SHARED_IDENTITY="$(cat "$CANONICAL_KEY")"
|
|
elif [[ -z "$SHARED_IDENTITY" && -f "$LEGACY_CANONICAL_KEY" ]]; then
|
|
SHARED_IDENTITY="$(cat "$LEGACY_CANONICAL_KEY")"
|
|
fi
|
|
|
|
if [[ -n "$SHARED_IDENTITY" ]]; then
|
|
export BUZZ_PRIVATE_KEY="$SHARED_IDENTITY"
|
|
else
|
|
echo "⚠ BUZZ_SHARE_IDENTITY=1 but no identity found in $SECRETS_FILE, keyring service $KEYRING_SERVICE, $CANONICAL_KEY, or $LEGACY_CANONICAL_KEY — run Buzz from repo root first" >&2
|
|
fi
|
|
fi
|
|
|
|
ICON_DIR="$WORKTREE_ROOT/desktop/src-tauri/target/dev-icons"
|
|
mkdir -p "$ICON_DIR"
|
|
DEV_ICON="$ICON_DIR/icon.icns"
|
|
GENERATE_DEV_ICON="$WORKTREE_ROOT/scripts/generate-dev-icon.swift"
|
|
BASE_ICON="$WORKTREE_ROOT/desktop/src-tauri/icons/icon.icns"
|
|
|
|
if swift "$GENERATE_DEV_ICON" "$BASE_ICON" "$DEV_ICON" "$BUZZ_WORKTREE_LABEL"; then
|
|
echo "🌳 Worktree: ${BUZZ_WORKTREE_LABEL}"
|
|
export VITE_DEV_BRANCH="$BUZZ_WORKTREE_LABEL"
|
|
BUZZ_TAURI_CONFIG="{\"build\":{\"devUrl\":\"${DEV_URL}\",\"beforeDevCommand\":\"exec ./node_modules/.bin/vite --port ${BUZZ_VITE_PORT} --strictPort\"},\"identifier\":\"xyz.block.buzz.app.dev.${BUZZ_INSTANCE_SLUG}\",\"productName\":\"Buzz Dev (${BUZZ_WORKTREE_LABEL})\",\"bundle\":{\"icon\":[\"$DEV_ICON\"]}}"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
export BUZZ_TAURI_CONFIG
|