<feed xmlns='http://www.w3.org/2005/Atom'>
<title>archangel/scripts, branch main</title>
<subtitle>Arch Linux installer ISO — ZFS-on-root or BTRFS, doubles as rescue disk
</subtitle>
<id>https://git.cjennings.net/archangel/atom?h=main</id>
<link rel='self' href='https://git.cjennings.net/archangel/atom?h=main'/>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archangel/'/>
<updated>2026-10-24T10:52:42+00:00</updated>
<entry>
<title>fix(test): verify installations from the live ISO</title>
<updated>2026-10-24T10:52:42+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-10-24T10:52:42+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archangel/commit/?id=02066b7106eef6f2ab3ed12de4ad2fbf3a83d669'/>
<id>urn:sha1:02066b7106eef6f2ab3ed12de4ad2fbf3a83d669</id>
<content type='text'>
I read the exact completion banner for every config and check exported
ZFS pools with zpool import. Failed pre-reboot checks stop the scenario
before it can count as passed.

I moved checks that need the running target into the post-reboot path.
</content>
</entry>
<entry>
<title>fix(test): distinguish stale archzfs cache failures</title>
<updated>2026-10-24T10:52:38+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-10-24T10:52:38+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archangel/commit/?id=1ea3434be0bf6f66f1a3828ef0e4a6e0a6b8a761'/>
<id>urn:sha1:1ea3434be0bf6f66f1a3828ef0e4a6e0a6b8a761</id>
<content type='text'>
I only blame the archzfs cache when the failure names a supported ZFS
package. Other checksum and signature failures now point at pacoloco.
Stale cache errors no longer enter the transient retry loop.
</content>
</entry>
<entry>
<title>fix(test): keep each failed install attempt's log</title>
<updated>2026-09-13T15:59:31+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-09-13T15:59:31+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archangel/commit/?id=0adcfaa5c7e479e9c433b92836a5548a19168e05'/>
<id>urn:sha1:0adcfaa5c7e479e9c433b92836a5548a19168e05</id>
<content type='text'>
The retry loop refetched the guest's newest install log after every failed attempt, so `test-logs/` only ever held the last one. When a retry followed a pacstrap stall, the saved log showed the retry's "Disk in use" and the stall behind it was gone. Each failed attempt's log now lands in `test-logs/&lt;config&gt;-install-attempt&lt;N&gt;.log`.
</content>
</entry>
<entry>
<title>fix(test): clear the cached archzfs repo before the first scenario</title>
<updated>2026-08-07T18:41:10+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-08-07T18:41:10+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archangel/commit/?id=5b48bef697a6c98de9b1336ad5a36ad7341a639d'/>
<id>urn:sha1:5b48bef697a6c98de9b1336ad5a36ad7341a639d</id>
<content type='text'>
archzfs re-uploads its GitHub Releases assets under the same filenames, so pacoloco can hold package bodies that no longer match what its archzfs.db advertises. Every ZFS scenario then dies at pacstrap with "invalid or corrupted package", which reads exactly like an installer regression. It went stale twice on 2026-08-06, the second time mid-run: fresh at scenario one, rotten by scenario six, and the ZFS half of the suite was lost.

The whole directory goes, not the zfs-dkms and zfs-utils globs build.sh uses. Removing the bodies while leaving a stale db trades a checksum error for "Maximum file size exceeded", which is how the second failure passed itself off as a new bug.

Removal needs root, so it degrades to a warning rather than failing the run. The path is injectable because the first version hardcoded it, and running the unit suite then invoked sudo rm -rf against the live cache. A shape check guards the override, since this is rm -rf under sudo and a mistyped path should cost a warning instead of the machine.
</content>
</entry>
<entry>
<title>fix(test): reboot the guest after a live-root rollback</title>
<updated>2026-08-07T07:40:29+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-08-07T07:40:29+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archangel/commit/?id=14c08b4706a486ec080de606ccf1a7c2acd672b6'/>
<id>urn:sha1:14c08b4706a486ec080de606ccf1a7c2acd672b6</id>
<content type='text'>
verify_zfssnapshot_wrapper rolled back zroot/ROOT/default while it was mounted and running, then immediately read the result and destroyed the snapshot. ZFS doesn't support rolling back a mounted root, and both follow-ups were unsafe. The page and dentry caches kept serving the pre-rollback view, so the sentinel check failed on four scenarios and passed on two identical ones. The cleanup destroy was worse: it blocked in cv_wait_common behind an uninterruptible txg_quiesce and wedged the pool, taking a whole run with it.

The check reboots the guest first now. A reboot exports and reimports the pool, which clears the fragile state and makes the on-disk contents readable. I pulled boot_from_disk and config_encrypt_flag out of run_test's inline boot block, so the mid-test and end-of-install paths share one implementation.

ssh_cmd is bounded by SSH_CMD_TIMEOUT, default 120s. ConnectTimeout only bounds the connection, so that wedged guest held a healthy session open and hung the run for 40 minutes. The installer call raises the bound to INSTALL_TIMEOUT, since it legitimately runs for many minutes.

