Files
pi-devbox/rootfs/usr/local/bin/pi-devbox-version
T
joakimp 93f986e90e
Lint / hadolint (push) Successful in 9s
Publish Docker Image / resolve-versions (push) Successful in 15s
Publish Docker Image / base-decide (push) Successful in 8s
Lint / actionlint (push) Successful in 1m9s
Publish Docker Image / build-base (push) Successful in 41m23s
Publish Docker Image / smoke (push) Successful in 4m50s
Publish Docker Image / smoke-studio (push) Successful in 5m5s
Publish Docker Image / build-variant (push) Successful in 15m46s
Publish Docker Image / update-description (push) Successful in 7s
Publish Docker Image / promote-base-latest (push) Successful in 15s
Publish Docker Image / build-variant-studio (push) Successful in 19m59s
v1.8.6: adopt pi 0.84.3 + mempalace 3.8.0, close the v1.8.5 doc/observability gaps
Three coupled pieces of work, all of which ride on the base rebuild that the
mempalace bump forces anyway.

DRIFT ADOPTED
- pi 0.84.2 -> 0.84.3. Its release notes carry a "Breaking Changes" line
  (GoogleThinkingLevel -> GoogleApiThinkingLevel). Audited before adopting:
  zero references across all four vendored companions (pi-fork,
  pi-observational-memory, pi-atelier, pi-studio), so it is inert for us. The
  reason to adopt is two skill-discovery fixes that land directly on v1.8.5's
  vendored-skill work: nested Markdown skills inside grouping directories were
  not discovered, and root README.md/AGENTS.md in skill dirs were reported as
  broken skills.
