4d - every gate leg names itself, its image and its seconds #106
@@ -238,12 +238,13 @@ The leg re-checks the conversions and nothing else — each fixture's emitted ma
|
||||
document, its error code — leaving the corpus's pairing, uniqueness, source positions and
|
||||
byte-level equality to the Node suite that owns them.
|
||||
|
||||
Every leg announces its name and its image before it runs and its elapsed time after, `publish.sh`
|
||||
alongside `ci.sh`, so a long run reads as progress rather than as a hang — which is what a silent
|
||||
one cost the `0.1.0` release. A leg added later owes the same marker, and a leg that buffers its
|
||||
output to read something out of it streams and keeps the copy in a file: `tee /dev/stderr` reopens
|
||||
the stream, so under the `> log 2>&1` a reader runs locally the two offsets advance independently
|
||||
and punch NUL holes through each other's lines (4d).
|
||||
Every leg announces its name, and the image where it runs in one, before it runs and its elapsed
|
||||
time after, `publish.sh` alongside `ci.sh`, so a long run reads as progress rather than as a hang.
|
||||
A leg added later owes the same marker, and a function a leg runs chains its statements with `&&`,
|
||||
because the `||` that captures the leg's status suspends `set -e` for everything it calls. A leg
|
||||
whose output is both streamed and grepped keeps the copy in a `mktemp` file: `tee /dev/stderr`
|
||||
reopens fd 2, and under `./ci.sh > log 2>&1` the two offsets punch NUL holes through each other's
|
||||
lines (4d).
|
||||
|
||||
The floors live in the `test` script, so `npm test` and the gate are one path: 100% of lines and
|
||||
functions, and a branch floor that only ever moves upward. It sits below 100 because the guards
|
||||
|
||||
@@ -3,16 +3,14 @@ set -euo pipefail
|
||||
cd "$(dirname "$0")"
|
||||
source ./docker-runner.sh
|
||||
|
||||
test_log=$(mktemp)
|
||||
leg "install ($node_image)" in_image "$node_image" npm ci
|
||||
leg "typecheck ($node_image)" in_image "$node_image" npm run typecheck
|
||||
|
||||
# Streamed, and copied so the zero-test guard reads the count without trading the output for it.
|
||||
test_log=$(mktemp)
|
||||
node_tests() {
|
||||
in_image "$node_image" npm test 2>&1 | tee "$test_log"
|
||||
}
|
||||
|
||||
leg "install ($node_image)" in_image "$node_image" npm ci
|
||||
leg "typecheck ($node_image)" in_image "$node_image" npm run typecheck
|
||||
|
||||
leg "tests ($node_image)" node_tests
|
||||
if grep -q 'ℹ tests 0' "$test_log"; then
|
||||
echo 'the gate ran zero tests — failing instead of a vacuous green'
|
||||
|
||||
+4
-3
@@ -1,3 +1,5 @@
|
||||
: "${EPOCHREALTIME:?the gate times its legs with EPOCHREALTIME — bash 5 or newer}"
|
||||
|
||||
bun_image=oven/bun:1.4.0-alpine
|
||||
deno_image=denoland/deno:2.9.6
|
||||
firefox_image=selenium/standalone-firefox:153.0.4
|
||||
@@ -10,8 +12,7 @@ in_image() {
|
||||
docker run --rm -u "$(id -u):$(id -g)" -e HOME=/tmp ${PROPERTY_RUNS+-e PROPERTY_RUNS} ${in_image_network:+--network "$in_image_network"} -v "$PWD:/app" -w /app --entrypoint "$entrypoint" "$image" "$@"
|
||||
}
|
||||
|
||||
# Markers go to stderr so a leg reporting a value stays capturable, and the locals carry the
|
||||
# function's own name because bash scopes them dynamically into whatever the leg runs.
|
||||
# Markers on stderr so a captured leg's value stays clean; leg_* because bash scopes local into the leg's own call.
|
||||
leg() {
|
||||
local leg_name=$1 leg_elapsed leg_started leg_status=0
|
||||
shift
|
||||
@@ -26,7 +27,7 @@ leg() {
|
||||
|
||||
with_firefox() {
|
||||
local container in_image_network status=0
|
||||
container=$(docker run -d --rm "$firefox_image")
|
||||
container=$(docker run -d --rm "$firefox_image") || return $?
|
||||
# The id is baked in: the trap fires after this function's locals are gone.
|
||||
trap "docker rm -f $container >/dev/null 2>&1" EXIT
|
||||
trap 'exit 130' INT
|
||||
|
||||
+12
-13
@@ -3,23 +3,22 @@ set -euo pipefail
|
||||
cd "$(dirname "$0")"
|
||||
source ./docker-runner.sh
|
||||
|
||||
read_field() {
|
||||
in_image "$node_image" npm pkg get "$1" | tr -d '"\r'
|
||||
}
|
||||
|
||||
read_package_fields() {
|
||||
private=$(read_field private)
|
||||
name=$(read_field name)
|
||||
version=$(read_field version)
|
||||
}
|
||||
|
||||
published_version() {
|
||||
in_image "$node_image" npm view "$1@$2" version 2>/dev/null || true
|
||||
}
|
||||
|
||||
push_tag() {
|
||||
git tag "v$version"
|
||||
git push origin "v$version"
|
||||
git tag "v$version" && git push origin "v$version"
|
||||
}
|
||||
|
||||
read_field() {
|
||||
in_image "$node_image" npm pkg get "$1" | tr -d '"\r'
|
||||
}
|
||||
|
||||
read_package_fields() {
|
||||
private=$(read_field private) &&
|
||||
name=$(read_field name) &&
|
||||
version=$(read_field version)
|
||||
}
|
||||
|
||||
leg "read package.json ($node_image)" read_package_fields
|
||||
@@ -28,7 +27,7 @@ if [ "$private" = 'true' ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
published=$(leg "ask npmjs for $name@$version" published_version "$name" "$version")
|
||||
published=$(leg "ask npmjs for $name@$version ($node_image)" published_version "$name" "$version")
|
||||
tagged=$(leg "ask origin for v$version" git ls-remote --tags origin "v$version")
|
||||
|
||||
# Both steps observe their own end state, so a partial run converges on the next push to main.
|
||||
|
||||
+21
-13
@@ -560,19 +560,27 @@ The done `todo.md` items in full, as they were written. `todo.md` keeps a one-li
|
||||
rather than promoting the gate's `dist`, and unmeasured until the log shows them. Per-leg
|
||||
timing is what turns "slow or hung" from a guess into a reading; the browser leg's own
|
||||
5.4–7.9s against a 17s warm gate is the number that made it obviously cheap.
|
||||
**Measured** (2026-09-19): ten legs, not the nine counted above, each naming its image, on a
|
||||
27.9 s warm gate — install 1.5 s, typecheck 1.1 s, Node tests 4.6 s, Deno 6.3 s, Bun 3.9 s,
|
||||
build 1.0 s, pack and install 1.6 s, consumer typecheck 1.0 s, engines floor 0.5 s, browser
|
||||
5.7 s. The browser leg lands in the 5.4–7.9 s the item quotes, and the markers cost nothing
|
||||
measurable: 28.9 s before against 27.9 s after. `publish.sh` reads its fields in 2.5 s and the
|
||||
registry in 1.4 s; its `npm ci` and rebuild are the gate's own 1.5 s and 1.0 s, so the seconds
|
||||
§9 accepts for rebuilding rather than promoting the gate's `dist` are about 2.5.
|
||||
Three things the writing turned up. The markers print to stderr, so a leg whose value is read —
|
||||
`publish.sh` asking npmjs — stays capturable. `leg`'s locals carry its own name because bash
|
||||
scopes them into whatever the leg runs: unprefixed, `name` was swallowed by the leg reading
|
||||
`package.json`. And `leg` returns its command's status the way `with_firefox` already did,
|
||||
because the bare call swallowed a non-zero one wherever `set -e` is suspended, which also gets
|
||||
the elapsed time printed for the leg that failed.
|
||||
**Measured** (2026-09-20): ten legs, not the nine counted above, each naming the image it runs
|
||||
in where it runs in one, on a 28.4 s warm gate — install 1.5 s, typecheck 1.2 s, Node tests
|
||||
4.9 s, Deno 6.0 s, Bun 4.5 s, build 1.0 s, pack and install 1.6 s, consumer typecheck 1.0 s,
|
||||
engines floor 0.4 s, browser 5.6 s. The browser leg lands in the 5.4–7.9 s the item quotes,
|
||||
and the markers cost nothing measurable: 28.9 s before against 28.4 s after. `publish.sh`
|
||||
reads its fields in 2.6 s and the registry in 1.4 s; its `npm ci` and rebuild are the gate's
|
||||
own 1.5 s and 1.0 s, so the seconds §9 accepts for rebuilding rather than promoting the gate's
|
||||
`dist` are about 2.5.
|
||||
Four things the writing turned up, three of them bash scoping a rule differently than it
|
||||
reads. The markers print to stderr, so a leg whose value is read — `publish.sh` asking npmjs —
|
||||
stays capturable. `leg`'s locals carry its own name because bash scopes them into whatever the
|
||||
leg runs: unprefixed, `name` was swallowed by the leg reading `package.json`. `leg` returns
|
||||
its command's status the way `with_firefox` already did, because the bare call dropped a
|
||||
non-zero one wherever `set -e` is suspended, which also gets the elapsed time printed for the
|
||||
leg that failed. And the `||` that captures that status suspends `set -e` for everything the
|
||||
leg calls, so a function a leg runs chains its statements with `&&` or every statement but the
|
||||
last runs unchecked: `read_package_fields` read on past a failed read, and `push_tag` pushed a
|
||||
tag the tag step had refused to write, both of which aborted before this chunk (the
|
||||
stability-reviewer, 2026-09-20). §10 carries the rule so the next leg cannot reintroduce it,
|
||||
and `EPOCHREALTIME` is guarded at `source` so an older bash names itself rather than dying as
|
||||
an unbound variable on the first leg.
|
||||
|
||||
- [x] **5a — Rename to `@larvit/adf-codec` (`0.1.0`).** Before the first publish, the name being
|
||||
the published identity: `package.json` `name` and `repository`, the Gitea repo and its
|
||||
|
||||
Reference in New Issue
Block a user