53b41cd76ba5f4d43ac4919484f2f48bba02ba54
25 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
53b41cd76b |
smoke: assert the pi stage $HOME-relative, and add a smoke_only dispatch
Lint / actionlint (push) Successful in 16s
Lint / hadolint (push) Successful in 13s
Publish Docker Image / resolve-versions (push) Successful in 10s
Publish Docker Image / base-decide (push) Successful in 16s
Publish Docker Image / build-base (push) Has been skipped
Publish Docker Image / smoke-studio (push) Successful in 5m20s
Publish Docker Image / smoke (push) Successful in 14m40s
Publish Docker Image / build-variant-studio (push) Successful in 21m51s
Publish Docker Image / build-variant (push) Successful in 15m59s
Publish Docker Image / update-description (push) Successful in 6s
Publish Docker Image / promote-base-latest (push) Successful in 17s
v1.8.0 never shipped: smoke (67/68) and smoke-studio (70/71) each failed the
same single assertion, so build-variant and everything downstream skipped and
latest stayed on v1.7.0.
The assertion was wrong, not the product. It grepped for a literal
stage=/home/developer/.mempalace/pi-stage/, but run() invokes
docker run --rm --entrypoint="" "$IMAGE" sh -c "$cmd"
and neither Dockerfile sets USER or ENV HOME — the published base image config
has no HOME at all; it is normally set by entrypoint-user.sh, which
--entrypoint="" skips on purpose. So the assertion executed as root with
HOME=/root, mempalace-pi-session correctly resolved
stage=/root/.mempalace/pi-stage/... (the stage is $HOME-relative by design), and
the literal grep could never match under any circumstances.
The tell was one line below in the log: the sibling assertion "pi stage follows
MEMPALACE_PALACE_PATH" PASSED, because it sets the variable explicitly and never
consults HOME. Default fails + explicit passes = wrong HOME, not broken staging.
Now asserts the invariant actually intended — the stage sits beside the resolved
palace, sharing its lifetime — which is user-independent:
case "$stage" in "stage=$HOME/.mempalace/pi-stage/"*) exit 0 ;; *) exit 1 ;; esac
$HOME is expanded by the container's own shell, so it holds as root, as
developer, or under any future user. Verified all four cases against the real
bin/mempalace-pi-session by extracting the committed assertion bodies and
running them under sh -c: virgin HOME -> exit 0; HOME=/home/developer -> exit 0;
MEMPALACE_PI_STAGE pinned to a .cache path -> exit 1 (the regression this
assertion exists to catch still fails it); developer-identity companion -> 0.
Added that companion assertion, "pi stage is palace-adjacent for the developer
user", which covers the deployment-specific path properly by SUPPLYING
HOME=/home/developer rather than assuming it.
Why this took a release to surface: docker-publish.yml triggers on push tags v*
only. The assertion was added on a push to main (
|
||
|
|
43cd6e22f2 |
v1.7.0: bundle pi-atelier at a pinned tag; pin pi to an audited 0.84.1
Publish Docker Image / resolve-versions (push) Successful in 9s
Lint / actionlint (push) Successful in 15s
Lint / hadolint (push) Successful in 13s
Publish Docker Image / base-decide (push) Successful in 8s
Publish Docker Image / build-base (push) Successful in 41m22s
Publish Docker Image / smoke-studio (push) Successful in 5m16s
Publish Docker Image / smoke (push) Successful in 7m31s
Publish Docker Image / build-variant-studio (push) Successful in 18m31s
Publish Docker Image / build-variant (push) Successful in 27m18s
Publish Docker Image / promote-base-latest (push) Successful in 11s
Publish Docker Image / update-description (push) Successful in 12s
Two changes that belong together, because the first is what makes the second dangerous to get wrong. pi-atelier (TUI sidebar + status rail) is now vendored to /opt/pi-atelier at PI_ATELIER_REF=v0.8.0 and registered by entrypoint-user.sh — the pi-fork / pi-observational-memory / pi-studio pattern, deliberately NOT `pi install npm:pi-atelier`, which writes into ~/.pi/npm-global on the config volume where it shadows the image and pins nothing. Unlike its siblings it gets no `npm install`: atelier declares zero runtime deps (peerDeps only, satisfied by the baked pi) and has no build step, so pi loads its TypeScript straight from the checkout via package.json `pi.extensions`. pi is no longer resolved to npm `latest` at build time. The pin lives in Dockerfile.variant and CI reads it from there, so a local `docker build` and a CI release ship the same versions by construction. The pin is a CHECKPOINT, NOT A FREEZE: bumping stays a one-line change; what stops is *unreviewed* adoption of whatever shipped that morning, in the same build that then gets tagged and published. CI fails when a pin is not concrete or not actually published on npm, and warns — never adopts — when npm latest moves ahead, naming what to re-check. Why this pairing needed care: pi-atelier 0.6.0/0.7.0 wrap pi's PRIVATE TUI renderer, and under pi 0.84 that wrapper recurses — pi hangs at startup burning CPU with no error. Upstream fixed the recursion in 0.7.1 and restored the non-overlapping split in 0.7.2; 0.8.0 is additive on top. atelier's own peerDependencies still say >=0.80.7, which does not express that floor, so nothing in npm metadata could have warned us. The floor is therefore encoded as an executable rule — pi >= 0.84 => pi-atelier >= 0.7.1 — asserted in both smoke-test.sh (build time) and recreate-sanity-check.sh (after a real recreate), verified against a 4x4 version matrix. Existing volumes needed migration, not just vendoring: a hand-installed `npm:pi-atelier` entry is counted as already-registered by the entrypoint guard, so every existing volume would have kept its unpinned npm copy — and a 0.6.x copy next to pi 0.84 is exactly the startup hang. The entrypoint now drops that one exact string (settings.json.bak.atelier.<ts> backup, distinct prefix so it cannot clobber the template merge's backup in the same second) and lets the pinned /opt copy register. Tested against a real settings.json: only that entry removed, other packages and all keys intact, idempotent, and unparseable JSON leaves the file untouched. DEVBOX_ATELIER=0 opts out entirely — in the entrypoint rather than via `pi uninstall`, because this component's failure mode is "pi will not start", which cannot be repaired from inside pi. 0.84.1 was audited for this release, not merely adopted: theme/TUI changes are additive, the session format is unchanged (CURRENT_SESSION_VERSION = 3 in both 0.83.0 and 0.84.1 with an identical migrateV1ToV2/migrateV2ToV3 ladder, so existing transcripts are neither migrated nor at risk and pi-session-repair stays valid), and the Node engine floor is unmoved at >=22.19.0. CI resolves the atelier tag to its PEELED commit SHA — atelier uses annotated tags, so the unpeeled ref is a tag object, not a commit; pi-studio's lightweight tags never exposed that distinction. Also: docs for overriding the read-only ~/.ssh/config from the container — container-only keys in ~/.ssh-local, hardened authorized_keys, the fact that `from=` must allow the HOST's addresses because container egress is NAT'd through it, and the macOS-only-keyword trap (`UseKeychain` is fatal to Linux OpenSSH and takes out dssh/pi --ssh while the host keeps working). Corrects two claims in "Naming LAN peers": ssh-lan.conf is not ProxyJump-only, and first-time creation does need one restart because the Include is emitted only when the file already exists at start. |
||
|
|
62a2a79b1c |
ci(lint): correct the rationale comment — runner contention was overstated
The previous commit justified excluding tag pushes partly on runner contention: that the duplicate lint run stole one of two self-hosted runners from the release build. Measured, that is false for THIS repo — pi-devbox lint runs take 0.3-0.9 min (ids 529/531/532/533) against a 77.6 min release build (id=530). I imported the claim from opencode-devbox, where actionlint apt-installs shellcheck inside the container and takes 6-15 min, so contention there is real. The change stands on its actual merits: duplicate lint of an identical tree, and release-run discovery ambiguity (the substantive one — it is what made the naive "first run matching refs/tags/<tag>" rule pick lint over the publish run). No functional change; comment only. |
||
|
|
f20b2a7926 |
ci(lint): don't re-lint on tag pushes
`on: push:` with no filter also fires on refs/tags/v*, which is duplicate work:
the tagged tree was already linted when that same commit was pushed to main
(v1.6.4 sha
|
||
|
|
1fd524e7fb |
Give each variant its own image description label
Both published variants inherited Dockerfile.base's description="pi-devbox — base image (variant-independent)", so v1.6.4 and v1.6.4-studio both advertised themselves on Docker Hub as the base image — misleading, and useless for telling the two apart. A LABEL cannot branch on INSTALL_STUDIO, so the text arrives as a build-arg: CI passes a variant-specific string (interpolating RELEASE_TAG, PI_VERSION and, for studio, STUDIO_TAG), and the Dockerfile default keeps a bare local `docker build -f Dockerfile.variant` honest instead of misleading. Also sets org.opencontainers.image.title/description alongside the legacy bare `description` key, so Hub and OCI-aware tooling both see it. ARGs stay in the last-declared block, so the label layer is still the only thing invalidated. Verified: hadolint 2.14.0 (the CI-pinned version) clean on both Dockerfiles; workflow YAML parses; check-workflow-shell.sh passes. Lands on the next release. |
||
|
|
291ae5345e |
repo: add LICENSE, THIRD_PARTY.md, .dockerignore, hadolint lint, IDEAS backlog
Repo/CI hygiene batch (none base-affecting; image contents unchanged): - LICENSE: actual MIT file (repo previously declared MIT only in prose). - THIRD_PARTY.md: notes bundled software + licenses (pi/pi-fork/pi-obsmem/ pi-studio MIT, gosu Apache-2.0, Debian packages under their own terms). - .dockerignore: trims build context to what the Dockerfiles COPY (rootfs/ + entrypoint*.sh); keeps .git/docs/scripts/compose out. Verified it excludes none of the required COPY sources. - lint.yml: new hadolint job (pinned v2.14.0) lints both Dockerfiles; .hadolint.yaml grandfathers deliberate choices (DL3008/DL3016/DL4006/DL3003/ SC2086, mirroring the shellcheck excludes), fails on anything new at warning+. Verified hadolint exit 0 and the repo shell-guard passes with the new job. - IDEAS.md: parks deferred follow-ups (SHA-pin actions, trivy, buildx SBOM/ provenance, Makefile, renovate). - README/DOCKER_HUB License sections now link LICENSE + THIRD_PARTY.md. No tag. |
||
|
|
38d8832d34 |
ci: record pi-studio version as image label
Lint workflows / actionlint (push) Successful in 1m16s
Adds se.jordbo.pi-devbox.pi-studio-version (e.g. v0.9.36) alongside the existing SHA label, so 'docker inspect' shows the human-readable version. Plumbing: resolve-versions exposes studio_tag output -> both studio build steps pass --build-arg PI_STUDIO_VERSION -> Dockerfile.variant declares ARG PI_STUDIO_VERSION=none and emits the LABEL. Studio-variant only. Validated: yq parse OK, resolve run-script bash -n OK. No tag. |
||
|
|
32586f19e7 |
ci: pin pi-studio to newest semver tag, not main HEAD
Lint workflows / actionlint (push) Successful in 23s
Upstream omaclaren/pi-studio stopped publishing GitHub Releases at v0.5.55 but keeps tagging every version (v0.9.36 now) and pushing to main. Tracking main HEAD risked baking half-finished commits that land after a tag. resolve-versions now lists all tags via a single git ls-remote (the REST tags API paginates at 100 and the repo has >140 tags, so a single page can miss the newest), picks the highest X.Y.Z with sort -V (pre-releases excluded), and peels it to a commit SHA for PI_STUDIO_REF. SHA (not moving tag) keeps cache-busting + reproducibility; require_sha still enforced. Studio-variant only, not base-affecting. No change to the resolved commit today (v0.9.36 == current main HEAD). Validated: yq parse OK, bash -n OK, live git ls-remote -> v0.9.36 -> 2ef38ef. No tag. |
||
|
|
d1db595f17 |
ci(lint): pass explicit workflow paths to actionlint
Lint workflows / actionlint (push) Successful in 15s
actionlint's no-arg project auto-detection looks for .github/workflows and hard-fails (exit 3, 'no project was found') on this .gitea/workflows layout — observed on run 420. Glob the workflow files explicitly. The Gitea shell guard step already passed in that run; only the actionlint invocation needed the path fix. |
||
|
|
26384fe9f1 |
ci: eliminate the sh-vs-bash footgun class (defaults + lint guard)
Lint workflows / actionlint (push) Failing after 34s
Root cause of the recurring 'Illegal option -o pipefail' failures ( |
||
|
|
b33e9dc592 |
fix(ci): promote-base-latest re-tag step needs shell: bash (set -o pipefail)
|
||
|
|
b7197e88b0 |
ci(promote-base-latest): re-point base-latest by digest, not need_build
The gate keyed off need_build=='true', assuming need_build==false meant base-latest was already current. A dry-run dispatch (promote_latest=false) that pre-builds base-<hash> falsifies that: the later tag run sees need_build==false and skipped promotion, leaving base-latest one base behind (observed 2026-06-27, v1.2.3 dry-run-first release). Gate now runs on every tag release / promote dispatch; the no-op optimization moved into the step as a crane digest compare so it re-tags only when base-latest actually differs from the released base-<hash>. Workflow-only change; base hash unaffected (no base rebuild). |
||
|
|
ed49b8d97a |
fix(ci): resolve-versions needs shell: bash for 'set -o pipefail'
Publish Docker Image / smoke (push) Successful in 9m0s
Publish Docker Image / build-variant-studio (push) Successful in 17m41s
Publish Docker Image / build-variant (push) Successful in 19m1s
Publish Docker Image / update-description (push) Successful in 7s
Publish Docker Image / promote-base-latest (push) Successful in 10s
Publish Docker Image / resolve-versions (push) Successful in 6s
Publish Docker Image / base-decide (push) Successful in 11s
Publish Docker Image / build-base (push) Successful in 45m54s
Publish Docker Image / smoke-studio (push) Successful in 3m43s
The default run shell is 'sh -e {0}' (dash on the act runner), which
rejects 'set -o pipefail' ('Illegal option -o pipefail') — failing the
resolve-versions job on line 2 and cascading every dependent job to
skipped (v1.1.6 run 401). The heavy build steps already declare
'shell: bash'; the resolve step did not. Added it.
|
||
|
|
9eff3f3c48 |
release: v1.1.6 — build provenance + reproducibility hardening; pi 0.79.7 → 0.79.8
Publish Docker Image / resolve-versions (push) Failing after 52s
Publish Docker Image / base-decide (push) Has been skipped
Publish Docker Image / build-base (push) Has been skipped
Publish Docker Image / smoke-studio (push) Has been skipped
Publish Docker Image / smoke (push) Has been skipped
Publish Docker Image / build-variant (push) Has been skipped
Publish Docker Image / build-variant-studio (push) Has been skipped
Publish Docker Image / promote-base-latest (push) Has been skipped
Publish Docker Image / update-description (push) Has been skipped
Adds OCI labels + /etc/pi-devbox/build-manifest.json so a published tag is self-describing and reconstructable after CI logs rotate (manifest is written from the actual checked-out HEAD of each /opt clone + live pi --version, not just the intended build-args). Hardens the build plumbing: - scripts/check-base-hash.sh guards the base-rebuild invariant: every floating ARG *_REF in Dockerfile.base must be folded into the base_tag hash, else a ref-only change silently fails to rebuild the base (v1.1.2-class staleness footgun). Runs in base-decide and locally. - resolve-versions now fails loud instead of falling back to a floating main/master on a transient API failure — validates each ref is a 40-hex SHA (and pi a real semver) and aborts the release otherwise. - The three gitea companions (pi-toolkit, pi-extensions, mempalace-toolkit) gained overridable *_REPO build-args (defaulting to the canonical gitea origin) so a relocated/forked build can repoint them without editing the Dockerfiles — matching the existing PI_FORK_REPO/PI_OBSMEM_REPO pattern. README documents the forked/relocated build-arg trick and how to read the labels + manifest. smoke-test asserts the manifest + labels. pi bumps 0.79.7 → 0.79.8 (auto-resolved at build). |
||
|
|
4744f05232 |
ci: CI-resolve mempalace-toolkit to a pinned SHA
mempalace-toolkit is the only companion cloned in Dockerfile.base (all others live in Dockerfile.variant), so it bypassed the resolve-versions -> build-arg plumbing and its ref stayed a literal `main`. Because the base only rebuilds on a content hash of Dockerfile.base + rootfs/* + entrypoints, a toolkit-only fix would silently fail to land unless Dockerfile.base itself changed (as it incidentally did in v1.1.1). Changes: - resolve-versions: new mempalace_toolkit_ref output (gitea commits API, mirrors pi-toolkit resolution; jq '.[0].sha // "main"' fallback). - base-decide: needs resolve-versions; fold the resolved SHA into the base-tag hash so a moved toolkit forces a base rebuild automatically. - build-base: needs resolve-versions; pass --build-arg MEMPALACE_TOOLKIT_REF. - Dockerfile.base: switch clone from `git clone --branch` to a SHA-capable `git fetch <ref> + checkout FETCH_HEAD` (the --branch <SHA> footgun already fixed in Dockerfile.variant, run 374). base_tag now reflects a live gitea lookup; on API blip it falls back to `main`, triggering one extra rebuild, never a missed one. No new tag — lands on the next v* release or workflow_dispatch. |
||
|
|
a78e59fb5b |
feat(studio): add :latest-studio variant (PR-3)
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. |
||
|
|
edd6be1737 |
fix(ci): update-description must depend on resolve-versions
Run 376 published the Hub description with PI_VERSION=empty ("Current
:latest ships pi `` ") because the update-description job has
needs: [build-variant] but not resolve-versions. In Gitea Actions
needs.<job>.outputs.* only resolves for jobs in your needs: list.
The post-substitution sanity-check (grep -q '{{PI_VERSION}}') passed
because sed had successfully replaced the placeholder — with empty
string. Pre-empted by adding a non-empty assertion in this commit:
the step now fails loudly if PI_VERSION resolves to empty rather than
silently publishing a broken description.
|
||
|
|
efd254f4e6 |
docs: rewrite DOCKER_HUB.md for v1.0.0 reality + auto-inject pi version
The Hub description still described the pre-v1.0.0 reality (tags follow
pi npm version, builds FROM joakimp/pi-devbox:base-pi-only, opencode-
devbox lineage as source of truth) — none of that has been true since
v1.0.0 decoupled. End users on Hub got a misleading story.
DOCKER_HUB.md changes:
- Versioning section rewritten: semver from v1.0.0, with a deprecation
note for the pre-v1.0.0 v{pi_version}[letter] scheme.
- New 'Build pipeline' section briefly explains the two-phase
base/variant content-addressed structure so users understand what
base-<hash> and base-latest tags are for.
- New 'Document and image tooling' section (pandoc, graphviz,
imagemagick) added since these are new in v1.0.0 and broadly useful.
- Tealdeer noted (vs the old Node tldr).
- Tmux 0-indexing called out (relevant for future :latest-studio
variant).
- Removed all 'pi-only build' / 'FROM base-pi-only' / 'opencode-devbox
bakes the pi version' framing — pi-devbox is now self-contained.
- New {{PI_VERSION}} placeholder in 4 locations so the Hub description
always shows which pi is in :latest.
Workflow change:
- update-description step now substitutes {{PI_VERSION}} placeholders
in DOCKER_HUB.md before sending to Hub. PI_VERSION comes from the
resolve-versions output (same one baked into the image), so the page
and image can never disagree. Sanity-check fails the step if any
unsubstituted placeholder remains.
|
||
|
|
b55b44e7b6 |
ci: shorten Hub short-description to ≤100 bytes + resolve toolkit/extensions to SHAs
The v1.0.0 release run failed at update-description because Docker Hub's short-description field has a 100-byte limit and the previous string was 151 bytes (the em dash is 3 bytes UTF-8). The image itself shipped fine — only the cosmetic Hub description patch failed. Changes: - Short description: 'Linux container with the pi coding-agent, MemPalace, and curated dev tooling.' (77 bytes, was 151) - resolve-versions now also resolves pi-toolkit and pi-extensions main HEADs to commit SHAs so workflow_dispatch re-runs produce byte-identical images when those repos haven't moved. Fork+obsmem were already SHA-resolved; toolkit+extensions were branch-named (drift risk on re-runs that we got lucky on for v1.0.0). |
||
|
|
c1154f1fa6 |
v1.0.0: decouple from opencode-devbox
Publish Docker Image / resolve-versions (push) Successful in 5s
Publish Docker Image / base-decide (push) Successful in 12s
Publish Docker Image / build-base (push) Successful in 45m47s
Publish Docker Image / smoke (push) Successful in 8m18s
Publish Docker Image / build-variant (push) Successful in 22m41s
Publish Docker Image / update-description (push) Failing after 9s
Publish Docker Image / promote-base-latest (push) Successful in 14s
Self-contained build chain — own Dockerfile.base + Dockerfile.variant
+ entrypoint scripts + rootfs + CI pipeline. Previously v0.79.0 and
earlier were thin re-brands of opencode-devbox's pi-only variant
(joakimp/pi-devbox:base-pi-only built by opencode-devbox CI).
Architectural changes:
- Replace 5-line Dockerfile shim with full base+variant pair.
- Adapt CI workflow from opencode-devbox/docker-publish-split.yml,
simplified to a single variant. Includes content-addressed base hash,
PI_VERSION concrete-resolution to defeat registry-buildcache footgun,
crane-based base-latest promotion, and the c6f9d11 smoke-test gate.
- pi-devbox releases no longer require rebuilding opencode-devbox first.
Base image additions:
- pandoc, graphviz, imagemagick, yq — broadly useful, ~260 MB total.
- tldr (tealdeer) — Rust port replaces Node tldr global, saves 135 MB.
- /etc/tmux.conf with base-index 0 + pane-base-index 0 — required for
the planned :latest-studio variant; pi-studio hard-codes :0.0 target.
Smoke test:
- New checks for pandoc, graphviz, imagemagick, yq, tldr, tmux config,
/tmp/sshcm directory.
- Image-size measurement now sums docker history layers (the prior
inspect --format='{{.Size}}' returned only the variant-unique layer
with the new base/variant split, understating by 2+ GB).
- Threshold 2850 → 3500 MB to absorb base additions + arch margin.
Image size:
- Local arm64 build: 3.20 GB. ~390 MB up from prior pi-only equivalent.
- Will tighten threshold once amd64 actuals settle in CI.
Pre-1.0 history preserved at tag pre-v1.0.0-decouple-backup.
Future work:
- v1.1.0: :latest-studio variant (adds pi-studio).
- v1.2.0: :latest-studio-tex variant (adds texlive-xetex for PDF).
- opencode-devbox v2.0.0 will retire INSTALL_PI / pi-only paths.
|
||
|
|
c139be326f |
refactor: re-brand the opencode-devbox with-pi variant (single source of truth)
pi-devbox no longer installs pi itself. The Dockerfile is now a thin FROM joakimp/opencode-devbox:latest-with-pi (overridable via BASE_IMAGE), inheriting pi + pi-toolkit + pi-extensions + pi-fork (fork) + pi-observational-memory (recall) + the LAN-access helper + all base tooling from the single source of truth. Eliminates the install-logic duplication that drifted against opencode-devbox/Dockerfile.variant (decision #3). Consequences (documented in CHANGELOG/AGENTS): - The image now ALSO contains opencode (with-pi has INSTALL_OPENCODE=true). A leaner pi-only image would need a dedicated pi-only variant upstream. - Publish ordering: release opencode-devbox first so latest-with-pi carries the target pi version, THEN tag this repo. The smoke test asserts pi --version matches the tag (EXPECTED_PI_VERSION) and fails loudly if the base is stale — turning the version coupling into an enforced ordering guard. CI: drop PI_VERSION build-arg (Dockerfile installs nothing); keep tag->version resolution to feed the smoke base-freshness guard. Smoke adds fork/recall clone + node_modules + settings.json registration checks; size threshold 2200 -> 2900 MB (now tracks with-pi). Docs updated across README, AGENTS, DOCKER_HUB, .env.example, docker-compose. |
||
|
|
2d397663d5 |
ci: workflow-level 3-attempt retry around buildx build --push
Belt-and-braces against transient registry-1.docker.io blips (rate limits, brief 5xx, CDN flap). Replaces docker/build-push-action@v7 with a shell: bash step that runs docker buildx build --push in a for-loop with backoff (15s, 30s). Does NOT mask deterministic failures: a true regression (e.g. the cache-export 400 we hit 2026-05-23..28) fails all 3 attempts identically and the job still fails by design. Orthogonal layer to both cache-export disablement and the ci-release-watcher skill's transient-rerun heuristic. No image-side change. |
||
|
|
e6a21f36f1 |
Cut v0.76.0 — pi 0.76.0 + inherit SSH-CM/gitleaks from base-latest
pi 0.75.5 → 0.76.0 (published upstream 2026-05-27 20:03 UTC). First pi-devbox release built against opencode-devbox base-latest carrying the SSH ControlMaster bake-in (commit 668592d) and gitleaks (73a7f96) — both inherited transparently with no Dockerfile change here. PI_VERSION is resolved from the git tag by the workflow (v0.75.5b cache-hit fix), so no Dockerfile default bump needed. Workflow change: registry cache-export removed from publish step. buildkit mode=max cache-export to registry-1.docker.io reproducibly returns HTTP 400 (Hub-CDN protocol mismatch with buildx 0.34.x, surfaced ~2026-05-23). Diagnosed during opencode-devbox v1.15.12 manual publish: image push works, only --cache-to fails. Pi-devbox would hit the same regression on the next tag push without this fix. See opencode-devbox CHANGELOG v1.15.12 for the full root-cause analysis. Pi-devbox is single-stage with a tiny diff (npm install pi only) on top of base-latest, so builds are fast even uncached. |
||
|
|
34cae2a1d2 |
Cut v0.75.5b — fix cache-hit silent same-bytes regression
ALL FOUR releases v0.74.0 -> v0.75.5 had been shipping the same image bytes due to a Docker layer-cache hit on the bare 'npm install -g @earendil-works/pi-coding-agent' command (when PI_VERSION=latest). The command string is identical across builds, so the layer-hash is identical, so registry buildcache (cache-from/cache-to) silently reuses the layer from whatever pi version was current when the cache was first populated. Verification: docker manifest inspect joakimp/pi-devbox:vX.Y.Z showed identical SHA256 digests on both linux/amd64 and linux/arm64 for v0.74.0, v0.75.3, v0.75.4, v0.75.5. Users on :latest were getting whatever pi version was baked into the v0.74.0 build. DISCOVERED 2026-05-23 by user trying to update pi-devbox on MBP-M1 and seeing pi 0.74.0 reported despite pulling v0.75.5. CHANGES .gitea/workflows/docker-publish.yml — both smoke and publish jobs get a new 'Resolve PI_VERSION from tag' step that strips the leading 'v' and any trailing letter suffix from github.ref_name. Result is passed as a build-arg to docker/build-push-action so the npm install layer's hash includes the concrete version, forcing cache miss when pi bumps. scripts/smoke-test.sh — new run_expect helper that asserts pi --version contains the EXPECTED_PI_VERSION env var. Smoke job sets this from the resolve step output. Would have caught this regression on v0.75.3. Dockerfile — comment block above ARG PI_VERSION=latest documenting the cache-hit footgun. The 'if latest' branch in the install RUN is preserved for local dev convenience but never fires in CI now. AGENTS.md — new convention bullet explaining the cache-hit class of bug and noting the latent same-bug in opencode-devbox's with-pi variants (currently masked by OPENCODE_VERSION bumps; will manifest when cutting a vN.N.Nb-style opencode-version-unchanged release that only bumps pi). CHANGELOG.md — full entry under v0.75.5b describing the recovery, the silent-failure mechanism, and the verification steps. NO IMAGE-CONTENT CHANGES vs v0.75.5 INTENT. This build produces the actual pi 0.75.5 image content that v0.75.5 was supposed to ship. NEXT FOLLOWUP (parked, not in this commit) opencode-devbox should get the same workflow change for its build-variant-with-pi and build-variant-omos-with-pi jobs. Currently masked because every release also bumps OPENCODE_VERSION which invalidates the cache, but that masking would fail on a pi-only bump release. |
||
|
|
7b634605b7 |
Initial commit: pi-devbox v0.74.0
pi coding-agent container built on opencode-devbox:base-latest. Includes Dockerfile, docker-compose, CI workflow, smoke-test, README, CHANGELOG, AGENTS.md. |