diff --git a/CHANGELOG.md b/CHANGELOG.md index e0603c1..95a91ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -209,6 +209,38 @@ jump, four new base packages and a new mailbox feature. The release carrying a ### Fixed +- **The smoke suite asserted a pi dependency that upstream deleted, and it cost + this release its first tag build** — `scripts/smoke-test.sh` required + `@mariozechner/clipboard` to be installed and `require()`-able at every install + site. pi **0.86.0** (`#9163`) *"Replaced the external native clipboard + dependency with bundled asynchronous macOS, Windows, and X11 helpers while + preserving platform command and OSC 52 fallbacks"*. Measured in the published + tarballs: pi `0.85.1` declares `@mariozechner/clipboard@0.3.9`; `0.87.1` and + `1.0.0` declare no `@mariozechner/*` at all. So on a correct v1.10.0 image the + package is legitimately absent, the assertion's `if [ -z "$sites" ]; then exit + 1` fired, and run 704 ended **100 passed / 1 failed** on `smoke` and **103 + passed / 1 failed** on `smoke-studio` — same single assertion, every + studio-specific check green. `build-variant`, `build-variant-studio`, + `promote-base-latest` and `update-description` were all skipped, so **nothing + was published**: the gate behaved exactly as designed, against a stale + expectation rather than a real defect. + + The fix does not delete the guard, because the guard was right: "fail when the + family is absent" is what stops `prune_foreign_natives()` from silently + deleting the binding instead of the surplus platform copies. It now **derives + its expectation from what the image's pi actually declares** — if pi declares + the dependency an install must exist and load; if pi does not, no install may + linger. That re-arms by itself should a future pi re-add it, and it still + catches an orphaned install. Verified as a four-quadrant truth table with the + command string extracted verbatim from the file and run through `sh -c` the way + `run()` invokes it: declared+present → rc=0, declared+absent → rc=1, + undeclared+absent → rc=0, undeclared+present → rc=1. + + Worth stating plainly, since it is the whole value of the round: pi 1.0.0, + pi-atelier v0.13.0 and pi-studio v0.9.61 passed **103 of 104** assertions on a + pi MAJOR bump. The one red was the suite describing pi 0.85.1's dependency + graph, not the image. + - **`git push` from inside the container dies on a read-only ControlPath, and no amount of documentation was fixing it** — `entrypoint-user.sh` now sets `core.sshCommand` to `ssh -F $HOME/.ssh-local/config` when that sidecar exists. diff --git a/scripts/smoke-test.sh b/scripts/smoke-test.sh index bba8bb1..aed10a5 100755 --- a/scripts/smoke-test.sh +++ b/scripts/smoke-test.sh @@ -30,7 +30,7 @@ # client bundle present + registered via `pi install` # - no foreign npm-11 platform packages (@esbuild, clipboard) beyond the host # - no build-time npm cache (/root/.npm) shipped in the image -# - esbuild compiles + clipboard native loads at every install site +# - esbuild compiles everywhere; clipboard present iff pi declares it # - image size within threshold set -euo pipefail @@ -986,11 +986,27 @@ run "no build-time npm cache shipped (/root/.npm)" \ run "esbuild works at every install site (prune removed weight, not function)" \ 'sites=$(find /usr/lib/node_modules /opt -type d -path "*/node_modules/esbuild" -prune 2>/dev/null); if [ -z "$sites" ]; then echo "no esbuild install found at all" >&2; exit 1; fi; for d in $sites; do node -e "require(\"$d\").transformSync(\"const x:number=1\",{loader:\"ts\"})" || { echo "esbuild broken at $d" >&2; exit 1; }; done; echo ok' -# Clipboard is the family pruned second, and its napi-rs loader picks its native -# binding at require() time — so a successful load IS the proof that the kept -# platform package is the one this image needs. -run "clipboard native loads at every install site" \ - 'sites=$(find /usr/lib/node_modules /opt -type d -path "*/node_modules/@mariozechner/clipboard" -prune 2>/dev/null); if [ -z "$sites" ]; then echo "no @mariozechner/clipboard install found at all" >&2; exit 1; fi; for d in $sites; do node -e "var c=require(\"$d\"); if (typeof c.setText !== \"function\") { throw new Error(\"native binding missing\"); }" || { echo "clipboard native broken at $d" >&2; exit 1; }; done; echo ok' +# Clipboard is the family pruned second. Upstream pi 0.86.0 (#9163) REPLACED the +# external `@mariozechner/clipboard` dependency with bundled macOS/Windows/X11 +# helpers plus the OSC 52 fallback, so from 0.86.0 on there is nothing external to +# prune and nothing to require(). pi 0.85.1 declared it; 0.87.1 and 1.0.0 do not. +# This assertion therefore derives its expectation from what the image's pi +# actually DECLARES rather than hardcoding either state: it re-arms by itself if a +# future pi re-adds the dependency, and it still fails if an install lingers that +# nothing depends on. Hardcoding "must exist" is what failed the v1.10.0 tag build +# (100 passed, 1 failed, nothing published) against a perfectly correct image. +run "clipboard native: present iff pi declares it (prune removed weight, not function)" \ + 'PKG=/usr/lib/node_modules/@earendil-works/pi-coding-agent/package.json; + if grep -q "\"@mariozechner/clipboard\"" "$PKG" 2>/dev/null; then declared=yes; else declared=no; fi; + sites=$(find /usr/lib/node_modules /opt -type d -path "*/node_modules/@mariozechner/clipboard" -prune 2>/dev/null); + if [ "$declared" = yes ]; then + [ -n "$sites" ] || { echo "pi declares @mariozechner/clipboard but NO install found - prune removed function" >&2; exit 1; }; + for d in $sites; do node -e "var c=require(\"$d\"); if (typeof c.setText !== \"function\") { throw new Error(\"native binding missing\"); }" || { echo "clipboard native broken at $d" >&2; exit 1; }; done; + echo "ok: pi declares it and the native binding loads at every site"; + else + [ -z "$sites" ] || { echo "pi no longer declares @mariozechner/clipboard yet installs remain: $sites" >&2; exit 1; }; + echo "ok: pi bundles clipboard since upstream 0.86.0 (#9163); no external install expected"; + fi' # ── Image size ──────────────────────────────────────────────────────── echo ""