481359ddb2
Co-authored-by: Kandev Agent <agent@kandev.dev> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
92 lines
3.4 KiB
Bash
Executable File
92 lines
3.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Boot an isolated Kandev instance with authentication enabled and seed a small
|
|
# team, so workspace visibility and membership can be clicked through.
|
|
#
|
|
# The instance is deliberately hermetic: `env -i` plus an explicit
|
|
# KANDEV_HOME_DIR, because an inherited KANDEV_DATABASE_PATH points at the
|
|
# developer's real database.
|
|
set -euo pipefail
|
|
|
|
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
PORT="${KANDEV_DEMO_PORT:-8231}"
|
|
HOME_DIR="${KANDEV_DEMO_HOME:-/tmp/kandev-team-access-demo}"
|
|
# Bind loopback plus any extra host (e.g. a Tailscale IP) so the instance is
|
|
# reachable from another device on the tailnet. Seeding always talks to
|
|
# loopback; BASE is only what the seeder and probes use.
|
|
# Loopback is always bound: the health probe and the seeder talk to BASE on
|
|
# 127.0.0.1, so an extra host must be added to loopback rather than replace it.
|
|
BIND_HOSTS="127.0.0.1${KANDEV_DEMO_BIND:+,${KANDEV_DEMO_BIND}}"
|
|
BASE="http://127.0.0.1:${PORT}"
|
|
COOKIE_JAR="${HOME_DIR}/admin-cookies.txt"
|
|
|
|
log() { printf '\033[1;36m==>\033[0m %s\n' "$*"; }
|
|
|
|
api() { # api METHOD PATH [JSON]
|
|
local method="$1" path="$2" body="${3:-}"
|
|
if [ -n "$body" ]; then
|
|
curl -sS -b "$COOKIE_JAR" -c "$COOKIE_JAR" -X "$method" \
|
|
-H 'Content-Type: application/json' -d "$body" "${BASE}${path}"
|
|
else
|
|
curl -sS -b "$COOKIE_JAR" -c "$COOKIE_JAR" -X "$method" "${BASE}${path}"
|
|
fi
|
|
}
|
|
|
|
start_backend() {
|
|
# Refuse to boot onto an occupied port. Without this the health probe below
|
|
# happily passes against a STALE server still holding the port, and every
|
|
# later check silently measures the old binary.
|
|
if ss -lptn "sport = :${PORT}" 2>/dev/null | grep -q LISTEN; then
|
|
echo "port ${PORT} is already in use; run '$0 stop' or free it first" >&2
|
|
ss -lptn "sport = :${PORT}" >&2
|
|
exit 1
|
|
fi
|
|
rm -rf "$HOME_DIR"
|
|
mkdir -p "$HOME_DIR"
|
|
log "booting kandev on ${BASE} (home ${HOME_DIR})"
|
|
env -i \
|
|
HOME="$HOME_DIR" \
|
|
PATH="${REPO_ROOT}/apps/backend/bin:/usr/local/bin:/usr/bin:/bin" \
|
|
KANDEV_HOME_DIR="$HOME_DIR" \
|
|
KANDEV_PORT="$PORT" \
|
|
KANDEV_SERVER_HOST="$BIND_HOSTS" \
|
|
KANDEV_FEATURES_AUTH=true \
|
|
KANDEV_FEATURES_OFFICE="${KANDEV_DEMO_OFFICE:-false}" \
|
|
KANDEV_FEATURES_MULTI_TENANCY="${KANDEV_DEMO_TENANCY:-false}" \
|
|
KANDEV_WEB_DIST_DIR="${REPO_ROOT}/apps/web/dist" \
|
|
"${REPO_ROOT}/apps/backend/bin/kandev" __backend \
|
|
>"${HOME_DIR}/backend.log" 2>&1 &
|
|
echo $! >"${HOME_DIR}/backend.pid"
|
|
|
|
for _ in $(seq 1 90); do
|
|
if curl -sf "${BASE}/health" >/dev/null 2>&1; then
|
|
log "backend is up (pid $(cat "${HOME_DIR}/backend.pid"))"
|
|
return 0
|
|
fi
|
|
sleep 1
|
|
done
|
|
echo "backend did not become healthy; last log lines:" >&2
|
|
tail -40 "${HOME_DIR}/backend.log" >&2
|
|
exit 1
|
|
}
|
|
|
|
seed() {
|
|
log "seeding the team"
|
|
python3 "${REPO_ROOT}/scripts/demo_team_access_seed.py" "$BASE"
|
|
}
|
|
|
|
case "${1:-up}" in
|
|
up) start_backend; seed ;;
|
|
stop)
|
|
# Kill by the port's actual listener, not just the pid file: a crashed or
|
|
# replaced launch leaves the pid file naming a process that is not the one
|
|
# serving requests.
|
|
for pid in $(ss -lptn "sport = :${PORT}" 2>/dev/null | grep -oP 'pid=\K[0-9]+' | sort -u); do
|
|
if tr '\0' ' ' <"/proc/${pid}/environ" 2>/dev/null | grep -q "$HOME_DIR"; then
|
|
kill "$pid" 2>/dev/null || true
|
|
log "stopped pid ${pid}"
|
|
fi
|
|
done
|
|
;;
|
|
*) echo "usage: $0 [up|stop]" >&2; exit 2 ;;
|
|
esac
|