From 052fd7dde67f20a5bbf62a66caaab5e3b3084232 Mon Sep 17 00:00:00 2001 From: Henrik Klee <89942958+henrikklee@users.noreply.github.com> Date: Wed, 28 Oct 2026 00:43:43 +0800 Subject: [PATCH 1/2] Allow an Omarchy-only shared launcher without rebuilding Minimal Linux --- .github/workflows/launcher.yml | 40 +++++++++++++++++++++++++++++----- docs/ci.md | 14 ++++++++++-- tests/test_quiet_boot.py | 38 ++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 7 deletions(-) diff --git a/.github/workflows/launcher.yml b/.github/workflows/launcher.yml index 24179a54..9753f7c3 100644 --- a/.github/workflows/launcher.yml +++ b/.github/workflows/launcher.yml @@ -5,14 +5,18 @@ on: hardware_tag: description: Published hardware release containing the wheel and component bundle required: true - default: v0.1.9 + default: v0.1.10 cli_tag: description: Published compatible CLI release required: true - default: v0.1.8 + default: v0.1.10 minimal_tag: - description: Published Minimal Linux release (supplies the Alpine base and one compatible root) + description: Published Minimal Linux release supplying the Alpine base userspace required: true + include_minimal_root: + description: Also advertise its installed root (requires matching kernel/modules) + type: boolean + default: true omarchy_tag: description: Optional published Omarchy A release to support as well required: false @@ -48,6 +52,7 @@ jobs: HARDWARE_TAG: ${{ inputs.hardware_tag }} CLI_TAG: ${{ inputs.cli_tag }} MINIMAL_TAG: ${{ inputs.minimal_tag }} + INCLUDE_MINIMAL_ROOT: ${{ inputs.include_minimal_root }} OMARCHY_TAG: ${{ inputs.omarchy_tag }} steps: - uses: actions/checkout@v4 @@ -56,6 +61,12 @@ jobs: - uses: actions/setup-python@v5 with: python-version: '3.12' + - name: Require at least one installed root + run: | + if [[ "$INCLUDE_MINIMAL_ROOT" != true && -z "$OMARCHY_TAG" ]]; then + echo 'Select a matching Minimal Linux root or provide an Omarchy A release.' >&2 + exit 1 + fi - name: Await exact first-release image builds if: inputs.hardware_run_id != '' env: @@ -139,8 +150,11 @@ jobs: (cd out/hardware && sha256sum --check --ignore-missing SHA256SUMS) tar -xzf out/hardware/j71b-components.tar.gz -C out gh release download "$MINIMAL_TAG" --repo OrchardLinux/minimal-linux \ - --pattern rootfs.tar.gz --pattern rootfs.json --pattern build-receipt.json \ --pattern base.tar.gz --pattern base.tar.gz.json --pattern SHA256SUMS --dir out/minimal + if [[ "$INCLUDE_MINIMAL_ROOT" == true ]]; then + gh release download "$MINIMAL_TAG" --repo OrchardLinux/minimal-linux \ + --pattern rootfs.tar.gz --pattern rootfs.json --pattern build-receipt.json --dir out/minimal + fi (cd out/minimal && sha256sum --check --ignore-missing SHA256SUMS) if [[ -n "$OMARCHY_TAG" ]]; then mkdir -p out/omarchy @@ -152,7 +166,10 @@ jobs: - name: Build and verify the shared launcher run: | python ci/launcher_inputs.py --components out/components --output out/launcher-inputs.json - installations=(--installation out/minimal/rootfs.tar.gz out/minimal/rootfs.json out/minimal/build-receipt.json) + installations=() + if [[ "$INCLUDE_MINIMAL_ROOT" == true ]]; then + installations+=(--installation out/minimal/rootfs.tar.gz out/minimal/rootfs.json out/minimal/build-receipt.json) + fi if [[ -n "$OMARCHY_TAG" ]]; then installations+=(--installation out/omarchy/rootfs.tar.zst out/omarchy/rootfs.json out/omarchy/build-receipt.json) fi @@ -162,6 +179,19 @@ jobs: "${installations[@]}" --release "${HARDWARE_TAG#v}" \ --public-base-url "https://github.com/OrchardLinux/hardware/releases/download/$HARDWARE_TAG" \ --output out/launcher + # The host must carry this build's firmware recipe, not just its protocol. + python - <<'PY' + import os, re + from pathlib import Path + minimum = os.environ['CLI_TAG'].removeprefix('v') + for name in ('boot.toml', 'j71b-launcher.boot.toml'): + path = Path('out/launcher') / name + text, count = re.subn(r'^min_orchard = "[^"]+"$', + 'min_orchard = "' + minimum + '"', + path.read_text(), flags=re.MULTILINE) + assert count == 1, 'Expected one launcher CLI requirement' + path.write_text(text) + PY mkdir -p out/publication cp out/launcher/recovery.bin out/launcher/pongo.bin out/launcher/j71b-launcher.boot.toml \ out/launcher/launch.json out/publication/ diff --git a/docs/ci.md b/docs/ci.md index 73e257f1..2f6a69ef 100644 --- a/docs/ci.md +++ b/docs/ci.md @@ -53,8 +53,18 @@ Use the expected repository identity, not just a matching hash. Attestation iden Run `.github/workflows/launcher.yml` manually after the image releases exist. Select: - The hardware and compatible CLI tags. -- A Minimal Linux tag (supplies its signed Alpine base and compatible root). -- Optionally an Omarchy A tag, built with the same kernel/modules/runtime. +- A Minimal Linux tag (supplies its signed Alpine base userspace). +- Whether to include that Minimal Linux installation (`include_minimal_root`, default true); its root must match the selected kernel/modules/runtime. +- Optionally an Omarchy A tag, built with the same kernel/modules/runtime. It is required when `include_minimal_root` is false. + +For an Omarchy-only hardware update, set `include_minimal_root=false` and supply +the new Omarchy tag. The existing published Alpine base is reused only for RAM +userspace; its old installed root is neither downloaded in the public-input +path nor added to the launch catalogue. No Minimal Linux rebuild is required. +The producer still verifies the base receipt and every selected installation's +exact kernel/modules/runtime compatibility. Before checksumming and attesting, +the workflow sets both launcher manifests' `min_orchard` to the selected CLI +tag so older host firmware recipes are rejected before hardware preparation. The producer validates component inventories, quiet-patch receipts, root streams and kernel/runtime compatibility. It refuses incompatible roots rather than advertising unsupported installations. diff --git a/tests/test_quiet_boot.py b/tests/test_quiet_boot.py index 703d7db0..cdfdfa86 100644 --- a/tests/test_quiet_boot.py +++ b/tests/test_quiet_boot.py @@ -1,6 +1,10 @@ """Shared discovery must not accidentally inherit distro artwork.""" import hashlib import json +import os +import subprocess +import sys +import textwrap from pathlib import Path import tempfile import unittest @@ -10,6 +14,40 @@ class QuietBoot(unittest.TestCase): + def test_workflow_root_selection_keeps_omarchy_independent(self): + workflow = (Path(__file__).resolve().parents[1] / '.github/workflows/launcher.yml').read_text() + guard = workflow.split('- name: Require at least one installed root\n', 1)[1] + guard = textwrap.dedent(guard.split('run: |\n', 1)[1].split(' - name:', 1)[0]) + selection = workflow.split(' installations=()\n', 1)[1] + selection = 'installations=()\n' + textwrap.dedent(selection.split(' python -m orchard_hardware.image.launcher', 1)[0]) + for include, omarchy, count in [('true', '', 1), ('true', 'v0.1.1', 2), + ('false', 'v0.1.1', 1), ('false', '', 0)]: + with self.subTest(include=include, omarchy=omarchy): + result = subprocess.run(['bash', '-ec', guard + '\n' + selection + + '\nprintf "%s\\n" "${installations[@]}"'], + env={**os.environ, 'INCLUDE_MINIMAL_ROOT': include, + 'OMARCHY_TAG': omarchy}, capture_output=True, text=True) + if not count: + self.assertNotEqual(result.returncode, 0) + continue + self.assertEqual(result.returncode, 0, result.stderr) + self.assertEqual(result.stdout.splitlines().count('--installation'), count) + self.assertEqual('out/minimal/rootfs.tar.gz' in result.stdout, include == 'true') + self.assertEqual('out/omarchy/rootfs.tar.zst' in result.stdout, bool(omarchy)) + + def test_published_launcher_requires_paired_cli(self): + workflow = (Path(__file__).resolve().parents[1] / '.github/workflows/launcher.yml').read_text() + script = textwrap.dedent(workflow.split(" python - <<'PY'\n", 1)[1].split(' PY\n', 1)[0]) + with tempfile.TemporaryDirectory() as directory: + output = Path(directory) / 'out/launcher' + output.mkdir(parents=True) + for name in ('boot.toml', 'j71b-launcher.boot.toml'): + (output / name).write_text('min_orchard = "0.1.8"\nprotocol = 1\n') + subprocess.run([sys.executable, '-c', script], cwd=directory, + env={**os.environ, 'CLI_TAG': 'v0.1.10'}, check=True) + for path in output.iterdir(): + self.assertEqual(path.read_text(), 'min_orchard = "0.1.10"\nprotocol = 1\n') + def test_receipt_binds_both_components_and_current_patches(self): here = Path(orchard_hardware.__file__).parent / 'boot/quiet' files = {role: {'bytes': 1, 'sha256': role * 8} for role in ('loader', 'pongo')} From 375fee76712fa1ba70aa83c1e7e5b8b2b154405a Mon Sep 17 00:00:00 2001 From: Henrik Klee <89942958+henrikklee@users.noreply.github.com> Date: Wed, 28 Oct 2026 00:47:50 +0800 Subject: [PATCH 2/2] Keep both distro roots in sync and require the paired CLI --- .github/workflows/launcher.yml | 25 ++++--------------------- docs/ci.md | 18 ++++-------------- tests/test_quiet_boot.py | 21 --------------------- 3 files changed, 8 insertions(+), 56 deletions(-) diff --git a/.github/workflows/launcher.yml b/.github/workflows/launcher.yml index 9753f7c3..65895a35 100644 --- a/.github/workflows/launcher.yml +++ b/.github/workflows/launcher.yml @@ -11,12 +11,8 @@ on: required: true default: v0.1.10 minimal_tag: - description: Published Minimal Linux release supplying the Alpine base userspace + description: Published Minimal Linux release (supplies the Alpine base and one compatible root) required: true - include_minimal_root: - description: Also advertise its installed root (requires matching kernel/modules) - type: boolean - default: true omarchy_tag: description: Optional published Omarchy A release to support as well required: false @@ -52,7 +48,6 @@ jobs: HARDWARE_TAG: ${{ inputs.hardware_tag }} CLI_TAG: ${{ inputs.cli_tag }} MINIMAL_TAG: ${{ inputs.minimal_tag }} - INCLUDE_MINIMAL_ROOT: ${{ inputs.include_minimal_root }} OMARCHY_TAG: ${{ inputs.omarchy_tag }} steps: - uses: actions/checkout@v4 @@ -61,12 +56,6 @@ jobs: - uses: actions/setup-python@v5 with: python-version: '3.12' - - name: Require at least one installed root - run: | - if [[ "$INCLUDE_MINIMAL_ROOT" != true && -z "$OMARCHY_TAG" ]]; then - echo 'Select a matching Minimal Linux root or provide an Omarchy A release.' >&2 - exit 1 - fi - name: Await exact first-release image builds if: inputs.hardware_run_id != '' env: @@ -150,11 +139,8 @@ jobs: (cd out/hardware && sha256sum --check --ignore-missing SHA256SUMS) tar -xzf out/hardware/j71b-components.tar.gz -C out gh release download "$MINIMAL_TAG" --repo OrchardLinux/minimal-linux \ + --pattern rootfs.tar.gz --pattern rootfs.json --pattern build-receipt.json \ --pattern base.tar.gz --pattern base.tar.gz.json --pattern SHA256SUMS --dir out/minimal - if [[ "$INCLUDE_MINIMAL_ROOT" == true ]]; then - gh release download "$MINIMAL_TAG" --repo OrchardLinux/minimal-linux \ - --pattern rootfs.tar.gz --pattern rootfs.json --pattern build-receipt.json --dir out/minimal - fi (cd out/minimal && sha256sum --check --ignore-missing SHA256SUMS) if [[ -n "$OMARCHY_TAG" ]]; then mkdir -p out/omarchy @@ -166,10 +152,7 @@ jobs: - name: Build and verify the shared launcher run: | python ci/launcher_inputs.py --components out/components --output out/launcher-inputs.json - installations=() - if [[ "$INCLUDE_MINIMAL_ROOT" == true ]]; then - installations+=(--installation out/minimal/rootfs.tar.gz out/minimal/rootfs.json out/minimal/build-receipt.json) - fi + installations=(--installation out/minimal/rootfs.tar.gz out/minimal/rootfs.json out/minimal/build-receipt.json) if [[ -n "$OMARCHY_TAG" ]]; then installations+=(--installation out/omarchy/rootfs.tar.zst out/omarchy/rootfs.json out/omarchy/build-receipt.json) fi @@ -179,7 +162,7 @@ jobs: "${installations[@]}" --release "${HARDWARE_TAG#v}" \ --public-base-url "https://github.com/OrchardLinux/hardware/releases/download/$HARDWARE_TAG" \ --output out/launcher - # The host must carry this build's firmware recipe, not just its protocol. + # Require the paired host firmware recipe, not only the boot protocol. python - <<'PY' import os, re from pathlib import Path diff --git a/docs/ci.md b/docs/ci.md index 2f6a69ef..f9c73474 100644 --- a/docs/ci.md +++ b/docs/ci.md @@ -53,20 +53,10 @@ Use the expected repository identity, not just a matching hash. Attestation iden Run `.github/workflows/launcher.yml` manually after the image releases exist. Select: - The hardware and compatible CLI tags. -- A Minimal Linux tag (supplies its signed Alpine base userspace). -- Whether to include that Minimal Linux installation (`include_minimal_root`, default true); its root must match the selected kernel/modules/runtime. -- Optionally an Omarchy A tag, built with the same kernel/modules/runtime. It is required when `include_minimal_root` is false. - -For an Omarchy-only hardware update, set `include_minimal_root=false` and supply -the new Omarchy tag. The existing published Alpine base is reused only for RAM -userspace; its old installed root is neither downloaded in the public-input -path nor added to the launch catalogue. No Minimal Linux rebuild is required. -The producer still verifies the base receipt and every selected installation's -exact kernel/modules/runtime compatibility. Before checksumming and attesting, -the workflow sets both launcher manifests' `min_orchard` to the selected CLI -tag so older host firmware recipes are rejected before hardware preparation. - -The producer validates component inventories, quiet-patch receipts, root streams and kernel/runtime compatibility. It refuses incompatible roots rather than advertising unsupported installations. +- A Minimal Linux tag (supplies its signed Alpine base and compatible root). +- Optionally an Omarchy A tag, built with the same kernel/modules/runtime. + +The producer validates component inventories, quiet-patch receipts, root streams and kernel/runtime compatibility. It refuses incompatible roots rather than advertising unsupported installations. For the J71b update, rebuild both Minimal Linux and Omarchy A with hardware/CLI v0.1.10 and include both new roots. Before checksumming and attesting, the workflow sets both launcher manifests' `min_orchard` to the selected CLI tag so older host firmware recipes are rejected before preparation. By default the workflow only uploads `shared-launcher-assets`. Explicitly enable `attach_assets` to attach new assets to the selected hardware release. This never overwrites existing assets, changes the latest-release selection, or publishes a draft. The wheel/component checksums stay immutable; launcher assets have a separate `launcher.SHA256SUMS`. diff --git a/tests/test_quiet_boot.py b/tests/test_quiet_boot.py index cdfdfa86..c57dfbe3 100644 --- a/tests/test_quiet_boot.py +++ b/tests/test_quiet_boot.py @@ -14,27 +14,6 @@ class QuietBoot(unittest.TestCase): - def test_workflow_root_selection_keeps_omarchy_independent(self): - workflow = (Path(__file__).resolve().parents[1] / '.github/workflows/launcher.yml').read_text() - guard = workflow.split('- name: Require at least one installed root\n', 1)[1] - guard = textwrap.dedent(guard.split('run: |\n', 1)[1].split(' - name:', 1)[0]) - selection = workflow.split(' installations=()\n', 1)[1] - selection = 'installations=()\n' + textwrap.dedent(selection.split(' python -m orchard_hardware.image.launcher', 1)[0]) - for include, omarchy, count in [('true', '', 1), ('true', 'v0.1.1', 2), - ('false', 'v0.1.1', 1), ('false', '', 0)]: - with self.subTest(include=include, omarchy=omarchy): - result = subprocess.run(['bash', '-ec', guard + '\n' + selection + - '\nprintf "%s\\n" "${installations[@]}"'], - env={**os.environ, 'INCLUDE_MINIMAL_ROOT': include, - 'OMARCHY_TAG': omarchy}, capture_output=True, text=True) - if not count: - self.assertNotEqual(result.returncode, 0) - continue - self.assertEqual(result.returncode, 0, result.stderr) - self.assertEqual(result.stdout.splitlines().count('--installation'), count) - self.assertEqual('out/minimal/rootfs.tar.gz' in result.stdout, include == 'true') - self.assertEqual('out/omarchy/rootfs.tar.zst' in result.stdout, bool(omarchy)) - def test_published_launcher_requires_paired_cli(self): workflow = (Path(__file__).resolve().parents[1] / '.github/workflows/launcher.yml').read_text() script = textwrap.dedent(workflow.split(" python - <<'PY'\n", 1)[1].split(' PY\n', 1)[0])