- mempalace core 3.7.1 -> 3.8.0. Additive/reliability only. Its sync fix
  (#2320/#2322) stops sync --apply deleting drawers whose source_file was
  unreachable *at that moment* -- which does NOT relax the standing landmine
  against sync on the shared palace, because that landmine is about paths
  permanently absent from whichever host runs the sync. Different failure
  shape; the caution stands.

DOCS -- three defects, one of them public
- DOCKER_HUB.md advertised "neovim (LazyVim defaults)". Nothing in the image
  installs LazyVim; the only nvim config is a 19-line sysinit.vim. CI PATCHes
  this file into the Docker Hub description on every release, so this was a
  false claim published to the world. Removed.
- agent-browser + Playwright + Chromium is the single largest addition in the
  image (~625 MB) and had zero mentions in README, DOCKER_HUB or THIRD_PARTY --
  it was documented only to agents, in the AGENTS.md managed block. Now
  documented to humans, including the Chromium licence dimension.
- typst and socat appeared in README prose but not in the "What's inside"
  inventory. Added.

OBSERVABILITY -- the three gaps v1.8.5 listed as still open
- build-manifest.json now records mempalace core, read from the live binary
  (ground truth, not the build ARG). Placed as a sibling of pi_version rather
  than inside components{}, because pi-devbox-version renders that map through
  [0:12] and would truncate a version string.
- smoke asserts the pi-observational-memory clone actually CONTAINS the ce9fc98
  auth fix, pinned to src/runtime.ts. Deliberately not a repo-wide grep: two of
  the three markers also live under tests/, so the repo-wide form stays green
  with the fix site reverted. That is the third false-green of this exact family
  in this repo (canary phrase in both snapshots; reconciler fixture using a
  non-owned name; now this) -- pin containment checks to the fix site.
- smoke asserts the feeder's pi@<device> agent default behaviourally. The
  earlier audit concluded this needed a --print-config added upstream; it does
  not. AGENT is assigned before arg parsing, so `bash -x mempalace-pi-session
  --help` observes the real resolution with no toolkit change. Two-sided:
  device set => pi@<device>, unset => must not be pi@*.
- pi-devbox-version now prints a palace: line with the same live-vs-baked drift
  detection pi already had. This matters more than it looks: mempalace is the
  one component that is both client (here) and server (synlig), so skew between
  them is a real failure mode. Degrades quietly on pre-v1.8.6 images.

Deferred deliberately: a native arm64 act_runner on tor-ms22 (the current
runner is on synlig, x86_64, so every arm64 layer ships QEMU-emulated).
Analysis and caveats filed to the palace rather than actioned here.
2026-08-25 15:29:22 +02:00

108 lines
4.2 KiB
Bash
Executable File

#!/usr/bin/env bash
# pi-devbox-version — show which pi-devbox image build is running.
#
# WHY THIS EXISTS
# The image bakes ground-truth build info into /etc/pi-devbox/build-manifest.json
# at `docker build` time (see Dockerfile.variant): the release tag, build date,
# source commit, live `pi --version` at build time, and the actual checked-out
# commit of every /opt component clone. That answers "what image am I running?"
# — but only if you know to go look for the file. This wraps it into one
# command, prints it human-first at container start (see entrypoint-user.sh),
# and stays available on demand for the rest of the session.
#
# USAGE
# pi-devbox-version human-readable summary (default)
# pi-devbox-version --json raw manifest JSON (for scripting)
# pi-devbox-version --quiet one-line "release_tag (source_revision)" form
#
# EXIT STATUS
# 0 on success. 1 if the manifest is missing (e.g. an image built before
# this file existed, or a non-pi-devbox base) — prints a short notice
# to stderr rather than failing silently.
set -euo pipefail
MANIFEST=/etc/pi-devbox/build-manifest.json
MODE="human"
case "${1:-}" in
--json) MODE="json" ;;
--quiet|-q) MODE="quiet" ;;
--help|-h)
sed -n '2,20p' "$0" | sed 's/^# \?//'
exit 0
;;
esac
if [ ! -f "$MANIFEST" ]; then
echo "pi-devbox-version: no build manifest at $MANIFEST" >&2
echo " (image predates the manifest, or this isn't a pi-devbox image)" >&2
exit 1
fi
if ! command -v jq >/dev/null 2>&1; then
echo "pi-devbox-version: jq not found; dumping raw manifest instead" >&2
cat "$MANIFEST"
exit 0
fi
if [ "$MODE" = "json" ]; then
cat "$MANIFEST"
exit 0
fi
release_tag=$(jq -r '.release_tag' "$MANIFEST")
build_date=$(jq -r '.build_date' "$MANIFEST")
source_rev=$(jq -r '.source_revision' "$MANIFEST")
pi_version_baked=$(jq -r '.pi_version' "$MANIFEST")
# `// empty` matters: images built before v1.8.6 have no such field, and
# `jq -r` renders a JSON null as the 4-char string "null" — which would
# print as a bogus version rather than being treated as absent.
mp_version_baked=$(jq -r '.mempalace_version // empty' "$MANIFEST")
if [ "$MODE" = "quiet" ]; then
printf '%s (%s)\n' "$release_tag" "${source_rev:0:7}"
exit 0
fi
# Live drift check: has `pi` been upgraded since this container was built?
# (image is immutable, but a volume-persisted ~/.pi could in theory shadow
# the baked binary — this stays honest rather than trusting the manifest
# blindly, same "ground truth over intent" spirit as how the manifest
# itself is generated in Dockerfile.variant.)
pi_version_live=""
if command -v pi >/dev/null 2>&1; then
pi_version_live=$(pi --version 2>/dev/null | head -n1 | tr -d '\r\n')
fi
# Same check for the palace, which matters more than it looks: mempalace is
# the one component that is BOTH client (here) and server (synlig runs this
# same image), so a skew between the two is a real failure mode rather than
# cosmetic. `mempalace --version` prints "MemPalace 3.8.0" — name-prefixed,
# unlike pi's bare "0.84.3" — hence $NF rather than reading the whole line.
mp_version_live=""
if command -v mempalace >/dev/null 2>&1; then
mp_version_live=$(mempalace --version 2>/dev/null | head -n1 | awk '{print $NF}' | tr -d '\r\n')
fi
printf 'pi-devbox %s\n' "$release_tag"
printf ' built: %s (source %s)\n' "$build_date" "${source_rev:0:12}"
if [ -n "$pi_version_live" ] && [ "$pi_version_live" != "$pi_version_baked" ]; then
printf ' pi: %s \033[33m(baked as %s — drift detected)\033[0m\n' "$pi_version_live" "$pi_version_baked"
else
printf ' pi: %s\n' "${pi_version_live:-$pi_version_baked}"
fi
# Printed only when known, so this degrades quietly on pre-v1.8.6 images
# instead of showing an empty or "null" palace line.
if [ -n "$mp_version_live" ] || [ -n "$mp_version_baked" ]; then
if [ -n "$mp_version_live" ] && [ -n "$mp_version_baked" ] && [ "$mp_version_live" != "$mp_version_baked" ]; then
printf ' palace: %s \033[33m(baked as %s — drift detected)\033[0m\n' "$mp_version_live" "$mp_version_baked"
else
printf ' palace: %s\n' "${mp_version_live:-$mp_version_baked}"
fi
fi
printf ' components:\n'
jq -r '.components | to_entries[] | select(.value != null) | " \(.key): \(.value[0:12])"' "$MANIFEST"