Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 56e3742a2d | |||
| 784fad78f3 | |||
| c2c42c34a2 | |||
| f3b3748223 |
@@ -222,6 +222,13 @@ jobs:
|
|||||||
- name: Components the next build would bake differently are named in the CHANGELOG
|
- name: Components the next build would bake differently are named in the CHANGELOG
|
||||||
run: bash scripts/check-doc-drift.sh
|
run: bash scripts/check-doc-drift.sh
|
||||||
|
|
||||||
|
# Runs HERE as well as in lint.yml deliberately. A guard that gates only
|
||||||
|
# `push` lets a tag regress — the adoption slip that let doc-drift land
|
||||||
|
# 27 h after the v1.9.3 tag. This one protects the tag build from the
|
||||||
|
# zero-failing-step failure mode that killed v1.10.0 and v1.10.1.
|
||||||
|
- name: Dockerfile.variant still opens with its BuildKit check directive
|
||||||
|
run: bash scripts/check-dockerfile-directives.sh Dockerfile.variant
|
||||||
|
|
||||||
resolve-versions:
|
resolve-versions:
|
||||||
# Gated: a defective tree must not reach a 46-minute base build.
|
# Gated: a defective tree must not reach a 46-minute base build.
|
||||||
needs: [lint-gate]
|
needs: [lint-gate]
|
||||||
|
|||||||
@@ -90,6 +90,12 @@ jobs:
|
|||||||
# actionlint so the more precise diagnostic surfaces first.
|
# actionlint so the more precise diagnostic surfaces first.
|
||||||
run: bash scripts/check-workflow-shell.sh .gitea/workflows
|
run: bash scripts/check-workflow-shell.sh .gitea/workflows
|
||||||
|
|
||||||
|
- name: Dockerfile.variant still opens with its BuildKit check directive
|
||||||
|
# See scripts/check-dockerfile-directives.sh: without that first line the
|
||||||
|
# smoke jobs fail with `invalid format delimiter 'ghadelimiter_...'` and
|
||||||
|
# NO failing step. Also wired into docker-publish.yml's lint-gate.
|
||||||
|
run: bash scripts/check-dockerfile-directives.sh Dockerfile.variant
|
||||||
|
|
||||||
- name: Install actionlint (pinned)
|
- name: Install actionlint (pinned)
|
||||||
env:
|
env:
|
||||||
ACTIONLINT_VERSION: 1.7.12
|
ACTIONLINT_VERSION: 1.7.12
|
||||||
|
|||||||
+110
-1
@@ -11,7 +11,116 @@ Pre-v1.0.0 tags followed the pi npm version (`v{pi_version}[letter]`).
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## v1.10.0 — 2026-10-02
|
## v1.10.2 — 2026-10-02
|
||||||
|
|
||||||
|
**v1.10.0 and v1.10.1 were both tagged and never published.** Neither failure
|
||||||
|
was in the image — both were in the test and CI harness, and each hid the other:
|
||||||
|
|
||||||
|
| tag | run | symptom | cause |
|
||||||
|
|---|---|---|---|
|
||||||
|
| v1.10.0 | 704 | `smoke` 100 passed / **1 failed**; `smoke-studio` 103 / **1** | a smoke assertion required a pi dependency upstream had deleted |
|
||||||
|
| v1.10.1 | 707 | `smoke` **101 passed / 0 failed**, `smoke-studio` **104 / 0**, and both jobs red anyway with **no failing step** | base64(`Dockerfile.variant`) in buildx's warning metadata crossed act_runner's 64 KiB line cap |
|
||||||
|
|
||||||
|
In both runs every publish job skipped, so no image, no `latest`, and no Hub
|
||||||
|
description was ever written — the gates did their job twice. **Everything
|
||||||
|
described under v1.10.0 below ships here**; that section remains the content
|
||||||
|
record and nothing in it changed.
|
||||||
|
|
||||||
|
The tag number moves each time rather than being re-pointed, because CI had
|
||||||
|
already consumed the previous one. A version that failed its build should stay
|
||||||
|
failed and readable, not be quietly overwritten with different bytes.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **A job can fail with every step green, a smoke suite reporting `0 failed`,
|
||||||
|
and no failing step anywhere — and it cost two tags** — `Dockerfile.variant`
|
||||||
|
now opens with `# check=skip=InvalidDefaultArgInFrom`, and
|
||||||
|
`scripts/check-dockerfile-directives.sh` keeps it there.
|
||||||
|
|
||||||
|
The chain, measured rather than reasoned: `ARG BASE_IMAGE` deliberately has no
|
||||||
|
default (the two-phase build always supplies it), which trips BuildKit's
|
||||||
|
`InvalidDefaultArgInFrom` check. buildx attaches that warning's source context
|
||||||
|
to the build metadata as `buildx.build.warnings[].sourceInfo.data` — **the
|
||||||
|
entire Dockerfile, base64, on one line**. `docker/build-push-action` writes
|
||||||
|
that metadata to `$GITHUB_OUTPUT` as a `name<<ghadelimiter_<uuid>` heredoc,
|
||||||
|
and Gitea's act_runner truncates any single line at exactly 65536 chars. Once
|
||||||
|
base64 of the file passed 64 KiB the closing delimiter was cut off and the
|
||||||
|
runner reported `invalid format delimiter 'ghadelimiter_...' not found before
|
||||||
|
end of file`, then failed the job while attributing it to nothing.
|
||||||
|
|
||||||
|
Numbers: the file was 42251 B at v1.9.4 → base64 56355 chars, fine; it is
|
||||||
|
50034 B now → base64 66712, truncated to exactly 65536 (2^16, i.e. cut *at*
|
||||||
|
the cap, not merely long). The cap corresponds to a 49152 B Dockerfile, so
|
||||||
|
this release's comment growth — much of it comments added while documenting
|
||||||
|
the v1.10.0 round — crossed it by **882 bytes**. Confirmed two ways: by
|
||||||
|
parsing the metadata out of both job logs, and independently by arithmetic on
|
||||||
|
the file size at each tag. Verified fixed by running `docker buildx build
|
||||||
|
--check` against the actual edited file: *"Check complete, no warnings
|
||||||
|
found."* With zero warnings there is no `sourceInfo`, so the longest metadata
|
||||||
|
line drops from 65536 to ~1936 chars and the Dockerfile's **size stops being
|
||||||
|
coupled to whether CI passes**.
|
||||||
|
|
||||||
|
Hypotheses measured and rejected on the way, each of which would have produced
|
||||||
|
a wrong fix: floating action tags moving (the act action-bundle hashes are
|
||||||
|
byte-identical between the green v1.9.4 run and the red one — all four); the
|
||||||
|
build-check annotation text changing (byte-identical); and `provenance: false`,
|
||||||
|
which was written, tested against a real buildx, and **reverted** when the
|
||||||
|
metadata turned out to carry the base64 under `buildx.build.warnings`, not
|
||||||
|
under provenance. The published images are unaffected either way: they are
|
||||||
|
built by raw `docker buildx build --push`, which never writes
|
||||||
|
`$GITHUB_OUTPUT` metadata.
|
||||||
|
|
||||||
|
The new guard runs in **both** `lint.yml` and the publish workflow's
|
||||||
|
`lint-gate`, because a check that gates only `push` lets a tag regress — the
|
||||||
|
same adoption slip that let doc-drift land 27 h after the v1.9.3 tag. It
|
||||||
|
also fails if `ARG BASE_IMAGE` ever gains a default, so the skip directive
|
||||||
|
cannot rot into pointing at a check that can no longer fire. Truth table:
|
||||||
|
directive present → rc=0; removed → rc=1; demoted to line 2 → rc=1; ARG given
|
||||||
|
a default → rc=1; file missing → rc=2 (cannot-run must not pass).
|
||||||
|
|
||||||
|
- **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.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## v1.10.0 — 2026-10-02 (tagged, never published — content shipped as v1.10.2)
|
||||||
|
|
||||||
|
> Tagged 2026-10-02 at commit `12f99c4`. Its build (run 704) went red on the
|
||||||
|
> stale clipboard assertion described under v1.10.1, so `build-variant`,
|
||||||
|
> `build-variant-studio`, `promote-base-latest` and `update-description` all
|
||||||
|
> skipped and nothing reached the Hub. No image carries this tag. The content
|
||||||
|
> below is accurate and shipped as **v1.10.2**. v1.10.1 (commit `c2c42c3`,
|
||||||
|
> run 707) was the first attempt at republishing it and died on the separate
|
||||||
|
> act_runner line-cap bug described under v1.10.2.
|
||||||
|
|
||||||
Three small fixes found by the v1.9.4 first-boot acceptance and the CI base
|
Three small fixes found by the v1.9.4 first-boot acceptance and the CI base
|
||||||
hash prediction, plus one boot-time wiring fix that stops a recurring `git push`
|
hash prediction, plus one boot-time wiring fix that stops a recurring `git push`
|
||||||
|
|||||||
+37
-3
@@ -1,3 +1,37 @@
|
|||||||
|
# check=skip=InvalidDefaultArgInFrom
|
||||||
|
#
|
||||||
|
# ^ MUST stay the FIRST line of this file, and it is load-bearing for CI, not
|
||||||
|
# style. BuildKit parses `# check=` only before any other line, so moving it
|
||||||
|
# below the title comment silently disables it.
|
||||||
|
#
|
||||||
|
# Why it exists: `ARG BASE_IMAGE` (below) deliberately has NO default — this
|
||||||
|
# file is only ever built by the two-phase CI with --build-arg BASE_IMAGE=
|
||||||
|
# <image>:base-<hash>. BuildKit's InvalidDefaultArgInFrom check flags that as
|
||||||
|
# "default value for global ARG results in an empty or invalid base image
|
||||||
|
# name". There is no honest default to give it: `scratch` would satisfy the
|
||||||
|
# linter while being a lie (nothing here can build FROM scratch), and it would
|
||||||
|
# convert today's instant "invalid reference format" into a failure deep in the
|
||||||
|
# build. So the check is skipped by name, with the reason written down.
|
||||||
|
#
|
||||||
|
# What the warning actually COST, which is why this is not cosmetic: buildx
|
||||||
|
# attaches the warning's source context to the build metadata as
|
||||||
|
# buildx.build.warnings[].sourceInfo.data — the ENTIRE Dockerfile, base64, on
|
||||||
|
# ONE line. docker/build-push-action writes that metadata to $GITHUB_OUTPUT as
|
||||||
|
# a `name<<ghadelimiter_<uuid>` heredoc, and Gitea's act_runner truncates any
|
||||||
|
# single line at exactly 65536 chars. Once base64(this file) crossed 64 KiB the
|
||||||
|
# closing delimiter was cut off, and the runner failed the job with
|
||||||
|
# invalid format delimiter 'ghadelimiter_...' not found before end of file
|
||||||
|
# and NO failing step: every step green, smoke suite "0 failed", job red.
|
||||||
|
# Measured: 42251 B at v1.9.4 -> base64 56355 (fine); 50034 B at v1.10.1 ->
|
||||||
|
# base64 66712, truncated to exactly 65536. The cap corresponds to a 49152 B
|
||||||
|
# Dockerfile, so this release's comment growth crossed it by 882 B. It killed
|
||||||
|
# v1.10.0 (run 704, where a stale clipboard assertion hid it) and v1.10.1
|
||||||
|
# (run 707). With zero warnings there is no sourceInfo, so the longest metadata
|
||||||
|
# line drops from 65536 to ~1936 chars and the Dockerfile's SIZE stops being
|
||||||
|
# coupled to CI passing at all.
|
||||||
|
#
|
||||||
|
# If this ever recurs — job red, zero failing steps, suite reporting 0 failed —
|
||||||
|
# grep the job log for `ghadelimiter` first.
|
||||||
# pi-devbox — variant image
|
# pi-devbox — variant image
|
||||||
#
|
#
|
||||||
# FROMs a base-<hash> image produced by Dockerfile.base and adds only
|
# FROMs a base-<hash> image produced by Dockerfile.base and adds only
|
||||||
@@ -154,11 +188,11 @@ ARG USER_NAME=developer
|
|||||||
# (e7d77dc -> 1529e14 -> 731c3d4 in the nine days to 2026-10-01), so waiting for
|
# (e7d77dc -> 1529e14 -> 731c3d4 in the nine days to 2026-10-01), so waiting for
|
||||||
# a tag means holding pi indefinitely. A full SHA keeps the one property
|
# a tag means holding pi indefinitely. A full SHA keeps the one property
|
||||||
# `master` does not have: rebuilding this tag later produces the SAME image.
|
# `master` does not have: rebuilding this tag later produces the SAME image.
|
||||||
# v1.9.5 SKIPPED 0.99.x and 1.0.0 for lack of evidence. v1.10.0 went and got the
|
# v1.9.5 SKIPPED 0.99.x and 1.0.0 for lack of evidence. v1.10.2 went and got the
|
||||||
# evidence instead of waiting for obsmem to mention a version, because "no issue
|
# evidence instead of waiting for obsmem to mention a version, because "no issue
|
||||||
# names 1.0" is an absence of a statement, not a measurement.
|
# names 1.0" is an absence of a statement, not a measurement.
|
||||||
#
|
#
|
||||||
# v1.10.0: 0.87.1 -> 1.0.0. MEASURED 2026-10-02 against the published npm
|
# v1.10.2: 0.87.1 -> 1.0.0. MEASURED 2026-10-02 against the published npm
|
||||||
# tarballs for 0.87.1 and 1.0.0, unpacked side by side:
|
# tarballs for 0.87.1 and 1.0.0, unpacked side by side:
|
||||||
# 1. NO `### Breaking Changes` SECTION IN 1.0.0 AT ALL. The changelog's
|
# 1. NO `### Breaking Changes` SECTION IN 1.0.0 AT ALL. The changelog's
|
||||||
# breaking sections belong to 0.87.0, 0.86.0, 0.84.3, 0.84.0, 0.83.0,
|
# breaking sections belong to 0.87.0, 0.86.0, 0.84.3, 0.84.0, 0.83.0,
|
||||||
@@ -273,7 +307,7 @@ ARG PI_ATELIER_REPO=https://github.com/michaelmjhhhh/pi-atelier.git
|
|||||||
# pi >=0.84.0, so it still spans the pinned 0.85.1. 13 commits v0.10.1..v0.10.3,
|
# pi >=0.84.0, so it still spans the pinned 0.85.1. 13 commits v0.10.1..v0.10.3,
|
||||||
# all under src/ tests/ docs/ scripts/ plus metadata; no entry-point move.
|
# all under src/ tests/ docs/ scripts/ plus metadata; no entry-point move.
|
||||||
#
|
#
|
||||||
# v1.10.0: v0.10.3 -> v0.13.0, closing the two-minor gap v1.9.5 flagged as the
|
# v1.10.2: v0.10.3 -> v0.13.0, closing the two-minor gap v1.9.5 flagged as the
|
||||||
# residual risk of the pi bump. peerDependencies are UNCHANGED at pi >=0.84.0
|
# residual risk of the pi bump. peerDependencies are UNCHANGED at pi >=0.84.0
|
||||||
# across v0.10.3, v0.12.1 and v0.13.0 — still a FLOOR, so still not evidence of
|
# across v0.10.3, v0.12.1 and v0.13.0 — still a FLOOR, so still not evidence of
|
||||||
# anything; the floor is satisfied by 1.0.0 either way. What was actually
|
# anything; the floor is satisfied by 1.0.0 either way. What was actually
|
||||||
|
|||||||
@@ -358,7 +358,7 @@ DOT syntax errors instead of crashing. Then in Studio: open the PNG (or a
|
|||||||
`.md` that embeds it) and hit **refresh-from-disk** after each edit.
|
`.md` that embeds it) and hit **refresh-from-disk** after each edit.
|
||||||
Note: SVG is **not** in Studio's local-image-link allowlist — use PNG.
|
Note: SVG is **not** in Studio's local-image-link allowlist — use PNG.
|
||||||
|
|
||||||
## Terminal UI mode: fullscreen is the default (since v1.10.0)
|
## Terminal UI mode: fullscreen is the default (since v1.10.2)
|
||||||
|
|
||||||
pi **1.0.0** changed the default terminal UI mode to **fullscreen**, and this
|
pi **1.0.0** changed the default terminal UI mode to **fullscreen**, and this
|
||||||
image adopts upstream's default rather than overriding it. Fullscreen draws into
|
image adopts upstream's default rather than overriding it. Fullscreen draws into
|
||||||
|
|||||||
Executable
+69
@@ -0,0 +1,69 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Guard: Dockerfile.variant must OPEN with its BuildKit `# check=` directive.
|
||||||
|
#
|
||||||
|
# Why this is a gate and not a comment. BuildKit parses `# check=` ONLY before
|
||||||
|
# any other line in the file, so moving it below the title comment — or dropping
|
||||||
|
# it during an unrelated header edit — silently re-enables the
|
||||||
|
# InvalidDefaultArgInFrom warning on `ARG BASE_IMAGE` / `FROM ${BASE_IMAGE}`.
|
||||||
|
#
|
||||||
|
# A re-enabled warning is not cosmetic. buildx attaches the warning's source
|
||||||
|
# context to the build metadata as buildx.build.warnings[].sourceInfo.data: the
|
||||||
|
# entire Dockerfile, base64, on ONE line. docker/build-push-action writes that
|
||||||
|
# metadata to $GITHUB_OUTPUT as a `name<<ghadelimiter_<uuid>` heredoc, and
|
||||||
|
# Gitea's act_runner truncates any single line at exactly 65536 chars. Since
|
||||||
|
# base64(Dockerfile.variant) passed 64 KiB (50034 B source -> 66712 chars, cut
|
||||||
|
# to 65536), the closing delimiter is lost and act_runner fails the job with
|
||||||
|
# invalid format delimiter 'ghadelimiter_...' not found before end of file
|
||||||
|
# while attributing it to NO step: every step reports Success, the smoke suite
|
||||||
|
# prints "0 failed", and the job is red regardless. That cost two tags —
|
||||||
|
# v1.10.0 (run 704, where a stale clipboard assertion masked it) and v1.10.1
|
||||||
|
# (run 707) — and most of a session to localise.
|
||||||
|
#
|
||||||
|
# So: one deterministic grep, run both on push AND in the publish workflow's
|
||||||
|
# lint-gate. A check that gates only `push` lets a tag regress — the same
|
||||||
|
# adoption slip that let doc-drift land 27 h after the v1.9.3 tag.
|
||||||
|
#
|
||||||
|
# Exit codes: 0 OK, 1 violation, 2 cannot-run (missing file) — matching
|
||||||
|
# lint-shell.sh / check-skill-floor.sh / check-doc-drift.sh, because a gate that
|
||||||
|
# cannot run must not pass.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
DF="${1:-Dockerfile.variant}"
|
||||||
|
EXPECTED='# check=skip=InvalidDefaultArgInFrom'
|
||||||
|
|
||||||
|
if [ ! -f "$DF" ]; then
|
||||||
|
echo "::error::check-dockerfile-directives: '$DF' not found (cannot-run)" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
first="$(head -n 1 "$DF")"
|
||||||
|
if [ "$first" != "$EXPECTED" ]; then
|
||||||
|
{
|
||||||
|
echo "::error::$DF line 1 must be exactly: $EXPECTED"
|
||||||
|
echo "::error::found instead: ${first:-<empty>}"
|
||||||
|
echo "::error::"
|
||||||
|
echo "::error::BuildKit only honours '# check=' before any other line. Without it the"
|
||||||
|
echo "::error::InvalidDefaultArgInFrom warning returns, buildx embeds this whole file as"
|
||||||
|
echo "::error::base64 in buildx.build.warnings[].sourceInfo.data, that single line exceeds"
|
||||||
|
echo "::error::act_runner's 65536-char cap, and the smoke jobs fail with"
|
||||||
|
echo "::error:: invalid format delimiter 'ghadelimiter_...' not found before end of file"
|
||||||
|
echo "::error::and NO failing step. See the header of $DF for the full measurement."
|
||||||
|
} >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Second, independent assertion: the condition the directive exists FOR. If a
|
||||||
|
# later edit gives ARG BASE_IMAGE a default, the directive becomes dead weight
|
||||||
|
# and should be removed deliberately rather than left to rot — and if the ARG is
|
||||||
|
# renamed, this guard would otherwise keep passing while guarding nothing.
|
||||||
|
if ! grep -qE '^ARG BASE_IMAGE$' "$DF"; then
|
||||||
|
{
|
||||||
|
echo "::error::$DF no longer contains a bare 'ARG BASE_IMAGE' (no default)."
|
||||||
|
echo "::error::That is the only thing '$EXPECTED' suppresses. Either restore the bare ARG"
|
||||||
|
echo "::error::or drop the directive and this guard together — do not leave a skip"
|
||||||
|
echo "::error::directive pointing at a check that can no longer fire."
|
||||||
|
} >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Dockerfile directive guard OK — $DF opens with the check-skip directive and still declares a bare ARG BASE_IMAGE."
|
||||||
+22
-6
@@ -30,7 +30,7 @@
|
|||||||
# client bundle present + registered via `pi install`
|
# client bundle present + registered via `pi install`
|
||||||
# - no foreign npm-11 platform packages (@esbuild, clipboard) beyond the host
|
# - no foreign npm-11 platform packages (@esbuild, clipboard) beyond the host
|
||||||
# - no build-time npm cache (/root/.npm) shipped in the image
|
# - 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
|
# - image size within threshold
|
||||||
|
|
||||||
set -euo pipefail
|
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)" \
|
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'
|
'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
|
# Clipboard is the family pruned second. Upstream pi 0.86.0 (#9163) REPLACED the
|
||||||
# binding at require() time — so a successful load IS the proof that the kept
|
# external `@mariozechner/clipboard` dependency with bundled macOS/Windows/X11
|
||||||
# platform package is the one this image needs.
|
# helpers plus the OSC 52 fallback, so from 0.86.0 on there is nothing external to
|
||||||
run "clipboard native loads at every install site" \
|
# prune and nothing to require(). pi 0.85.1 declared it; 0.87.1 and 1.0.0 do not.
|
||||||
'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'
|
# 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 ────────────────────────────────────────────────────────
|
# ── Image size ────────────────────────────────────────────────────────
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
Reference in New Issue
Block a user