a78e59fb5b
Bundle pi-studio (omaclaren/pi-studio) as a new -studio image variant: browser prompt editor, KaTeX/Mermaid preview, tmux-backed literate REPLs, /studio command + studio_* agent tools. - Dockerfile.variant: INSTALL_STUDIO + PI_STUDIO_REPO/REF args; vendor pi-studio to /opt/pi-studio (no build step — prebuilt client in git; npm install --omit=dev for 3 prod deps). STUDIO_PORT=8765 advisory. - entrypoint-user.sh: register /opt/pi-studio via the existing pi install local-path loop (auto-skips in non-studio variant). - smoke-test.sh: auto-detected studio assertions (clone + prebuilt client + pi install registration). - CI: resolve PI_STUDIO_REF to a SHA; independent smoke-studio + build-variant-studio jobs that gate ONLY the -studio tags, so a studio failure never blocks the core :latest release. - README: 'Using pi-studio' section documenting the container access reality — pi-studio hard-binds 127.0.0.1 (index.ts .listen(port, '127.0.0.1'), no --host flag), so -p publish alone can't reach it. Documents host-networking and loopback-bridge paths, the remote ssh -L forward, and the mosh caveat (no port forwarding; run parallel ssh -L). - CHANGELOG/AGENTS/DOCKER_HUB updated. Will tag as v1.1.0 (minor). No tag created — stopping for review.
158 lines
8.4 KiB
Docker
158 lines
8.4 KiB
Docker
# pi-devbox — variant image
|
|
#
|
|
# FROMs a base-<hash> image produced by Dockerfile.base and adds only
|
|
# the variant-specific tools — currently just the pi install. Kept as a
|
|
# separate file (rather than collapsed into Dockerfile.base) so future
|
|
# variants (e.g. studio, studio-tex) can FROM the variant or extend
|
|
# this Dockerfile with additional build args without rebuilding the
|
|
# base on every pi version bump.
|
|
#
|
|
# Pass `--build-arg BASE_IMAGE=<repo>:base-<hash>` to select the base.
|
|
# CI computes the base hash from Dockerfile.base + rootfs/ +
|
|
# entrypoint*.sh and feeds it in.
|
|
#
|
|
# IMPORTANT: the base image sets NPM_CONFIG_PREFIX to
|
|
# /home/developer/.pi/npm-global so runtime `pi install npm:...` and
|
|
# `npm install -g` by the developer user lands on the named volume.
|
|
# At BUILD time we want the baked binaries on /usr so they survive the
|
|
# volume mount. Each `npm install -g` below therefore prefixes the
|
|
# command with `NPM_CONFIG_PREFIX=/usr`.
|
|
|
|
ARG BASE_IMAGE
|
|
FROM ${BASE_IMAGE}
|
|
|
|
ARG TARGETARCH
|
|
ARG USER_NAME=developer
|
|
|
|
# ── pi coding-agent + companions ─────────────────────────────────────
|
|
# pi-toolkit and pi-extensions are cloned into /opt/. entrypoint-user.sh
|
|
# runs each repo's install.sh on container start so symlinks land under
|
|
# ~/.pi/agent/ on the named volume.
|
|
#
|
|
# PI_VERSION should be passed explicitly by CI as a concrete version
|
|
# (resolved from `npm view @earendil-works/pi-coding-agent version`).
|
|
# The default `latest` is for local dev convenience only — it has a
|
|
# known cache-hit footgun in registry-cached CI builds: the resulting
|
|
# build-arg string is byte-identical across builds, the layer-hash is
|
|
# identical, and the registry buildcache silently reuses the layer
|
|
# from whatever pi version was current when the cache was first
|
|
# populated. CI MUST pass a resolved concrete version. See pi-devbox
|
|
# v0.75.5b 2026-05-23 for the discovery + canonical fix.
|
|
ARG PI_VERSION=latest
|
|
ARG PI_TOOLKIT_REF=main
|
|
ARG PI_EXTENSIONS_REF=main
|
|
# pi-fork (fork tool) + pi-observational-memory (recall tool) live on GitHub
|
|
# under elpapi42. CI resolves these to commit SHAs to defeat the same
|
|
# cache-hit footgun that affects PI_VERSION.
|
|
ARG PI_FORK_REPO=https://github.com/elpapi42/pi-fork.git
|
|
ARG PI_FORK_REF=master
|
|
ARG PI_OBSMEM_REPO=https://github.com/elpapi42/pi-observational-memory.git
|
|
ARG PI_OBSMEM_REF=master
|
|
|
|
RUN set -e && \
|
|
# git_fetch_ref: clone-equivalent helper that accepts EITHER a branch name
|
|
# OR a commit SHA as $ref. Uses `git fetch <ref> + checkout FETCH_HEAD`
|
|
# which (a) works with both name and SHA forms uniformly, and (b) defeats
|
|
# the registry-buildcache footgun when CI passes a resolved SHA. The
|
|
# earlier helper `git_clone_retry` (using `git clone --branch`) only
|
|
# worked with branch names — a SHA-resolved build-arg made `git clone
|
|
# --branch <40-char-SHA>` fail with "Remote branch not found". Surfaced
|
|
# in pi-devbox v1.0.0-rerun (run 374) 2026-06-10 and fixed by switching
|
|
# all four clones to git_fetch_ref. Both Gitea and GitHub allow fetching
|
|
# arbitrary commits by default (uploadpack.allowReachableSHA1InWant).
|
|
git_fetch_ref() { \
|
|
url="$1"; ref="$2"; dest="$3"; \
|
|
rm -rf "$dest"; mkdir -p "$dest"; \
|
|
git -C "$dest" init -q && git -C "$dest" remote add origin "$url" && \
|
|
for i in 1 2 3 4 5; do \
|
|
if git -C "$dest" fetch --depth 1 origin "$ref" && git -C "$dest" checkout -q FETCH_HEAD; then return 0; fi; \
|
|
echo "git fetch $url@$ref failed (attempt $i/5), retrying in $((i*5))s..."; \
|
|
sleep $((i*5)); \
|
|
done; \
|
|
return 1; \
|
|
} && \
|
|
if [ "${PI_VERSION}" = "latest" ]; then \
|
|
NPM_CONFIG_PREFIX=/usr npm install -g @earendil-works/pi-coding-agent ; \
|
|
else \
|
|
NPM_CONFIG_PREFIX=/usr npm install -g @earendil-works/pi-coding-agent@${PI_VERSION} ; \
|
|
fi && \
|
|
pi --version && \
|
|
git_fetch_ref "https://gitea.jordbo.se/joakimp/pi-toolkit.git" "${PI_TOOLKIT_REF}" /opt/pi-toolkit && \
|
|
git_fetch_ref "https://gitea.jordbo.se/joakimp/pi-extensions.git" "${PI_EXTENSIONS_REF}" /opt/pi-extensions && \
|
|
git_fetch_ref "${PI_FORK_REPO}" "${PI_FORK_REF}" /opt/pi-fork && \
|
|
git_fetch_ref "${PI_OBSMEM_REPO}" "${PI_OBSMEM_REF}" /opt/pi-observational-memory && \
|
|
(cd /opt/pi-fork && npm install --omit=dev --no-audit --no-fund) && \
|
|
(cd /opt/pi-observational-memory && npm install --omit=dev --no-audit --no-fund) && \
|
|
echo "pi-toolkit at $(cd /opt/pi-toolkit && git rev-parse --short HEAD)" && \
|
|
echo "pi-extensions at $(cd /opt/pi-extensions && git rev-parse --short HEAD)" && \
|
|
echo "pi-fork at $(cd /opt/pi-fork && git rev-parse --short HEAD)" && \
|
|
echo "pi-observational-memory at $(cd /opt/pi-observational-memory && git rev-parse --short HEAD)"
|
|
|
|
# ── Optional: pi-studio (:latest-studio variant) ─────────────────────
|
|
# pi-studio (omaclaren/pi-studio) is a pi-package + theme providing a
|
|
# two-pane browser workspace: prompt/response editor, KaTeX/Mermaid live
|
|
# preview, and tmux-backed literate REPLs. Off by default; the studio
|
|
# variant sets INSTALL_STUDIO=true.
|
|
#
|
|
# Vendored to /opt/pi-studio and registered at container start by
|
|
# entrypoint-user.sh via `pi install /opt/pi-studio` — the SAME pattern
|
|
# as pi-fork / pi-observational-memory above. We deliberately do NOT run
|
|
# `pi install <git-url>` at build time: that writes into ~/.pi/agent,
|
|
# which is a named volume, so a build-time install collides with / is
|
|
# shadowed by the volume on first run. Vendoring to /opt (an image layer)
|
|
# + a runtime local-path install keeps it on the image and idempotent.
|
|
#
|
|
# No build step is needed: pi-studio ships its browser bundle prebuilt in
|
|
# git (client/studio-client.js) and pi loads index.ts directly; its
|
|
# package.json scripts are only test/typecheck. So we just fetch + install
|
|
# the 3 prod deps (@earendil-works/pi-ai, @sinclair/typebox, ws).
|
|
#
|
|
# PI_STUDIO_REF is CI-resolved to a commit SHA to defeat the registry-
|
|
# buildcache cache-hit footgun (see the PI_VERSION note above).
|
|
ARG INSTALL_STUDIO=false
|
|
ARG PI_STUDIO_REPO=https://github.com/omaclaren/pi-studio.git
|
|
ARG PI_STUDIO_REF=main
|
|
RUN if [ "${INSTALL_STUDIO}" = "true" ]; then \
|
|
set -e; \
|
|
rm -rf /opt/pi-studio && mkdir -p /opt/pi-studio && \
|
|
git -C /opt/pi-studio init -q && \
|
|
git -C /opt/pi-studio remote add origin "${PI_STUDIO_REPO}" && \
|
|
ok=0; for i in 1 2 3 4 5; do \
|
|
if git -C /opt/pi-studio fetch --depth 1 origin "${PI_STUDIO_REF}" && \
|
|
git -C /opt/pi-studio checkout -q FETCH_HEAD; then ok=1; break; fi; \
|
|
echo "git fetch pi-studio@${PI_STUDIO_REF} failed (attempt $i/5), retrying in $((i*5))s..."; \
|
|
sleep $((i*5)); \
|
|
done; \
|
|
[ "$ok" = "1" ] && \
|
|
(cd /opt/pi-studio && npm install --omit=dev --no-audit --no-fund) && \
|
|
echo "pi-studio at $(cd /opt/pi-studio && git rev-parse --short HEAD)"; \
|
|
fi
|
|
|
|
# STUDIO_PORT: advisory default consumed by docker-compose port publishing
|
|
# and the recommended `/studio --no-browser --port "$STUDIO_PORT"` launch.
|
|
# Harmless in the non-studio variant. NOTE: pi-studio hard-binds the server
|
|
# to 127.0.0.1 inside the container (index.ts: .listen(port,"127.0.0.1")),
|
|
# so reaching it from a browser needs a loopback bridge or host networking —
|
|
# see the "Using pi-studio" section in README.md.
|
|
ENV STUDIO_PORT=8765
|
|
|
|
# ── Optional: Go toolchain ───────────────────────────────────────────
|
|
# Off by default; opt in for users who run Go tools inside the devbox.
|
|
ARG INSTALL_GO=false
|
|
ARG GO_VERSION=latest
|
|
RUN if [ "${INSTALL_GO}" = "true" ]; then \
|
|
GOARCH=$(case "${TARGETARCH}" in amd64) echo "amd64" ;; arm64) echo "arm64" ;; *) echo "amd64" ;; esac) && \
|
|
V="${GO_VERSION}" && \
|
|
if [ "$V" = "latest" ]; then \
|
|
V=$(curl -fsSL --retry 5 --retry-delay 5 --retry-all-errors "https://go.dev/dl/?mode=json" | \
|
|
awk -F'"' '/"version":/ { sub(/^go/,"",$4); print $4; exit }'); \
|
|
fi && \
|
|
[ -n "$V" ] && \
|
|
echo "Installing Go ${V}" && \
|
|
curl -fsSL --retry 5 --retry-delay 5 --retry-all-errors "https://go.dev/dl/go${V}.linux-${GOARCH}.tar.gz" | tar -C /usr/local -xz && \
|
|
ln -s /usr/local/go/bin/go /usr/local/bin/go && \
|
|
ln -s /usr/local/go/bin/gofmt /usr/local/bin/gofmt; \
|
|
fi
|
|
|
|
# WORKDIR / ENTRYPOINT / CMD inherited from base.
|