Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions .github/workflows/launcher.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ 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)
required: true
Expand Down Expand Up @@ -162,6 +162,19 @@ jobs:
"${installations[@]}" --release "${HARDWARE_TAG#v}" \
--public-base-url "https://github.com/OrchardLinux/hardware/releases/download/$HARDWARE_TAG" \
--output out/launcher
# Require the paired host firmware recipe, not only the boot 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/
Expand Down
2 changes: 1 addition & 1 deletion docs/ci.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Run `.github/workflows/launcher.yml` manually after the image releases exist. Se
- 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.
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`.

Expand Down
17 changes: 17 additions & 0 deletions tests/test_quiet_boot.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -10,6 +14,19 @@


class QuietBoot(unittest.TestCase):
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')}
Expand Down
Loading