From c28cc0191bab2e40bc841419a419bfc24d509f9c Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 12 Nov 2026 15:17:08 +0000 Subject: [PATCH] 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 Claude-Session: https://claude.ai/code/session_017RzZgrf7V7Zk2iczBXDRbm --- scripts/nightly.sh | 39 ++++++++++++------- scripts/test_nightly_dict_hash.sh | 62 +++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 13 deletions(-) create mode 100644 scripts/test_nightly_dict_hash.sh diff --git a/scripts/nightly.sh b/scripts/nightly.sh index 64719261..17b9073f 100755 --- a/scripts/nightly.sh +++ b/scripts/nightly.sh @@ -13,8 +13,10 @@ # time, in that order: scripts/qemu_session.py zuse -- [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 | test-32 | proofs | images" >&2 echo " | publish | docs | publish-docs | gitea-release " >&2 - echo " | protocol [...] | protocol-report" >&2 + echo " | protocol [...] | 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) diff --git a/scripts/test_nightly_dict_hash.sh b/scripts/test_nightly_dict_hash.sh new file mode 100644 index 00000000..9af60ebb --- /dev/null +++ b/scripts/test_nightly_dict_hash.sh @@ -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-.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 : what accept 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 "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-.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 ]