diff --git a/ci.sh b/ci.sh index 27ae3af..d4609bd 100755 --- a/ci.sh +++ b/ci.sh @@ -62,6 +62,13 @@ interrupt_stops_container() { leg "Ctrl-C stops an in_image container ($node_image)" interrupt_stops_container +# memory.swap.max is 0 when --memory-swap equals --memory. +container_is_limited() { + [ "$(in_image "$node_image" sh -c 'cat /sys/fs/cgroup/memory.max /sys/fs/cgroup/memory.swap.max' | tr '\n' ' ')" = '8589934592 0 ' ] +} + +leg "an in_image container has equal memory and swap limits ($node_image)" container_is_limited + test_log=$(mktemp) node_tests() { in_image "$node_image" npm test 2>&1 | tee "$test_log" diff --git a/docker-runner.sh b/docker-runner.sh index 98c0aa7..8b1b593 100644 --- a/docker-runner.sh +++ b/docker-runner.sh @@ -6,11 +6,14 @@ firefox_image=selenium/standalone-firefox:159.0.1 floor_image=node:18.20.8-alpine3.21 node_image=node:26.13.0-alpine3.24 +# The test legs peak near 4.5g in the container's memory.peak, and a leg building strings near V8's longest fails when the limit is too small. +container_memory=8g + # --init: the container's process 1 ignores a SIGINT it has no handler for, so without it Ctrl-C leaves the container running. in_image() { local image=$1 entrypoint=$2 shift 2 - docker run --rm --init -u "$(id -u):$(id -g)" -e HOME=/tmp ${PROPERTY_RUNS+-e PROPERTY_RUNS} ${in_image_memory:+--memory "$in_image_memory" --memory-swap "$in_image_memory"} ${in_image_network:+--network "$in_image_network"} -v "$PWD:/app" -w /app --entrypoint "$entrypoint" "$image" "$@" + docker run --rm --init -u "$(id -u):$(id -g)" -e HOME=/tmp ${PROPERTY_RUNS+-e PROPERTY_RUNS} --memory "${in_image_memory:-$container_memory}" --memory-swap "${in_image_memory:-$container_memory}" ${in_image_network:+--network "$in_image_network"} -v "$PWD:/app" -w /app --entrypoint "$entrypoint" "$image" "$@" } # Markers on stderr so a captured leg's value stays clean; leg_* because bash scopes local into the leg's own call. @@ -28,7 +31,7 @@ leg() { with_firefox() { local container in_image_network status=0 - container=$(docker run -d --rm "$firefox_image") || return $? + container=$(docker run -d --rm --memory "$container_memory" --memory-swap "$container_memory" "$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 diff --git a/docs/reading-cost.md b/docs/reading-cost.md index 7558e2d..019993f 100644 --- a/docs/reading-cost.md +++ b/docs/reading-cost.md @@ -919,3 +919,20 @@ Read most (opens and searches): - `src/markdown/parse/markdown-to-adf.test.ts` 2 - `src/result.test.ts` 2 - `README.md` 1 + +## Reading cost, chunk 38c1baab4d5b, finished 2026-11-09T07:42:47Z + +87030 new input tokens before the first commit. + +Read most (opens and searches): + +- `README.md` 7 +- `src/markdown/portable/adf-to-portable-markdown.test.ts` 6 +- `todo.md` 5 +- `src/markdown/portable/task-list.ts` 4 +- `src/result.test.ts` 3 +- `docker-runner.sh` 2 +- `src/conformance/flavour.test.ts` 2 +- `src/markdown/portable/adf-to-portable-markdown.ts` 2 +- `src/result.ts` 2 +- `AGENTS.md` 1 diff --git a/publish.sh b/publish.sh index ab70462..dff5bf2 100755 --- a/publish.sh +++ b/publish.sh @@ -38,7 +38,7 @@ if [ -z "$published" ]; then # npmjs shows the README's tagline, which package.json's description is overwritten with here. in_image_memory=1g leg "copy the README tagline to the description ($node_image)" in_image "$node_image" node release-tooling/write-description.mjs leg "publish $name@$version ($node_image)" \ - docker run --rm -u "$(id -u):$(id -g)" -e HOME=/tmp -e NPM_TOKEN -v "$PWD:/app" -w /app --entrypoint sh "$node_image" -c \ + docker run --rm --memory "$container_memory" --memory-swap "$container_memory" -u "$(id -u):$(id -g)" -e HOME=/tmp -e NPM_TOKEN -v "$PWD:/app" -w /app --entrypoint sh "$node_image" -c \ 'printf "//registry.npmjs.org/:_authToken=%s\n" "$NPM_TOKEN" > "$HOME/.npmrc" && npm publish --access public' fi diff --git a/todo.md b/todo.md index 5606a2b..3977c25 100644 --- a/todo.md +++ b/todo.md @@ -13,7 +13,6 @@ | ID | Release | Blocked by | Exempt | Item | L | E | I | R | S | Moves | Score | |---|---|---|---|---|---|---|---|---|---|---|---| | 153 | 0.4.0 | | question | **Decide whether `src/markdown/` root may import `emit/`, `parse/` or `portable/`, and hold the answer in `.oxlintrc.json` and §The source parts by ADF and format.** | 1 | 2 | 2 | 3 | 2 | code health by 3 | 26.2 | -| 144 | 0.4.0 | | | **Give every container `docker-runner.sh` and `publish.sh` start a memory limit and an equal swap limit, sized from the largest a gate leg measurably needs.** | 1 | 1 | 1 | 1 | 2 | code health by 1 | 8.7 | | 143 | 0.4.0 | | defect | **Keep, in `adfToPortableMarkdown`, the link around a note, so a linked stored image writes every target.** | 2 | 1 | 3 | 4 | 3 | 7 by 1 | 6.5 | | 169 | 0.4.0 | | owner | **Investigate `kovetskiy/mark` in depth and record in a report how its formats differ from ours, where the two projects overlap and how they compare.** | 1 | 1 | 1 | 1 | 3 | 3 by 4 | 6.3 | | 112 | 0.4.0 | | | **Pad a GFM pipe table's short rows with empty cells, read its body rows holding no pipe, and read a one-column table without leading pipes in `portableMarkdownToAdf`.** | 2 | 1 | 2 | 3 | 3 | 4 by 4 | 6.2 | @@ -65,13 +64,6 @@ a paragraph opening with a spellable `!adf:link` (§Nothing recurses unbounded). `src/markdown/**` bans only `html/`; banning `emit/`, `parse/` and `portable/` for the root's own files, with its `ci.sh` probe, extends the decision. -### 144. Give every container `docker-runner.sh` and `publish.sh` start a memory limit and an equal swap limit, sized from the largest a gate leg measurably needs. - -`in_image`, `with_firefox` and `publish.sh` run `docker run` with neither `--memory` nor -`--memory-swap`, so a runaway leg can starve the machine instead of failing alone. Some tests build -strings near V8's longest, about 512 MB each, so measure each leg's peak before picking the limit: -one too small fails the gate. - ### 143. Keep, in `adfToPortableMarkdown`, the link around a note, so a linked stored image writes every target. `noteLeaves` (`src/markdown/portable/inline-reduction.ts`) drops the node's marks, so a stored