2026-04-08 11:39:51 +10:00
#!/usr/bin/env bash
# Computes the full multi-instance desktop dev environment.
# Source this file from desktop dev commands; it exports:
2026-06-10 19:29:51 -04:00
# 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)
2026-04-08 11:39:51 +10:00
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 " )
2026-06-10 19:29:51 -04:00
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 } "
2026-04-08 11:39:51 +10:00
2026-07-16 00:10:29 +10:00
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\"}"
2026-04-08 11:39:51 +10:00
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.
2026-05-18 19:11:34 -04:00
#
# 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.
2026-04-08 11:39:51 +10:00
if git rev-parse --is-inside-work-tree & >/dev/null; then
GIT_DIR = $( git rev-parse --git-dir)
2026-05-18 19:11:34 -04:00
GIT_COMMON_DIR = $( git rev-parse --git-common-dir 2>/dev/null)
if [[ -n " $GIT_COMMON_DIR " && " $GIT_DIR " != " $GIT_COMMON_DIR " ]] ; then
2026-04-08 11:39:51 +10:00
BRANCH_NAME = $( git rev-parse --abbrev-ref HEAD)
2026-06-10 19:29:51 -04:00
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/-$//' )
2026-04-08 11:39:51 +10:00
2026-06-10 19:29:51 -04:00
# BUZZ_SHARE_IDENTITY=1: reuse the main dev checkout's Nostr key so
2026-05-18 19:11:34 -04:00
# 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.
2026-06-10 19:29:51 -04:00
if [[ " ${ BUZZ_SHARE_IDENTITY :- 0 } " == "1" ]] ; then
2026-07-22 14:25:04 -04:00
KEYRING_SERVICE = "buzz-desktop-dev"
KEYRING_BLOB = ""
case " $( uname -s) " in
Darwin)
if command -v security & >/dev/null; then
KEYRING_BLOB = " $( security find-generic-password -s " $KEYRING_SERVICE " -a secrets -w 2>/dev/null || true ) "
fi
;;
Linux)
if 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
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 ) "
2026-06-10 19:29:51 -04:00
CANONICAL_KEY = " $HOME /Library/Application Support/xyz.block.buzz.app.dev/identity.key"
2026-06-11 08:06:24 -07:00
LEGACY_CANONICAL_KEY = " $HOME /Library/Application Support/xyz.block.sprout.app.dev/identity.key"
2026-07-22 14:25:04 -04:00
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 "
2026-05-18 19:11:34 -04:00
else
2026-07-22 14:25:04 -04:00
echo "⚠ BUZZ_SHARE_IDENTITY=1 but no identity found in keyring service $KEYRING_SERVICE , at $CANONICAL_KEY , or at $LEGACY_CANONICAL_KEY — run Buzz from repo root first" >& 2
2026-05-18 19:11:34 -04:00
fi
fi
2026-06-11 08:06:24 -07:00
ICON_DIR = " $WORKTREE_ROOT /desktop/src-tauri/target/dev-icons"
2026-04-08 11:39:51 +10:00
mkdir -p " $ICON_DIR "
DEV_ICON = " $ICON_DIR /icon.icns"
2026-06-11 08:06:24 -07:00
GENERATE_DEV_ICON = " $WORKTREE_ROOT /scripts/generate-dev-icon.swift"
BASE_ICON = " $WORKTREE_ROOT /desktop/src-tauri/icons/icon.icns"
2026-04-08 11:39:51 +10:00
2026-06-11 08:06:24 -07:00
if swift " $GENERATE_DEV_ICON " " $BASE_ICON " " $DEV_ICON " " $BUZZ_WORKTREE_LABEL " ; then
2026-06-10 19:29:51 -04:00
echo "🌳 Worktree: ${ BUZZ_WORKTREE_LABEL } "
export VITE_DEV_BRANCH = " $BUZZ_WORKTREE_LABEL "
2026-07-16 00:10:29 +10:00
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 \"]}}"
2026-04-08 11:39:51 +10:00
fi
fi
fi
2026-06-10 19:29:51 -04:00
export BUZZ_TAURI_CONFIG