diff --git a/.gitignore b/.gitignore index 781efb2..0b54aea 100644 --- a/.gitignore +++ b/.gitignore @@ -61,6 +61,9 @@ apps/addon/web-dist/ # Git worktrees (project-local) .worktrees/ +# Superset workspace-local HA ownership marker (see .superset/ha.sh) +.superset/.ha-started + # OS .DS_Store .claude/ diff --git a/.superset/config.json b/.superset/config.json new file mode 100644 index 0000000..d639164 --- /dev/null +++ b/.superset/config.json @@ -0,0 +1,5 @@ +{ + "setup": ["./.superset/setup.sh"], + "run": ["pnpm dev"], + "teardown": ["./.superset/teardown.sh"] +} diff --git a/.superset/ha.sh b/.superset/ha.sh new file mode 100755 index 0000000..978c781 --- /dev/null +++ b/.superset/ha.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash +# Workspace-aware control for the local Home Assistant dev stack. +# +# The HA dev container is a singleton: dev/ha-stack.yml hardcodes +# container_name: lovelacer-dev-ha and binds port 8123, so only one instance +# can run across the main repo and all worktrees. Use this wrapper instead of +# `pnpm dev:ha` inside a Superset workspace so teardown only stops the stack +# this workspace actually started (tracked via a workspace-local marker). +# +# ./.superset/ha.sh up # start HA + record ownership +# ./.superset/ha.sh down # stop HA + clear ownership +# +# `down` refuses to act unless this workspace owns the *currently running* +# container, so it can't stop a container the main repo or another workspace +# started. The marker stores the started container id and is revalidated before +# every stop, so a stale marker (e.g. after a `pnpm dev:ha:down` force-stop and +# a restart elsewhere) is cleared rather than acted on. To force-stop the shared +# stack from any checkout, use `pnpm dev:ha:down` directly. +set -euo pipefail + +marker=".superset/.ha-started" + +# Echo the running lovelacer-dev-ha container id, or nothing if it isn't up. +ha_container_id() { + docker inspect -f '{{.Id}}' lovelacer-dev-ha 2>/dev/null || true +} + +case "${1:-}" in + up) + # ha_container_id uses `docker inspect`, so this sees an existing + # lovelacer-dev-ha in ANY state (running, stopped, exited) — not just + # running ones — and avoids adopting/restarting another checkout's stopped + # singleton. + existing_id=$(ha_container_id) + if [ -n "$existing_id" ]; then + if [ -f "$marker" ] && [ "$(cat "$marker")" = "$existing_id" ]; then + # This workspace already owns this exact container — (re)start it. + docker compose -f dev/ha-stack.yml up -d + else + echo "lovelacer-dev-ha already exists (started by another checkout)." >&2 + echo "Leaving it untouched and not claiming ownership from this workspace." >&2 + fi + exit 0 + fi + # No container exists in any state — create one and claim ownership. + # A fresh per-worktree dev/ha-config means a brand-new HA with its own + # onboarding — any HA_TOKEN copied from the root .env won't authenticate. + fresh=0 + [ -e dev/ha-config/.storage ] || fresh=1 + docker compose -f dev/ha-stack.yml up -d + # Record the started container id so later down/teardown can prove ownership. + ha_container_id > "$marker" + if [ "$fresh" -eq 1 ]; then + echo "Started a fresh HA instance (empty dev/ha-config) on http://localhost:8123." >&2 + echo "Any HA_TOKEN copied from the root .env will NOT work against it —" >&2 + echo "onboard, generate a new long-lived token, and set HA_TOKEN in .env." >&2 + fi + ;; + down) + if [ ! -f "$marker" ]; then + echo "This workspace did not start HA (no $marker); refusing to stop the shared stack." >&2 + echo "Use 'pnpm dev:ha:down' if you really want to stop it." >&2 + exit 0 + fi + owned_id=$(cat "$marker") + current_id=$(ha_container_id) + if [ -z "$current_id" ]; then + echo "The HA container this workspace started is no longer running; clearing stale marker." >&2 + rm -f "$marker" + exit 0 + fi + if [ "$current_id" != "$owned_id" ]; then + echo "Running lovelacer-dev-ha was started by another checkout; not stopping it." >&2 + echo "Clearing this workspace's stale marker." >&2 + rm -f "$marker" + exit 0 + fi + docker compose -f dev/ha-stack.yml down + rm -f "$marker" + ;; + *) + echo "usage: $0 up|down" >&2 + exit 1 + ;; +esac diff --git a/.superset/setup.sh b/.superset/setup.sh new file mode 100755 index 0000000..8b86a05 --- /dev/null +++ b/.superset/setup.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# Superset workspace setup for Lovelacer. +# Installs workspace deps and seeds a local .env. +set -euo pipefail + +# Install all pnpm workspace dependencies (frozen lockfile for reproducibility). +corepack enable >/dev/null 2>&1 || true +pnpm install --frozen-lockfile + +# Seed .env: prefer the root repo's .env (carries HA_TOKEN etc.), else fall back +# to the committed example so the server has sane defaults. The copied HA_TOKEN +# targets the shared HA at localhost:8123; if this workspace later starts its +# own fresh HA via ./.superset/ha.sh up, that token won't match and must be +# regenerated (ha.sh up warns when that happens). +if [ ! -f .env ]; then + if [ -n "${SUPERSET_ROOT_PATH:-}" ] && [ -f "$SUPERSET_ROOT_PATH/.env" ]; then + cp "$SUPERSET_ROOT_PATH/.env" .env + echo "Copied .env from root repo (HA_TOKEN targets the existing shared HA)." + else + cp .env.example .env + echo "No root .env found; copied .env.example. Fill in HA_TOKEN before running." + fi +fi diff --git a/.superset/teardown.sh b/.superset/teardown.sh new file mode 100755 index 0000000..5295bad --- /dev/null +++ b/.superset/teardown.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +# Superset workspace teardown for Lovelacer. +# +# Only stops the local Home Assistant dev stack if THIS workspace started it and +# still owns the running container. The HA container is shared across the main +# repo and worktrees — a blanket `docker compose down` here would otherwise stop +# a container another checkout is relying on. Ownership (including stale-marker +# revalidation) lives in ha.sh down, which we delegate to so the logic stays in +# one place. +set -euo pipefail + +marker=".superset/.ha-started" + +if [ -f "$marker" ]; then + ./.superset/ha.sh down +fi