Files
bot-bottle/scripts/demo-setup.sh
T
didericis f23086171b fix: make demo setup/teardown idempotent and guard on PATH
Two failure modes found while validating the harness against a scratch
config root:

Teardown removed $dest unconditionally before restoring the backup, so
a second run deleted the user's own file — the first run had already
restored it, and a restored original is indistinguishable from an
installed demo file except by content. Compare against the source
before removing.

Setup had the mirror-image bug: run twice without an intervening
teardown it backed up its *own* copy, and teardown then "restored"
that, leaving demo.md in the user's config permanently. Treat an
already-identical destination as a no-op.

Also fail fast when bot-bottle is not on PATH. The tape types the real
console script, so without it a recording silently captures
`command not found` instead of a bottle launching.

Refs #540

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 22:08:22 -04:00

96 lines
3.7 KiB
Bash
Executable File

#!/usr/bin/env bash
# Stage everything the recorded demo needs, then hand off to demo.sh or
# demo-record.sh:
# - install scripts/demo/{bottle,agent}.md into $HOME/.bot-bottle/,
# backing up anything already sitting at those paths
# - create a dummy SSH identity where the demo bottle's git-gate
# expects one
# - pre-warm the agent + gateway images quietly so the recording
# doesn't spend its first 30s in BuildKit output
#
# Bottles can only be read from $HOME/.bot-bottle/bottles/ — a bottles/
# dir in CWD is ignored by design (manifest/index.py, PRD 0011) — so
# unlike the old throwaway manifest swap this writes into real config.
# Every write is paired with a .demo-backup so demo-teardown.sh can put
# things back exactly as they were; teardown is trapped by both
# callers and is safe to run twice.
set -euo pipefail
cd "$(dirname "$0")/.."
if ! docker info >/dev/null 2>&1; then
echo "demo-setup: docker daemon not reachable" >&2
exit 1
fi
# The tape types `bot-bottle start demo` verbatim, so the console
# script has to resolve in the recording shell. Without this guard a
# recording silently captures `command not found` instead of a bottle.
if ! command -v bot-bottle >/dev/null 2>&1; then
echo "demo-setup: bot-bottle is not on PATH. The demo runs the real" >&2
echo " console script, not ./cli.py — install it first (bash install.sh," >&2
echo " or 'pip install -e .' into an active venv), then re-run." >&2
exit 1
fi
config_root="${BOT_BOTTLE_ROOT:-$HOME/.bot-bottle}"
mkdir -p "$config_root/bottles" "$config_root/agents"
# Install one demo file, preserving whatever was there. The backup
# suffix is deterministic so teardown needs no state file. A stale
# backup from a killed run would be restored over the new install, so
# refuse rather than silently clobber it.
install_demo_file() {
src=$1
dest=$2
# Already installed (setup run twice without an intervening
# teardown). Backing up our own copy here would make teardown
# "restore" it and leave the demo file in the user's config forever,
# so treat this as a no-op.
if [ -e "$dest" ] && cmp -s "$src" "$dest"; then
return 0
fi
if [ -e "$dest.demo-backup" ]; then
echo "demo-setup: $dest.demo-backup already exists — a previous run" >&2
echo " did not tear down cleanly. Inspect it, then remove or restore" >&2
echo " it by hand before re-running." >&2
exit 1
fi
if [ -e "$dest" ]; then
mv "$dest" "$dest.demo-backup"
fi
cp "$src" "$dest"
}
install_demo_file scripts/demo/bottle.md "$config_root/bottles/demo.md"
install_demo_file scripts/demo/agent.md "$config_root/agents/demo.md"
# Dummy SSH identity — the git-gate validator wants a readable file at
# the key path. Contents don't matter for the demo: the unreachable
# upstream means the gate never actually uses the key.
fake_key_dir="$HOME/.cache/bot-bottle-demo"
mkdir -p "$fake_key_dir"
chmod 700 "$fake_key_dir"
printf 'not-a-real-key\n' > "$fake_key_dir/fake-key"
chmod 600 "$fake_key_dir/fake-key"
# Build the image graph quietly so the recorded run shows only the
# bottle launch and the four probes, not BuildKit progress.
node_base_image=$(
python3 -c \
'import json; print(json.load(open("image-build-args.json"))["NODE_BASE_IMAGE"])'
)
python_base_image=$(
python3 -c \
'import json; print(json.load(open("image-build-args.json"))["PYTHON_BASE_IMAGE"])'
)
docker build -q \
--build-arg "NODE_BASE_IMAGE=$node_base_image" \
-f bot_bottle/contrib/claude/Dockerfile \
-t bot-bottle-claude:latest . >/dev/null 2>&1 || true
docker build -q \
--build-arg "PYTHON_BASE_IMAGE=$python_base_image" \
-f Dockerfile.gateway \
-t bot-bottle-gateway:latest . >/dev/null 2>&1 || true