ci: the dict_hash check counts three hashes, not one distinct value (#172)

The stick-high stage passed when one distinct dict_hash appeared: an
architecture whose boot left no accept file, no LOG: line, no serial log or no
PARITY:V4_SYSTEM line gave nothing, and two that agreed passed for three.

It is now a stage of its own, scripts/nightly.sh dict-hash, which stick-high
runs: one hash from each of amd64, aarch64 and riscv64, all found and one value;
otherwise refused, naming the architectures with none or the values that
differ. NIGHTLY_OUT points the stages at another directory, for the tests.

scripts/test_nightly_dict_hash.sh, written first and seen to fail (6 of 6, the
stage not existing), then 7 cases: agree -- pass; differ, no PARITY line, no
LOG: line, not booted, no serial log, none at all -- refused. Two of them
caught lookups that ended the script silently under set -e; guarded.

Captain Bob, 2026-11-12: "172 then finish alpha.5".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RzZgrf7V7Zk2iczBXDRbm
This commit is contained in:
Claude
2026-11-12 15:17:08 +00:00
parent ff797d80cb
commit c28cc0191b
2 changed files with 88 additions and 13 deletions
+26 -13
View File
@@ -13,8 +13,10 @@
# time, in that order: scripts/qemu_session.py zuse <arch> -- [zuse@Hera] ok>, `3 4 + .`
# -> 7 ok, POST passed -- or stick_zuse amd64 --target qemu-stick-high, the PC stick loaded
# above 2 GiB. amd64 keeps the ISO it booted, byte for byte, as build/nightly/StarshipOS.iso.
# The last of the four (stick-high) also checks PARITY:V4_SYSTEM's dict_hash is the same
# on amd64, aarch64 and riscv64.
# The last of the four (stick-high) also runs dict-hash.
# scripts/nightly.sh dict-hash
# PARITY:V4_SYSTEM's dict_hash from each of the amd64, aarch64 and riscv64 boots: all three
# found, and one value (#172 -- it was one distinct value, which two of the three could make).
# scripts/nightly.sh test-32
# make -C v4 test-32: v4 with 32-bit cells, every test (the acceptance's last part).
# scripts/nightly.sh proofs
@@ -69,14 +71,14 @@
set -euo pipefail
cd "$(dirname "$0")/.."
OUT=build/nightly
OUT=${NIGHTLY_OUT:-build/nightly} # NIGHTLY_OUT: the tests' scratch (scripts/test_nightly_dict_hash.sh)
DOCS=build/nightly-docs
LITHOS=$(sed -n 's/^LITHOS_VERSION *?= *//p' kernel/Makefile)
VERSION=${NIGHTLY_VERSION:-$LITHOS-SNAPSHOT}
usage() {
echo "Usage: scripts/nightly.sh accept <amd64|aarch64|riscv64|stick-high> | test-32 | proofs | images" >&2
echo " | publish <URL> | docs | publish-docs <URL> | gitea-release <tag>" >&2
echo " | protocol [<stage>...] | protocol-report" >&2
echo " | protocol [<stage>...] | protocol-report | dict-hash" >&2
echo " protocol stages: $PROTOCOL_STAGES" >&2
exit 2
}
@@ -132,15 +134,26 @@ accept)
python3 scripts/qemu_session.py $mode $arch $extra --label "nightly-$what" --make "QEMU_DISPLAY=none" \
| tee "$OUT/accept-$what.txt"
[ "$what" != amd64 ] || cp build/amd64/kernel/starkernel.iso "$OUT/StarshipOS.iso"
if [ "$what" = stick-high ]; then
# the three architectures' boots of this run: one dict_hash
hashes=$(for a in amd64 aarch64 riscv64; do
log=$(sed -n 's/^LOG: //p' "$OUT/accept-$a.txt")
grep -aoE "PARITY:V4_SYSTEM [^[:cntrl:]]*" "$log" | sed -n 's/.*dict_hash=//p'
done | sort -u)
echo "=== nightly: dict_hash on the three: $hashes"
[ "$(echo "$hashes" | wc -l)" = 1 ] && [ -n "$hashes" ] \
|| { echo "nightly: the three architectures do not agree on dict_hash" >&2; exit 1; }
[ "$what" != stick-high ] || "$(pwd)/scripts/nightly.sh" dict-hash
;;
dict-hash)
# One dict_hash from each of the three architectures' boots, and the same (#172): an
# architecture with no accept file, no LOG: line, no serial log or no PARITY:V4_SYSTEM line
# gives no hash, and the check then fails -- two that agree are not three.
found=(); missing=()
for a in amd64 aarch64 riscv64; do
# each lookup may find nothing: under set -e that must be an answer, not the end of the script
log=$(sed -n 's/^LOG: //p' "$OUT/accept-$a.txt" 2>/dev/null | head -1 || true)
h=$( { [ -n "$log" ] && [ -f "$log" ] && grep -aoE "PARITY:V4_SYSTEM [^[:cntrl:]]*" "$log" | sed -n 's/.*dict_hash=//p' | head -1; } || true)
if [ -n "$h" ]; then found+=("$a=$h"); else missing+=("$a"); fi
done
distinct=$(printf '%s\n' "${found[@]#*=}" | grep . | sort -u || true)
echo "=== nightly: dict_hash on the three: ${found[*]:-none}"
if [ ${#missing[@]} -gt 0 ]; then
echo "nightly: the three architectures do not agree on dict_hash -- none from: ${missing[*]}" >&2; exit 1
fi
if [ "$(grep -c . <<<"$distinct")" != 1 ]; then
echo "nightly: the three architectures do not agree on dict_hash -- $(tr '\n' ' ' <<<"$distinct")" >&2; exit 1
fi
;;
test-32)
+62
View File
@@ -0,0 +1,62 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LicenseRef-Starship-1.0
# Copyright (c) 2023-2026 Robert A. James
# test_nightly_dict_hash.sh -- the tests of `scripts/nightly.sh dict-hash` (#172): the three
# architectures' boots agree on one dict_hash only when all three report one, and it is the same.
# No QEMU: each case is a scratch NIGHTLY_OUT with accept-<arch>.txt files and serial logs, as the
# accept stages leave them.
#
# Prints PASS:/FAIL: per check and RESULT:; exits 0 only if every check passes.
set -uo pipefail
NIGHTLY="$(cd "$(dirname "$0")" && pwd)/nightly.sh"
T=$(mktemp -d)
trap 'rm -rf "$T"' EXIT
ok=1
boot() { # boot <arch> <hash|none|nolog|nofile>: what accept <arch> leaves
local a=$1 h=$2 log="$T/logs/$1.log"
mkdir -p "$T/out" "$T/logs"
case $h in
nofile) rm -f "$T/out/accept-$a.txt"; return ;;
nolog) printf 'RESULT: PASS\n' > "$T/out/accept-$a.txt"; return ;;
esac
printf 'LOG: %s\nRESULT: PASS\n' "$log" > "$T/out/accept-$a.txt"
if [ "$h" = none ]; then printf 'POST: PASSED\n' > "$log"
else printf 'PARITY:V4_SYSTEM word_count=318 dict_hash=%s\nPOST: PASSED\n' "$h" > "$log"; fi
}
expect() { # expect <pass|refuse> "what"
local want=$1 what=$2 out rc
out=$(NIGHTLY_OUT="$T/out" bash "$NIGHTLY" dict-hash 2>&1); rc=$?
if { [ "$want" = pass ] && [ $rc -eq 0 ]; } ||
{ [ "$want" = refuse ] && [ $rc -eq 1 ] && grep -q "do not agree" <<<"$out"; }; then
echo "PASS: $what"
else
echo "FAIL: $what (exit $rc)"; printf ' %s\n' "$out" | head -4; ok=0
fi
}
H=0x8c92508f748f99cf
boot amd64 $H; boot aarch64 $H; boot riscv64 $H
expect pass "three boots, one dict_hash: pass"
boot riscv64 0x0000000000000001
expect refuse "two hashes differ: refused"
boot riscv64 none
expect refuse "one log has no PARITY:V4_SYSTEM line (two agree): refused"
boot riscv64 nolog
expect refuse "one accept-<arch>.txt names no LOG: (two agree): refused"
boot riscv64 nofile
expect refuse "one architecture was not booted (two agree): refused"
boot riscv64 $H; rm -f "$T/logs/riscv64.log"
expect refuse "one serial log is missing (two agree): refused"
rm -f "$T/out/accept-"*.txt
expect refuse "no architecture booted at all: refused, and says so"
echo "RESULT: $([ $ok = 1 ] && echo PASS || echo FAIL)"
[ $ok = 1 ]