send_zfs_passphrase sends once. Embedding the pool key left a single prompt, so the second send was typing the passphrase at a login prompt.
</content>
</entry>
<entry>
<title>fix(test): stop one failed scenario from failing every scenario after it</title>
<updated>2026-08-03T03:20:55+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-08-03T03:20:55+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archangel/commit/?id=1da64394f637347414ffc4954daf9c72d180e2d0'/>
<id>urn:sha1:1da64394f637347414ffc4954daf9c72d180e2d0</id>
<content type='text'>
run_test exported INSTALLED_PASSWORD after the reboot step but cleared it only on the success path. Any failure after that point leaked the installed system's password into the next scenario, where ssh_cmd offered it to the live ISO and every call failed instantly. Six ZFS scenarios died that way behind one flaky check. I made it a local, so bash clears it on every return path.

Install logs were written after stop_vm, so the fetch always reached a stopped guest. Every *-install.log this harness produced was empty, which is why the April mirror failure went 96 days undiagnosed. I write the captured log first now, then stop the VM.

verify_rollback rolls back the mounted root underneath the running system. The wrapper check that ran next inherited the damage and failed at random. I put the fatal check first on a clean guest and left the destabilising one last, where it can only warn. Rebooting between them is the real fix. It needs the encrypted-pool passphrase re-sent through the QEMU monitor, so it stays filed.

Both installer pushes in run_install are checked now. An unreported failure left the guest with no installer. The run then died at exit 127 with no output, which reads exactly like a real regression.
</content>
</entry>
<entry>
<title>fix(test): run the ZFS-encryption check on the booted system</title>
<updated>2026-05-23T01:58:01+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-23T01:58:01+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archangel/commit/?id=3165c50fed266fef0b388190296c149c0ae0ee47'/>
<id>urn:sha1:3165c50fed266fef0b388190296c149c0ae0ee47</id>
<content type='text'>
The ZFS native-encryption assertion lived in verify_install, which runs in the live ISO before reboot. But archangel exports zroot at the end of the install, so verify_install bails at "ZFS pool not found" and never reaches the check. It was dead code: the encrypted-config tests passed on the reboot path (entering the passphrase at ZFSBootMenu and booting is itself proof), while the explicit aes-256-gcm assertion gave false confidence by never running.

I moved it into verify_reboot_survival, which ssh's into the booted system where zroot is imported, so zfs get encryption zroot/ROOT actually returns aes-256-gcm and the assertion fires. Confirmed on a zfs-encrypt VM run: "ZFS encryption (aes-256-gcm) verified on running system."
</content>
</entry>
<entry>
<title>fix(build): clear stale archzfs from the pacoloco cache too</title>
<updated>2026-05-23T01:28:15+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-23T01:28:15+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archangel/commit/?id=bed054f46e3b41aae0d599ed7fbc3e1e42d6ddd7'/>
<id>urn:sha1:bed054f46e3b41aae0d599ed7fbc3e1e42d6ddd7</id>
<content type='text'>
archzfs re-uploads its GitHub release assets under the same filename, so pacoloco keeps serving a zfs-dkms/zfs-utils it cached earlier while pacman fetches a fresh archzfs.db with a new checksum. The two mismatch and pacstrap aborts with "invalid or corrupted package." build.sh already drops the stale packages from the host pacman cache, but it never cleared the pacoloco layer, which the VM test installs route through too, so test-install.sh kept hitting the corruption (four times in one session).

build.sh runs as root, so it now clears /var/cache/pacoloco/pkgs/archzfs/zfs-* alongside the host cache, which makes the build-then-test flow self-healing. The pacoloco cache is root-owned and test-install.sh runs as the user, so it can't clear it unattended. Instead, test-install.sh now recognizes the corruption (is_archzfs_cache_corruption) and prints how to clear it, the way it already names the SSH_PORT override on a port collision. A retry alone won't help since it hits the same cached file, so this fails fast with the hint rather than retrying.
</content>
</entry>
<entry>
<title>fix(test): fail clearly when the VM forward port is taken</title>
<updated>2026-05-23T01:12:14+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-23T01:12:14+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archangel/commit/?id=0f8bbc7c1e2c2f6fec0b17753ac0d9c4a3ad4317'/>
<id>urn:sha1:0f8bbc7c1e2c2f6fec0b17753ac0d9c4a3ad4317</id>
<content type='text'>
A test run launched qemu without first checking the SSH forward port, so a collision with another VM already holding it surfaced only as an opaque "Failed to start VM," with qemu unable to bind and no hint why. I added a port_in_use check in run_test before the launch: it errors with the port number and the SSH_PORT override to set, records the failure, and moves on.

The check lives in run_test, not start_vm, because start_vm runs in a command substitution (vm_pid=$(start_vm ...)) where this harness's non-exiting error() would be captured as the PID instead of failing the run. The pure half, port_listening_in, takes an `ss -tln` snapshot as a string so it's unit-testable.
</content>
</entry>
<entry>
<title>test: make SSH_PORT overridable in test-install.sh</title>
<updated>2026-05-22T23:09:50+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-22T23:09:50+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archangel/commit/?id=0a57d75d3947fddd6c6ab62924c52a456f4776b0'/>
<id>urn:sha1:0a57d75d3947fddd6c6ab62924c52a456f4776b0</id>
<content type='text'>
The port was hardcoded, so a test run collided with any other VM already forwarding 2222. It now defaults to 2222, so existing invocations are unchanged. SSH_PORT=2223 scripts/test-install.sh picks a free port to run alongside another VM.
</content>
</entry>
</feed>
