open-standards/joule-code/joule-code-rs/crates/joule-moq-room/deploy/check-image.sh
Transaction Science 0833173ed6
Some checks failed
wai-app and Create / wai-app · does this change touch it (push) Successful in 17s
joule-moq-web browser client / joule-moq-web · does this change touch it (push) Successful in 16s
joule-moq-room room service / joule-moq-room · does this change touch it (push) Successful in 36s
joule-moq-remote host agent / joule-moq-remote · does this change touch it (push) Successful in 39s
wai-audit collector and player integration / does this change touch it (push) Successful in 21s
WAI operation catalogue / does this change touch it (push) Successful in 21s
wai-ledger service / does this change touch it (push) Successful in 19s
wai-fleet service / does this change touch it (push) Successful in 20s
wai-quantum studio and desktop / does this change touch it (push) Successful in 21s
wai-quantum for JavaScript / does this change touch it (push) Successful in 22s
wai-sink-swift native sink package / does this change touch it (push) Successful in 21s
wai-sink-android native sink library / does this change touch it (push) Successful in 20s
WAI Studio / does this change touch it (push) Successful in 24s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (push) Has been cancelled
WAI operation catalogue / the catalogue and every door · linux (push) Has been cancelled
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (push) Has been cancelled
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (push) Has been cancelled
joule-code conformance gate / joule-moq-relay, its own energy on a counter (macos) (push) Successful in 43s
joule-moq-remote host agent / joule-moq-remote, every target (macos) (push) Successful in 1m11s
wai-fleet service / service · macos (push) Successful in 1m2s
wai-audit collector and player integration / collector, player integration and the browser end to end · macos (push) Successful in 1m28s
wai-ledger service / service · macos (push) Successful in 1m34s
wai-app and Create / wai-app · Create, browser end-to-end (macos) (push) Successful in 6m8s
wai-quantum studio and desktop / desktop app · macos (push) Successful in 4m12s
WAI Studio / Studio in Chromium and Firefox · macos (push) Successful in 9m11s
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (push) Successful in 11m10s
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (push) Successful in 15m15s
wai release binaries build on Windows / the seven release binaries · windows (push) Successful in 16m54s
wai-quantum studio and desktop / desktop app · windows (push) Successful in 10m27s
WAI breaking changes and the conformance runner / WAI breaking · does this change touch WAI (push) Successful in 11s
wai-app and Create / wai-app · tests, clippy, the line language's verifier (linux) (push) Successful in 49s
wai-audit collector and player integration / collector · linux (push) Successful in 1m33s
WAI Studio / the catalogue's actions, the unit tests and the core · linux (push) Successful in 57s
wai-ledger service / service · linux (push) Successful in 1m53s
wai-quantum for JavaScript / javascript · linux (push) Has been cancelled
WAI Studio / Studio in WebKit · linux (push) Has been cancelled
wai-quantum studio and desktop / desktop app · linux (push) Has been cancelled
wai-quantum studio and desktop / studio · browser (push) Has been cancelled
wai-fleet service / service · linux (push) Successful in 4m1s
joule-code conformance gate / Build + round-trip C1-C4 vectors (push) Successful in 5m4s
WAI breaking changes and the conformance runner / WAI breaking · wire, record and API against main · linux (push) Successful in 2m46s
WAI breaking changes and the conformance runner / WAI breaking and conform · their tests, and the independent verifiers through wai conform · linux (push) Successful in 7m20s
joule-moq-remote host agent / joule-moq-remote, every target (linux-arm64) (push) Has been cancelled
joule-moq-room room service / joule-moq-room, every target (push) Has been cancelled
joule-code conformance gate / joule-moq-relay, every target (push) Has been cancelled
joule-code conformance gate / joule-moq-ingest, every target (push) Has been cancelled
wai release: libwai for C, the Swift package by URL and checksum, the Android library published, and the relay's and room service's images
One release, four kinds of artefact, from wai-release.yml on a wai-v* tag:

- C: each Linux job attaches libwai-<version>-<triple>.tar.gz for x86_64- and
  aarch64-unknown-linux-gnu (wai/quickstart/build-libwai.sh): lib/libwai.a, the feature
  native_sink, built for the glibc target without crt-static; the committed include/wai.h;
  lib/pkgconfig/wai.pc, relocatable, its Libs naming rustc's native-static-libs. Static, so
  a program carries it and looks for nothing at run time. Before the attach: the header the
  build regenerates is the committed one, the library defines exactly the envelope's and the
  native sink's functions, and a C program compiles, links and runs against the archive
  through pkg-config (wai/quickstart/test/c-library.sh).
- Swift: Package.swift names WaiFFI by URL and checksum once a release has written them, and
  build/WaiFFI.xcframework while it is built there. The swift job attaches the zipped
  framework and wai-sink-swift-<version>.zip, the package naming it by download URL and
  SwiftPM checksum; scripts/check-release-package.sh resolves, builds and runs an
  application against it (before the attach through a mirror of the URL, after from the URL)
  and requires a wrong checksum to be refused. wai-sink-swift.yml runs the first on every
  change.
- Kotlin: maven-publish as science.transaction.wai:wai-sink:<version>, manifest package
  science.transaction.wai.sink (the Kotlin classes keep wai.sink). The android job checks
  the publication (scripts/check-publication.sh), attaches the AAR and publishes it to the
  forge's Maven registry; wai-sink-android.yml writes and checks it on every change.
- OCI: Containerfiles and check-image.sh for the relay and the room service (deploy/ beside
  each crate). The images job, on a runner labelled oci, builds and checks both and pushes
  them to the forge's container registry (wai/quickstart/release-images.sh, oci-push.py);
  an image that fails its check is not pushed.

The forge's job token reads the organisation's packages and cannot write them, so the Maven
publication and the images read the repository secret WAI_PACKAGES_TOKEN (scope
write:package). Every upload goes to the forge over the private network. The release body
names the new files.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-09 18:25:22 -05:00

107 lines
5.6 KiB
Shell
Executable file

#!/usr/bin/env bash
# check-image.sh: build the room service's image, run it beside the relay's, and hold it to its
# checks; then remove the containers, the network, the volume (and the images built, unless
# KEEP=1). Nothing is pushed anywhere.
#
# bash joule-code/joule-code-rs/crates/joule-moq-room/deploy/check-image.sh (from the repository's root)
#
# The checks: the image runs as the user wai (uid 10001), not as root, with wai-room as its entry
# point; `wai-room --version --json` runs in it and names the toolchain of this checkout
# (wai/wai-rs/Cargo.toml); `keygen` makes the service's keys in its volume and prints the lines
# that make a relay trust them; and with those lines in a relay's configuration, the two images
# on one network, each run with a read-only root, every capability dropped and no privilege to
# gain, the service answers /health from a process of uid 10001, and its clock's session to the
# relay is up (`"relay": true`).
#
# ENGINE picks the container engine (docker, or podman), JOBS bounds the builds' parallel jobs,
# PORT is the host port the service is published on. TAG names the image built (default
# wai-room-check:<pid>); IMAGE checks that image instead of building one; RELAY_IMAGE is the
# relay's image to run beside it (default: built from the relay's Containerfile); KEEP=1 keeps the
# images built. VERSION is the toolchain version the image must state (default this checkout's).
set -euo pipefail
ENGINE=${ENGINE:-docker}
JOBS=${JOBS:-8}
PORT=${PORT:-18745}
here=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
root=$(cd "$here/../../../../.." && pwd)
relay_dir="$root/joule-code/joule-code-rs/crates/joule-moq-relay/deploy"
VERSION=${VERSION:-$(sed -n 's/^version = "\(.*\)"$/\1/p' "$root/wai/wai-rs/Cargo.toml" | head -1)}
id="wai-room-check-$$"
work=$(mktemp -d)
built=()
cleanup() {
"$ENGINE" rm -f "$id-room" "$id-relay" >/dev/null 2>&1 || true
"$ENGINE" network rm "$id" >/dev/null 2>&1 || true
"$ENGINE" volume rm -f "$id" >/dev/null 2>&1 || true
if [ "${KEEP:-0}" != 1 ]; then
for b in ${built[@]+"${built[@]}"}; do "$ENGINE" image rm -f "$b" >/dev/null 2>&1 || true; done
fi
rm -rf "$work"
}
trap cleanup EXIT
die() { echo "check-image: $*" >&2; exit 1; }
build() { # <tag> <deploy dir>
local ignore=()
[ "$(basename "$ENGINE")" = podman ] && ignore=(--ignorefile "$2/Containerfile.dockerignore")
"$ENGINE" build ${ignore[@]+"${ignore[@]}"} --build-arg JOBS="$JOBS" -f "$2/Containerfile" -t "$1" "$root"
built+=("$1")
}
if [ -n "${IMAGE:-}" ]; then
tag="$IMAGE"
else
tag="${TAG:-wai-room-check:$$}"
build "$tag" "$here"
fi
if [ -n "${RELAY_IMAGE:-}" ]; then
relay="$RELAY_IMAGE"
else
relay="wai-relay-for-room-check:$$"
build "$relay" "$relay_dir"
fi
user=$("$ENGINE" image inspect --format '{{.Config.User}}' "$tag")
[ "$user" = wai ] || die "the image runs as '${user:-root}', not as wai"
entry=$("$ENGINE" image inspect --format '{{json .Config.Entrypoint}}' "$tag")
[ "$entry" = '["/usr/local/bin/wai-room"]' ] || die "the image's entry point is $entry, not wai-room"
uid=$("$ENGINE" run --rm --entrypoint /usr/bin/id "$tag" -u) || die "id does not run in the image"
[ "$uid" = 10001 ] || die "the image's user has uid $uid, not 10001"
out=$("$ENGINE" run --rm "$tag" --version --json) || die "wai-room --version does not run in the image"
python3 -I -c '
import json, sys
d = json.loads(sys.argv[1])["data"]
assert d["program"] == "wai-room" and d["toolchain"] == sys.argv[2], d
print("image: wai-room %s, of the WAI toolchain %s" % (d["version"], d["toolchain"]))
' "$out" "$VERSION" || die "wai-room --version --json says $out, not the toolchain $VERSION"
"$ENGINE" volume create "$id" >/dev/null
"$ENGINE" run --rm -v "$id:/var/lib/wai-room" "$tag" keygen /var/lib/wai-room/keys.toml > "$work/lines.toml" \
|| die "keygen does not make the service's keys in its volume"
grep -q '^\[\[auth.grants\]\]' "$work/lines.toml" || die "keygen printed no relay configuration lines"
# The relay's configuration in its image, with the lines that trust the service's keys.
cat "$relay_dir/relay.toml" "$work/lines.toml" > "$work/relay.toml"
chmod 644 "$work/relay.toml"
"$ENGINE" network create "$id" >/dev/null
"$ENGINE" run -d --name "$id-relay" --network "$id" --network-alias relay \
-v "$work/relay.toml:/etc/wai-relay/relay.toml:ro" \
--read-only --tmpfs /tmp --cap-drop ALL --security-opt no-new-privileges "$relay" >/dev/null
"$ENGINE" run -d --name "$id-room" --network "$id" -p "127.0.0.1:$PORT:9682" -v "$id:/var/lib/wai-room" \
--read-only --tmpfs /tmp --cap-drop ALL --security-opt no-new-privileges "$tag" >/dev/null
health=""
for _ in $(seq 1 150); do
health=$(curl -fsS "http://127.0.0.1:$PORT/health" 2>/dev/null || true)
python3 -I -c 'import json,sys; d=json.loads(sys.argv[1]); sys.exit(0 if d.get("ok") is True and d.get("relay") is True else 1)' "$health" 2>/dev/null && break
sleep 0.2
done
python3 -I -c '
import json, sys
d = json.loads(sys.argv[1] or "{}")
assert d.get("ok") is True and d.get("relay") is True, d
print("image: /health ok, the clock connected to the relay, room %s" % d["room"])
' "$health" || {
"$ENGINE" logs "$id-room" 2>&1 | tail -20 >&2
"$ENGINE" logs "$id-relay" 2>&1 | tail -20 >&2
die "the service did not answer /health with its clock connected to the relay (last: ${health:-nothing})"
}
status=$("$ENGINE" exec "$id-room" cat /proc/1/status) || die "the service's process cannot be read"
grep -qE '^Uid:[[:space:]]+10001[[:space:]]' <<<"$status" || die "the service runs as $(grep '^Uid:' <<<"$status"), not uid 10001"
echo "image: built, ran read-only with no capabilities as uid 10001 beside the relay's, and held to its checks"