Files
nisse/scripts/bringup.sh
T
dongarandClaude Opus 5.5 a18321b4b0
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 1m16s
Add a bring-up script and a transcript dump; let nisse-dev install on its own VM (M0.2)
Development Plan M0.2.

- scripts/bringup.sh: start [REF] sets the previous run aside under
  /root/nisse-e2e/runs/, exports the ref under test and starts the agent
  as the transient systemd unit nisse-bringup, so it outlives the shell
  that started it; stop (SIGTERM, clean shutdown), status, logs, and
  collect (the journal log and a readable transcript).
- nisse_dev.dump (python -m nisse_dev.dump STATE_DB [--full]): a readable
  dump of a state store's state, pending intents and canonical log. Opens
  the store read-only, so it is safe against a live agent.
- The agent runs as root on this development VM, as it will on its own
  VM. Architecture D3/D6/D7 corrected to match the operator's intent:
  installing packages and toolchains on its own VM is class 2, recorded
  so the environment can be rebuilt (it was class 3 with provisioning an
  open question); egress names OS package mirrors. The plan's M0.2, M4.1,
  M4.2 and bring-up guards follow.

Acceptance: a smoke run started, answered status in the test room,
stopped cleanly, and collect produced the log and transcript.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
2026-10-01 16:16:32 +00:00

104 lines
4.1 KiB
Bash
Executable File

#!/usr/bin/env bash
# Bring-up runs of nisse-dev on this development VM (Development Plan M0.2).
#
# The agent runs as root, as it will on its own VM in production (D7): it may
# install what it needs. It touches only test resources (the sandbox repo, the
# test Matrix room, a budget-capped LiteLLM key), from the run's config. It
# runs as a transient systemd unit, so it outlives the shell that started it.
#
# scripts/bringup.sh start [REF] set the last run aside, export REF (default
# HEAD) and start the agent
# scripts/bringup.sh stop stop it cleanly (SIGTERM)
# scripts/bringup.sh status the unit, and the agent's state
# scripts/bringup.sh logs follow the agent's log
# scripts/bringup.sh collect save the log and a readable transcript
# into the current run's directory
#
# Layout, under $E2E (default /root/nisse-e2e):
# nisse-dev.yaml, .env the run config and its secrets (made by hand once)
# code/ the exported code under test, with its own .venv
# state.db, project/ the agent's state and workspace
# run-info, collected/ what was run, and the output of `collect`
# runs/<time>/ earlier runs, set aside by `start`
set -euo pipefail
E2E=${NISSE_E2E:-/root/nisse-e2e}
SRC=${NISSE_SRC:-/root/nisse} # repository to export code from
CODE=$E2E/code
UNIT=nisse-bringup
UV=${UV:-$(command -v uv || echo /snap/bin/uv)}
die() { echo "bringup: $*" >&2; exit 1; }
running() { systemctl is-active --quiet "$UNIT"; }
cmd_start() {
local ref=${1:-HEAD} sha
[ -f "$E2E/nisse-dev.yaml" ] && [ -f "$E2E/.env" ] || die "no config in $E2E"
running && die "already running; stop it first"
sha=$(git -C "$SRC" rev-parse --verify "$ref^{commit}") || die "unknown ref $ref"
# Set the previous run aside, whole: state, workspace, collected output.
local items=() item aside
for item in "$E2E"/state.db* "$E2E/project" "$E2E/collected" "$E2E/run-info"; do
[ -e "$item" ] && items+=("$item")
done
if [ ${#items[@]} -gt 0 ]; then
aside=$E2E/runs/$(date +%Y%m%d-%H%M%S)
mkdir -p "$aside"
mv "${items[@]}" "$aside/"
echo "previous run set aside in $aside"
fi
# Export the code under test, so edits to the working tree can't change a
# running agent. Uncommitted changes are not part of a run.
rm -rf "$CODE"
mkdir -p "$CODE"
git -C "$SRC" archive "$sha" | tar -x -C "$CODE"
(cd "$CODE" && "$UV" sync --quiet --all-packages)
printf 'ref: %s\ncommit: %s\nstarted: %s\n' "$ref" "$sha" "$(date -Is)" > "$E2E/run-info"
systemctl reset-failed "$UNIT" 2>/dev/null || true
systemd-run --quiet --unit="$UNIT" --collect \
--property=WorkingDirectory="$CODE" \
--property=KillSignal=SIGTERM --property=TimeoutStopSec=120 \
--setenv=NISSE_CONFIG="$E2E/nisse-dev.yaml" \
--setenv=NISSE_ENV="$E2E/.env" \
"$CODE/.venv/bin/nisse-dev"
echo "started $UNIT at ${sha:0:8} ($ref)"
}
cmd_stop() {
running || { echo "not running"; return 0; }
systemctl stop "$UNIT" # SIGTERM: the agent shuts down cleanly (main.py)
echo "stopped"
}
cmd_status() {
if running; then echo "unit: running"; else echo "unit: not running"; fi
[ -f "$E2E/run-info" ] && sed 's/^/ /' "$E2E/run-info"
if [ -f "$E2E/state.db" ] && [ -x "$CODE/.venv/bin/python" ]; then
"$CODE/.venv/bin/python" -m nisse_dev.dump "$E2E/state.db" | head -n 3
fi
}
cmd_logs() { journalctl -u "$UNIT" -f -o cat; }
cmd_collect() {
[ -f "$E2E/run-info" ] || die "no run to collect"
local out=$E2E/collected since
since=$(sed -n 's/^started: //p' "$E2E/run-info")
mkdir -p "$out"
journalctl -u "$UNIT" --since "$since" -o cat > "$out/agent.log"
"$CODE/.venv/bin/python" -m nisse_dev.dump --full "$E2E/state.db" > "$out/transcript.txt"
echo "collected into $out"
}
case "${1:-}" in
start) shift; cmd_start "$@" ;;
stop) cmd_stop ;;
status) cmd_status ;;
logs) cmd_logs ;;
collect) cmd_collect ;;
*) sed -n '2,25p' "$0" | sed 's/^# \{0,1\}//'; exit 2 ;;
esac