diff --git a/Makefile b/Makefile index bfb9d60..c7e386d 100644 --- a/Makefile +++ b/Makefile @@ -262,6 +262,13 @@ sim-drive-stop: ## Stop the rover @$(CONTAINER_ENGINE) exec $(SIM_CONTAINER) bash -c \ 'ign topic -t $(GZ_DRIVE_TOPIC) -m ignition.msgs.Twist -p "linear: {x: 0}, angular: {z: 0}"' +# Demo helpers +.PHONY: demo-swap +demo-swap: ## Fire an OTA rollout: circle → figure-eight (robot-app on sim-robot-01) + curl -sX POST http://localhost:8081/v1/ota/rollouts \ + -H "content-type: application/json" \ + -d '{"image_ref":"localhost:14050/robot-app:figure-eight-v1","smoke_command":"true","cohort_selector":{"robot_ids":["sim-robot-01"]}}' + # ============================================================================= # CI cluster (smoke / pre-push parity) — alternate ports so it can run # alongside `make lab-up` on the same host. Used by .git-hooks/installer-smoke.sh diff --git a/installer/docker-compose/docker-compose.sim.yml b/installer/docker-compose/docker-compose.sim.yml index 9b4f37f..f413f06 100644 --- a/installer/docker-compose/docker-compose.sim.yml +++ b/installer/docker-compose/docker-compose.sim.yml @@ -53,6 +53,11 @@ services: # this, OTA runs the container on the default bridge network and # the new behaviour never reaches the simulated robot. OTA_RUN_ARGS: "--network=temporal-hack-lab_default,-e,ROS_DOMAIN_ID=42,-e,RMW_IMPLEMENTATION=rmw_cyclonedds_cpp" + # Controller images are built --platform=linux/amd64 (they layer on + # ROS Humble's amd64-only base). On Apple Silicon, docker pull defaults + # to arm64 and refuses the amd64-only manifest. Force amd64 for both + # the pull and the run so OTA can swap controllers cleanly. + DOCKER_DEFAULT_PLATFORM: linux/amd64 # The repo is bind-mounted read-only. Go's workspace mode tries # to update go.work.sum on `go run` and fails; turn the workspace # off so the agent module's own go.mod/go.sum are authoritative. @@ -71,13 +76,50 @@ services: # perform OTA. CONTAINER_SOCK is supplied by the Makefile at # compose time (resolves to docker.sock or podman.sock). - ${CONTAINER_SOCK:-/var/run/docker.sock}:/var/run/docker.sock - # apt is needed to install the C toolchain for go-sqlite3 (cgo). - command: > - bash -c " - apt-get update -qq && apt-get install -y -qq build-essential >/dev/null && - cd /src/agent && - CGO_ENABLED=1 go run ./cmd/agent - " + # apt installs: build-essential for go-sqlite3 (cgo), docker-ce-cli for + # OTA's `docker run`. The Bookworm-default `docker.io` package's CLI is + # API 1.41 — too old for Docker Desktop's daemon (needs 1.44+). + # Block scalar style is `|` (literal, line-preserving) instead of `>` + # (folded). Folded scalar plus yamlfmt will helpfully wrap the long + # `echo "..." > docker.list` line and break the redirect — bash then + # sees the redirect as a standalone command and truncates docker.list + # to empty. Literal scalar keeps each `&&` clause on its own line and + # yamlfmt leaves it alone. + command: | + bash -c ' + set -e + apt-get update -qq + apt-get install -y -qq build-essential ca-certificates curl gnupg >/dev/null + install -m 0755 -d /etc/apt/keyrings + curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg + chmod a+r /etc/apt/keyrings/docker.gpg + ARCH=$$(dpkg --print-architecture) + echo "deb [arch=$$ARCH signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian bookworm stable" > /etc/apt/sources.list.d/docker.list + apt-get update -qq + apt-get install -y -qq docker-ce-cli >/dev/null + cd /src/agent + exec env CGO_ENABLED=1 go run ./cmd/agent + ' + + # Initial robot-app behaviour. Auto-starts on `make sim-up` so the rover + # starts driving in a circle with zero clicks. OTA replaces this container + # by name (see agent/internal/ota/docker.go: docker rm -f robot-app + + # docker rename robot-app-new robot-app) — `container_name: robot-app` is + # required for that swap to find the right container. + robot-app: + image: localhost:14050/robot-app:circle-v1 + container_name: robot-app + depends_on: + sim: + condition: service_started + mqtt: + condition: service_healthy + environment: + ROS_DOMAIN_ID: 42 + RMW_IMPLEMENTATION: rmw_cyclonedds_cpp + networks: + - default + restart: unless-stopped volumes: bridge-sock: