Add INSTALL_PI build arg for pi as second harness
Optional integration of pi-coding-agent alongside opencode in the same
container. Both harnesses share the mempalace install and palace path —
wing/diary entries are mutually visible.
Build:
--build-arg INSTALL_PI=true # opt-in
--build-arg PI_VERSION=0.73.1 # pin a version (default: latest)
--build-arg INSTALL_OPENCODE=false # build pi-only image
Dockerfile:
• New INSTALL_PI block: npm install -g @mariozechner/pi-coding-agent
+ git-clones pi-toolkit and pi-extensions to /opt/.
• Existing opencode install gated behind new INSTALL_OPENCODE arg
(default true; existing builds unaffected).
• mkdir adds ~/.pi/agent/extensions for the named volume mount root.
• CMD changed from ['opencode'] to ['bash', '-l']. compose run --rm
devbox now drops to a login shell so users pick the harness; pass
'opencode' or 'pi' explicitly to launch directly. compose exec
workflows are unaffected (bypass entrypoint+CMD).
entrypoint.sh:
• Adds ~/.pi to volume ownership loop.
entrypoint-user.sh:
• New 'pi: deploy toolkit + extensions + mempalace bridge' block runs
pi-toolkit/install.sh, pi-extensions/install.sh, settings.json
template bootstrap, then symlinks the mempalace.ts bridge directly.
Order: toolkit before extensions before bridge. mempalace-toolkit's
full install.sh is intentionally NOT called (its install_skill
would race with skillset auto-deploy --prune-stale).
docker-compose.yml:
• New devbox-pi-config named volume mounted at /home/developer/.pi.
Persists user toggles (/ext-disabled extensions) and settings.json
edits across container recreate. Mirrors devbox-opencode-config
pattern from v1.14.33.
scripts/smoke-test.sh:
• New --variant with-pi (threshold 2700 MB) and --variant omos-with-pi
(3400 MB).
• Pi assertions gated on `command -v pi`: version, /opt/pi-toolkit
clone HEAD, /opt/pi-extensions clone HEAD, deployed keybindings
symlink, ≥4 extension symlinks, mempalace.ts bridge symlink,
settings.json bootstrap.
• Pi state assertions use docker exec from the host (not 'run'),
since the container has no docker CLI.
• opencode core test now gated on INSTALL_OPENCODE presence.
scripts/generate-dockerhub-md.py:
• SECTION_RULES adds 'pi (alternative/complementary harness)': drop.
Section stays in README; dropped from DOCKER_HUB.md to keep under
the 25 kB Docker Hub limit.
Docs:
• README adds full 'pi (alternative/complementary harness)' section.
• AGENTS.md codifies pi install contract, deploy ordering, named
volume rationale, and CMD change.
• CHANGELOG.md gets an Unreleased entry.
• .env.example documents new build args.
• docker-compose.yml example args block updated.
Verification (local builds on arm64):
• Default (INSTALL_PI=false): 1871 MB, all assertions pass — no
regression.
• INSTALL_PI=true: 2110 MB (within 2700 threshold), 37 assertions
pass including pi version, all 7 extensions deployed (6 from
pi-extensions + mempalace.ts bridge), settings.json bootstrap.
Not yet:
• CI workflow updates to add -with-pi tag variants. Deferred until
local path stabilizes through user testing.
• pi-devbox separate repo for fully stripped pi-only image. Phase 2.
This commit is contained in:
+61
-5
@@ -8,7 +8,7 @@
|
||||
# - Generated opencode.json has the expected shape
|
||||
# - MCP wrapper works (when mempalace is installed)
|
||||
#
|
||||
# Usage: ./scripts/smoke-test.sh <image> [--variant base|omos]
|
||||
# Usage: ./scripts/smoke-test.sh <image> [--variant base|omos|with-pi|omos-with-pi]
|
||||
#
|
||||
# Exit codes:
|
||||
# 0 all checks passed
|
||||
@@ -23,7 +23,7 @@ if [ "${2:-}" = "--variant" ]; then
|
||||
fi
|
||||
|
||||
if [ -z "$IMAGE" ]; then
|
||||
echo "usage: $0 <image> [--variant base|omos]" >&2
|
||||
echo "usage: $0 <image> [--variant base|omos|with-pi|omos-with-pi]" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
@@ -50,7 +50,12 @@ echo "-- Resolved component versions --"
|
||||
# always record what got baked into this image, even when Dockerfile
|
||||
# ARGs default to "latest".
|
||||
docker run --rm --entrypoint="" "$IMAGE" sh -c '
|
||||
printf " %-15s %s\n" "opencode" "$(opencode --version 2>&1 | head -1)"
|
||||
if command -v opencode >/dev/null 2>&1; then
|
||||
printf " %-15s %s\n" "opencode" "$(opencode --version 2>&1 | head -1)"
|
||||
fi
|
||||
if command -v pi >/dev/null 2>&1; then
|
||||
printf " %-15s %s\n" "pi" "$(pi --version 2>&1 | head -1)"
|
||||
fi
|
||||
printf " %-15s %s\n" "node" "$(node --version)"
|
||||
printf " %-15s %s\n" "npm" "$(npm --version)"
|
||||
printf " %-15s %s\n" "nvim" "$(nvim --version | head -1)"
|
||||
@@ -77,7 +82,13 @@ docker run --rm --entrypoint="" "$IMAGE" sh -c '
|
||||
'
|
||||
echo
|
||||
echo "-- Core binaries --"
|
||||
run "opencode" "opencode --version"
|
||||
# opencode is gated on INSTALL_OPENCODE=true (default). When absent, the
|
||||
# image is a pi-only build (or a pure base — no harness at all).
|
||||
if docker run --rm --entrypoint="" "$IMAGE" sh -c "command -v opencode" >/dev/null 2>&1; then
|
||||
run "opencode" "opencode --version"
|
||||
else
|
||||
echo " - opencode not installed (INSTALL_OPENCODE=false)"
|
||||
fi
|
||||
run "node" "node --version"
|
||||
run "npm" "npm --version"
|
||||
run "git" "git --version"
|
||||
@@ -117,6 +128,49 @@ elif docker run --rm --entrypoint="" "$IMAGE" sh -c "command -v mempalace" >/dev
|
||||
echo " - mempalace-toolkit not installed (INSTALL_MEMPALACE_TOOLKIT=false)"
|
||||
fi
|
||||
|
||||
# pi: present when built with INSTALL_PI=true. Verifies pi itself plus
|
||||
# the runtime-deployed pi-toolkit + pi-extensions + mempalace bridge
|
||||
# symlinks under ~/.pi/agent/. Note: extension symlinks are created by
|
||||
# entrypoint-user.sh on first start, so we test by running the entry
|
||||
# point chain (not just `docker run --entrypoint=""`).
|
||||
if docker run --rm --entrypoint="" "$IMAGE" sh -c "command -v pi" >/dev/null 2>&1; then
|
||||
run "pi" "pi --version"
|
||||
run "pi-toolkit clone" "test -d /opt/pi-toolkit && git -C /opt/pi-toolkit rev-parse --short HEAD"
|
||||
run "pi-extensions clone" "test -d /opt/pi-extensions && git -C /opt/pi-extensions rev-parse --short HEAD"
|
||||
|
||||
# Run the full entrypoint as developer to verify install.sh deployment.
|
||||
# Spin up a long-running container so we can `docker exec` into it from
|
||||
# the host — the `run` helper above invokes commands INSIDE the image
|
||||
# and has no docker CLI to nest with.
|
||||
CID=$(docker run -d --rm "$IMAGE" tail -f /dev/null)
|
||||
trap 'docker rm -f "$CID" >/dev/null 2>&1 || true' EXIT
|
||||
sleep 1 # entrypoint runs synchronously; container ready when running
|
||||
|
||||
exec_test() {
|
||||
local label="$1"; shift
|
||||
local out
|
||||
if out=$(docker exec -u developer "$CID" sh -c "$*" 2>&1); then
|
||||
pass "$label ($(echo "$out" | head -1))"
|
||||
else
|
||||
fail "$label: $out"
|
||||
fi
|
||||
}
|
||||
|
||||
exec_test "~/.pi/agent/keybindings.json (pi-toolkit)" \
|
||||
'test -L $HOME/.pi/agent/keybindings.json && echo ok'
|
||||
exec_test "~/.pi/agent/extensions/*.ts ≥ 4 (pi-extensions)" \
|
||||
'count=$(ls -1 $HOME/.pi/agent/extensions/*.ts 2>/dev/null | wc -l); [ $count -ge 4 ] && echo "$count extensions"'
|
||||
exec_test "~/.pi/agent/extensions/mempalace.ts (bridge)" \
|
||||
'test -L $HOME/.pi/agent/extensions/mempalace.ts && echo ok'
|
||||
exec_test "~/.pi/agent/settings.json (template bootstrap)" \
|
||||
'test -f $HOME/.pi/agent/settings.json && echo ok'
|
||||
|
||||
docker rm -f "$CID" >/dev/null 2>&1 || true
|
||||
trap - EXIT
|
||||
else
|
||||
echo " - pi not installed (INSTALL_PI=false)"
|
||||
fi
|
||||
|
||||
# bun: only in the omos variant
|
||||
if [ "$VARIANT" = "omos" ]; then
|
||||
run "bun (omos)" "bun --version"
|
||||
@@ -221,12 +275,14 @@ SIZE_BYTES=$(docker image inspect --format='{{.Size}}' "$IMAGE")
|
||||
SIZE_MB=$((SIZE_BYTES / 1024 / 1024))
|
||||
echo " Uncompressed size: ${SIZE_MB} MB"
|
||||
|
||||
# Thresholds (uncompressed): base 2500 MB, omos 3200 MB. Adjust as image content evolves.
|
||||
# Thresholds (uncompressed): base 2500 MB, omos 3200 MB, with-pi adds ~150 MB.
|
||||
# omos bumped 3000→3200 on v1.14.31c — mempalace-toolkit bake-in pushed the
|
||||
# omos variant to ~3.1 GB. Functional smoke checks all pass; this is a
|
||||
# guardrail, not a performance limit.
|
||||
THRESHOLD=2500
|
||||
[ "$VARIANT" = "omos" ] && THRESHOLD=3200
|
||||
[ "$VARIANT" = "with-pi" ] && THRESHOLD=2700
|
||||
[ "$VARIANT" = "omos-with-pi" ] && THRESHOLD=3400
|
||||
if [ "$SIZE_MB" -gt "$THRESHOLD" ]; then
|
||||
fail "image size ${SIZE_MB} MB exceeds threshold ${THRESHOLD} MB for variant=$VARIANT"
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user