Skip to content
Open
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
27 changes: 27 additions & 0 deletions .changeset/imager-device-metadata.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
"ftw": patch
---

Raspberry Pi Imager: the repository manifest now declares the hardware it
supports, so the documented install flow reaches the FTW image instead of
dead-ending on an empty device chooser.

Imager 2.x opens on "Select your Raspberry Pi device" and builds that list
solely from `imager.devices` in whichever manifest it was pointed at — a
custom repository replaces the stock manifest rather than extending it. FTW
published neither an `imager` object nor a `devices` array, so `HWListModel`
bailed with "missing imager", the device list stayed empty, and **Next**
stayed disabled: the OS list had loaded, so the offline escape hatch
(`osListUnavailable && hwlist.count === 0`) did not apply. Nothing on the
device step could be clicked and the FTW entry was never reached.

The entry itself was also untagged. Imager keeps a `devices`-less OS entry
only when the selected device matches inclusively, so a Pi 5 — which matches
exclusively — would have filtered FTW out even once a device was selectable.
`devices` is required by Imager's published schema for a direct-image entry.

The manifest now ships `imager.devices` (Pi 5, Pi 4, and "No filtering") and
tags the FTW entry `pi5-64bit` / `pi4-64bit`, matching the arm64 image it
actually builds. Both workflows that publish the manifest assert the two
lists are present and agree, so an entry tagged for a device the chooser
never offers fails CI rather than shipping unreachable.
12 changes: 12 additions & 0 deletions .github/workflows/release-assets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,18 @@ jobs:
and (.[0].url | contains("/releases/download/rpi-installer/"))
and (.[0].extract_sha256 | test("^[0-9a-f]{64}$"))
' installer/os_list.json >/dev/null
# Imager 2.x needs a device chooser (imager.devices) and device tags
# on the entry itself, or the wizard dead-ends before reaching FTW.
# Warn rather than fail: the installer image is independent of
# application releases, so an image published before this metadata
# landed must not block an unrelated version release. Rebuilding via
# rpi-image-build.yml clears it.
if ! jq -e '
(.imager.devices | length) > 0
and (.os_list[0].devices | length) > 0
' installer/os_list.json >/dev/null; then
echo "::warning::Published os_list.json has no imager.devices/os_list[].devices; Raspberry Pi Imager 2.x will dead-end on an empty device step. Rerun rpi-image-build.yml to republish the installer metadata."
fi
gh release upload "${TAG}" installer/os_list.json \
--repo "${GITHUB_REPOSITORY}" --clobber

Expand Down
11 changes: 11 additions & 0 deletions .github/workflows/rpi-image-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,17 @@ jobs:
and (.[0].extract_sha256 | contains("__EXTRACT_SHA256__"))
' deploy/imager/os_list.json >/dev/null

# Imager 2.x builds its device chooser from imager.devices and drops
# os_list entries that claim no device tags. Without both halves the
# wizard dead-ends on an empty device step, so assert they agree.
jq -e '
(.imager.devices | length) > 0
and all(.imager.devices[]; has("name") and has("description") and has("tags"))
and (.os_list[0].devices | length) > 0
and ([.imager.devices[].tags[]] as $offered
| .os_list[0].devices | all(. as $t | $offered | index($t)))
' deploy/imager/os_list.json >/dev/null

- name: Validate embedded Compose file
run: docker compose -f docker-compose.yml config --quiet

Expand Down
28 changes: 28 additions & 0 deletions deploy/imager/os_list.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,31 @@
{
"imager": {
"devices": [
{
"name": "Raspberry Pi 5",
"description": "Raspberry Pi 5, 500 / 500+, and Compute Module 5",
"icon": "https://downloads.raspberrypi.com/imager/icons/RPi_5.png",
"tags": ["pi5-64bit", "pi5-32bit"],
"matching_type": "exclusive",
"capabilities": []
},
{
"name": "Raspberry Pi 4",
"description": "Raspberry Pi 4 Model B, 400, and Compute Module 4 / 4S",
"icon": "https://downloads.raspberrypi.com/imager/icons/RPi_4.png",
"tags": ["pi4-64bit", "pi4-32bit"],
"matching_type": "exclusive",
"capabilities": []
},
{
"name": "No filtering",
"description": "Show every possible image",
"tags": [],
"matching_type": "inclusive",
"capabilities": []
}
]
},
"os_list": [
{
"name": "FTW",
Expand All @@ -10,6 +37,7 @@
"image_download_size": 0,
"release_date": "1970-01-01",
"init_format": "cloudinit-rpi",
"devices": ["pi5-64bit", "pi4-64bit"],
"website": "https://ftw.sourceful.energy"
}
]
Expand Down
37 changes: 28 additions & 9 deletions docs/rpi-image.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,24 @@ credentials and Wi-Fi before writing the card.

1. Install [Raspberry Pi Imager](https://www.raspberrypi.com/software/) 2.0 or
newer.
2. Open **App Options → Content Repository → Edit → Use custom file**.
3. Enter:
2. Open **App Options → Content Repository → Edit → Use custom URL**.
3. Paste:

```text
https://github.com/srcfl/ftw/releases/download/rpi-installer/os_list.json
```

4. Apply and restart Imager.
5. Choose **FTW**, choose the SD card, and set hostname, SSH user/password and
4. Click **Apply & Restart**.
5. Choose **Raspberry Pi 5** or **Raspberry Pi 4** on the device step.
6. Choose **FTW**, choose the SD card, and set hostname, SSH user/password and
Wi-Fi in OS customisation.
6. Write the card, insert it and power on the Pi.
7. After first-boot provisioning, open `http://ftw.local/` on the same network.
7. Write the card, insert it and power on the Pi.
8. After first-boot provisioning, open `http://ftw.local/` on the same network.

Pick **Use custom URL**, not **Use custom file**. They are separate options in
the same dialog: **Use custom file** opens a file picker for a manifest already
on your computer and offers no field to paste a web address into. The FTW
manifest is hosted, so it belongs in **Use custom URL**.

`rpi-installer` is the permanent repository URL and is the recommended value
to save in Imager. Stable application releases also mirror this small file, so
Expand Down Expand Up @@ -120,6 +126,19 @@ The output path and exact base image are defined by those scripts and the image
workflow. CI runs structural checks on changes; full images are built on the
installer schedule or explicit dispatch.

The Imager repository entry intentionally has no `devices` filter. Custom
repositories do not receive Raspberry Pi Imager's stock hardware list, so a
filter would hide FTW from the chooser even though the image supports Pi 4/5.
A custom repository replaces Imager's stock manifest rather than extending it,
so [`deploy/imager/os_list.json`](../deploy/imager/os_list.json) has to carry
both halves of the hardware contract itself:

- `imager.devices` supplies the device chooser. Imager 2.x builds the first
wizard step from this list alone, and leaves **Next** disabled until a device
is selected. A manifest without it dead-ends the wizard on an empty list.
- `os_list[].devices` marks FTW as compatible with those devices. Imager drops
an entry with no `devices` tags whenever the selected device filters
exclusively, and the field is required by
[Imager's schema](https://github.com/raspberrypi/rpi-imager/blob/main/doc/json-schema/os-list-schema.json).

The tags are Imager's own (`pi5-64bit`, `pi4-64bit`); FTW ships arm64 only, so
it claims the 64-bit tags for Pi 4 and Pi 5. Keep the two lists consistent when
either changes — an entry tagged for a device the chooser never offers is
unreachable.