OXYGENHUB

ci: boot the R49 ROM on narvi-1 instead of the conformance suite 712ea2b4d346

The conformance job of the first run on narvi-1 (run #1) never reached
the emulator; every test was skipped with

    emulator control port never came up: <urlopen error [Errno 97]
    Address family not supported by protocol>

The syscall tier's seccomp filter refuses every socket family but
AF_UNIX, network "loopback" or not, so no job there can talk TCP to
127.0.0.1, and the suite is HTTP on a loopback port. The build job
passed (129 s).

In its place a boot job takes the built binary, boots the R49 ROM
headless and has BASIC run a pasted program that prints
SUM 333833500, read back through -echo: the 65C02 core, VERA's timing,
the KERNAL and BASIC working on riscv64, with no network. The image now
carries the ROM at /opt/x16rom, from the emulator's R49 release (the zip
and the ROM checked against their SHA-256; the ROM is the same file as
the repository's rom.bin), and is pinned as
cas://blob/127605eab9812e79d7c80d99e012105ca12dc7060bec8bffb81576660981c157.
Python and pytest stay in it for the suite, for when the tier allows it.
The suite passes 22/22 run by hand on narvi.
Author
Andy McCall @andymccall <[email protected]>
via
agent agent:claude@andymccall <[email protected]> · keyless, under your grant for changes,refs,seal · key ee5caabc7ed3
When
· 2026-10-21 03:06 UTC
Signature
◈ sealed by agent:claude@andymccall
Hash
712ea2b4d3463dfffafa6ec39721181afc825a9f5fcee0f9d6532678e3147ebe
Depends on
a673804c781c
Depended on by
10b68259f181
In bookmarks
main riscv-1
Edits
14 atoms, 2 tree operations, 4 files

Browse the tree at this change

added .flow/jobs/boot.sh +20 −0
1 +
# The x16emu the build job made on narvi-1 boots the R49 ROM (in the image
2 +
# at /opt/x16rom) headless, to BASIC, and runs a program pasted in, which
3 +
# prints a sum that only right arithmetic gives: the 65C02 core, VERA's
4 +
# timing, the KERNAL and BASIC all working on riscv64. -echo copies what
5 +
# BASIC prints to stdout.
6 +
set -eu
7 +
chmod +x x16emu
8 +
printf '10 S=0:FOR I=1 TO 1000:S=S+I*I:NEXT\n20 PRINT "SUM";S\n' >/tmp/sum.bas
9 +
SDL_VIDEODRIVER=dummy SDL_AUDIODRIVER=dummy \
10 +
	./x16emu -rom /opt/x16rom/rom.bin -bas /tmp/sum.bas -run -echo -warp >/tmp/echo.txt 2>&1 &
11 +
emu=$!
12 +
i=0
13 +
while ! grep -q "SUM 333833500" /tmp/echo.txt && [ $i -lt 120 ]; do
14 +
	sleep 1
15 +
	i=$((i + 1))
16 +
done
17 +
kill $emu 2>/dev/null || true
18 +
tr -cd '[:print:]\n' </tmp/echo.txt
19 +
grep -q "SUM 333833500" /tmp/echo.txt || { echo "boot: BASIC did not print SUM 333833500 within ${i} s"; exit 1; }
20 +
echo "boot: BASIC ran on $(uname -m) in ${i} s"
removed .flow/jobs/conformance.sh +0 −0
…
28 unchanged lines
modified .flow/image/build-riscv64.sh +11 −3
…
15 unchanged lines
16 16
release=24.04.5
17 17
base=ubuntu-base-$release-base-riscv64.tar.gz
18 18
sha256=651c516f291259ec913caa1efd0cfff738cd223adfc6617c884f07af487f08cf
19 -
# What the Makefile and the conformance suite need, as README.md lists for
20 -
# riscv64 Linux: C11 and C++17 compilers, make, SDL2 and zlib, Python and
21 -
# pytest.
19 +
# What the Makefile needs, as README.md lists for riscv64 Linux: C11 and
20 +
# C++17 compilers, make, SDL2 and zlib; and Python with pytest for the
21 +
# conformance suite, once the syscall tier lets a job use loopback TCP.
22 22
packages="build-essential make libsdl2-dev zlib1g-dev python3 python3-pytest"
23 23
24 24
test "$(uname -m)" = riscv64 || { echo "build-riscv64.sh: run this on a riscv64 machine" >&2; exit 1; }
…
20 unchanged lines
45 45
	apt-get clean
46 46
	rm -rf /var/lib/apt/lists/*"
47 47
sudo umount "$root/proc" "$root/dev"
48 +
# The R49 ROM, from the emulator's own R49 release, for the boot job: the
49 +
# zip and the ROM in it both checked against their SHA-256.
50 +
r49=https://github.com/X16Community/x16-emulator/releases/download/r49/x16emu_linux-x86_64-r49.zip
51 +
curl -fsSL -o "$work/r49.zip" "$r49"
52 +
echo "efb2d4ad36ae4244370004891aa67bdf85d2ec01b42b7f12f97b48b62aff009d  $work/r49.zip" | sha256sum -c - >/dev/null
53 +
python3 -c 'import sys, zipfile; open(sys.argv[2], "wb").write(zipfile.ZipFile(sys.argv[1]).read("rom.bin"))' "$work/r49.zip" "$work/rom.bin"
54 +
echo "b81654cc8c87ed96e3ffc7c8e7c312c9f3b7b870c7bb34de61e61eac931b819a  $work/rom.bin" | sha256sum -c - >/dev/null
55 +
sudo install -D -m 0644 "$work/rom.bin" "$root/opt/x16rom/rom.bin"
48 56
sudo rm "$root/etc/resolv.conf"
49 57
sudo mv "$work/resolv.conf.image" "$root/etc/resolv.conf" 2>/dev/null || true
50 58
sudo tar -C "$root" -cf "$out" .
…
2 unchanged lines
modified .flow/pipeline.kdl +18 −20
1 1
// x16emu's pipeline on OxygenFlow: every push to a bookmark on OxygenHub
2 -
// builds the emulator natively on narvi-1, a real riscv64 board, and runs
3 -
// the control-API conformance suite against what it built. The jobs appear
4 -
// as checks on the change and in the Flow tab. The retro-remote-debug-
5 -
// controller subrepo (src/extern) is put in the workspace by the runner.
2 +
// builds the emulator natively on narvi-1, a real riscv64 board, and boots
3 +
// what it built to BASIC. The jobs appear as checks on the change and in the
4 +
// Flow tab. The retro-remote-debug-controller subrepo (src/extern) is put in
5 +
// the workspace by the runner.
6 +
//
7 +
// The control-API conformance suite (.github/workflows/conformance.yml on
8 +
// x86) is not run here: it talks to the emulator over TCP on 127.0.0.1, and
9 +
// the syscall tier refuses every socket family but AF_UNIX, even with
10 +
// network "loopback". It passes 22/22 run by hand on narvi.
6 11
schema-version 1
7 12
8 -
// Ubuntu 24.04 riscv64 with gcc, make, SDL2, zlib, Python and pytest
13 +
// Ubuntu 24.04 riscv64 with gcc, make, SDL2, zlib and the R49 ROM
9 14
// (.flow/image/build-riscv64.sh), in the syscall tier: narvi's Ky X1 has no
10 15
// hardware virtualisation, so no microVM. `runs-on` keeps it on a RISC-V
11 -
// runner. Loopback only: the suite talks to the emulator on 127.0.0.1.
16 +
// runner.
12 17
sandbox "x16-riscv64" {
13 18
    runtime "syscall"
14 -
    rootfs "cas://blob/a1405289def4850dade956ea6d4e4870a73bb631cc353f16f0e5fcd796fe2541"
19 +
    rootfs "cas://blob/127605eab9812e79d7c80d99e012105ca12dc7060bec8bffb81576660981c157"
15 20
    runs-on "riscv64"
16 21
    memory-limit "2GiB"
17 22
    cpu-limit 6
18 -
    network "loopback"
23 +
    network "isolated"
19 24
}
20 25
21 26
// make as README.md gives it, -Werror included; the binary is kept with the
22 -
// runner's signed receipt and handed to the conformance job.
27 +
// runner's signed receipt and handed to the boot job.
23 28
job "build" {
24 29
    sandbox "x16-riscv64"
25 30
    timeout "30m"
…
5 unchanged lines
31 36
    }
32 37
}
33 38
34 -
// The conformance suite (src/extern/retro-remote-debug-controller) against
35 -
// that binary, headless; pytest's JUnit XML for the hub's Tests tab.
36 -
job "conformance" {
39 +
// That binary boots the R49 ROM headless and runs a BASIC program.
40 +
job "boot" {
37 41
    sandbox "x16-riscv64"
38 42
    depends-on "build"
39 -
    timeout "15m"
43 +
    timeout "10m"
40 44
    input {
41 45
        artifact "x16emu-linux-riscv64" from "build"
42 46
        path "/src/x16emu"
43 47
    }
44 -
    exec "sh" "-eu" ".flow/jobs/conformance.sh"
45 -
    output {
46 -
        artifact "junit" {
47 -
            path "/src/junit.xml"
48 -
            when "always"
49 -
        }
50 -
    }
48 +
    exec "sh" "-eu" ".flow/jobs/boot.sh"
51 49
}