From f13b9b4479d7ce751f4fca6d484861f40806759e Mon Sep 17 00:00:00 2001 From: Viktor Petersson Date: Tue, 19 May 2026 18:13:55 +0000 Subject: [PATCH 1/2] fix(ci): annotate per-board snippets with Pi Imager devices tags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pi Imager filters the OS list against the user-selected device. The Pi 5 entry in the upstream root JSON uses `matching_type: exclusive`, so an image lacking a `devices` array is silently hidden when a user picks Pi 5 in the device picker. Raspberry Pi has signalled (Nov 2023 Tom Dewey email) that the same exclusive filter will roll out to older boards. Our per-board JSON snippets currently emit no `devices` field — meaning fresh Anthias builds wouldn't surface in Pi Imager on Pi 5 even if the upstream master list pointed at our subitems_url. - Map BOARD slug → device tags in the workflow's package step (pi2 → pi2-32bit, pi3 → pi3-64bit, pi4-64 → pi4-64bit, pi5 → pi5-64bit, x86 → []). - Extend REQUIRED_FIELDS in build_pi_imager_json.py so the contract is documented and the existing required-fields test enforces it. - Update test fixtures + add a per-board devices-preservation test. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../workflows/build-balena-disk-image.yaml | 20 +++++++++++++++- .../build_pi_imager_json.py | 5 ++++ .../tests/test_build_pi_imager_json.py | 23 +++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-balena-disk-image.yaml b/.github/workflows/build-balena-disk-image.yaml index bba5d546c..78ed9fc5b 100644 --- a/.github/workflows/build-balena-disk-image.yaml +++ b/.github/workflows/build-balena-disk-image.yaml @@ -395,6 +395,22 @@ jobs: printf '%s %s\n' "$DOWNLOAD_SHA256" "$ARTIFACT.img.zst" } > "$ARTIFACT.sha256" + # Map our board slug to the Pi Imager `devices` tags. Without + # this annotation, Pi 5 (which uses `matching_type: exclusive` + # in the upstream root JSON) hides our image when a user picks + # Pi 5 in the device picker. Raspberry Pi has been rolling the + # same exclusive filter out to older boards, so we annotate + # all of them. x86 isn't a Pi Imager target — emit an empty + # array so the snippet still validates as schema-conformant. + case "$BOARD" in + pi2) DEVICES='["pi2-32bit"]' ;; + pi3) DEVICES='["pi3-64bit"]' ;; + pi4-64) DEVICES='["pi4-64bit"]' ;; + pi5) DEVICES='["pi5-64bit"]' ;; + x86) DEVICES='[]' ;; + *) echo "::error::unknown board $BOARD for devices mapping" >&2; exit 1 ;; + esac + # Per-board metadata snippet, uploaded to the release # alongside the .img.zst it describes. # build_pi_imager_json.py fetches it via the same base name @@ -406,6 +422,7 @@ jobs: --arg DOWNLOAD_SHA256 "$DOWNLOAD_SHA256" \ --argjson DOWNLOAD_SIZE "$DOWNLOAD_SIZE" \ --arg RELEASE_DATE "$BUILD_DATE" \ + --argjson DEVICES "$DEVICES" \ '{ "name": ("Anthias (" + $BOARD + ")"), "description": "Anthias, formerly known as Screenly OSE, is the most popular open source digital signage project in the world.", @@ -415,7 +432,8 @@ jobs: "extract_sha256": $IMAGE_SHA256, "image_download_size": $DOWNLOAD_SIZE, "image_download_sha256": $DOWNLOAD_SHA256, - "release_date": $RELEASE_DATE + "release_date": $RELEASE_DATE, + "devices": $DEVICES }' > "$ARTIFACT.json" - name: Upload artifacts diff --git a/tools/raspberry_pi_imager/build_pi_imager_json.py b/tools/raspberry_pi_imager/build_pi_imager_json.py index 5d9a19013..cd4cd7df0 100644 --- a/tools/raspberry_pi_imager/build_pi_imager_json.py +++ b/tools/raspberry_pi_imager/build_pi_imager_json.py @@ -33,6 +33,11 @@ 'image_download_sha256', 'release_date', 'url', + # Pi Imager filters the OS list by selected device. Pi 5 uses + # `matching_type: exclusive` in the upstream root JSON, so an image + # without `devices` is hidden when a user picks Pi 5. The same + # exclusive filter is being rolled out to older boards. + 'devices', } diff --git a/tools/raspberry_pi_imager/tests/test_build_pi_imager_json.py b/tools/raspberry_pi_imager/tests/test_build_pi_imager_json.py index 485db414b..0a7ac35fb 100644 --- a/tools/raspberry_pi_imager/tests/test_build_pi_imager_json.py +++ b/tools/raspberry_pi_imager/tests/test_build_pi_imager_json.py @@ -62,6 +62,14 @@ } +BOARD_DEVICES = { + 'pi2': ['pi2-32bit'], + 'pi3': ['pi3-64bit'], + 'pi4-64': ['pi4-64bit'], + 'pi5': ['pi5-64bit'], +} + + def make_image_metadata(board: str) -> dict[str, Any]: return { 'name': f'Anthias ({board})', @@ -73,6 +81,7 @@ def make_image_metadata(board: str) -> dict[str, Any]: 'image_download_size': '1600981967', 'image_download_sha256': 'def456', 'release_date': '2025-01-01', + 'devices': BOARD_DEVICES[board], } @@ -272,6 +281,20 @@ def test_retrieve_and_patch_json_has_all_required_fields( assert not missing, f'Missing fields: {missing}' +@pytest.mark.parametrize('board, expected_devices', BOARD_DEVICES.items()) +def test_retrieve_and_patch_json_preserves_devices( + mock_requests_get: MagicMock, + board: str, + expected_devices: list[str], +) -> None: + mock_requests_get.return_value = _build_side_effect( + make_image_metadata(board) + ) + url = f'{BASE_RELEASE_URL}/2025-01-01-anthias-{board}.img.zst' + + assert retrieve_and_patch_json(url)['devices'] == expected_devices + + # --------------------------------------------------------------------------- # build_imager_json # --------------------------------------------------------------------------- From 6b198a5ef136d5c92de41318814e6a6bb770b17a Mon Sep 17 00:00:00 2001 From: Viktor Petersson Date: Thu, 21 May 2026 08:11:14 +0000 Subject: [PATCH 2/2] fix(ci): correct pi3 device tag to pi3-32bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Anthias pi3 image is 32-bit armhf (target_platform `linux/arm/v7` in tools/image_builder/utils.py, with libraspberrypi0 linked via the Raspbian legacy userland — see image_builder/__main__.py which calls out pi2/pi3 as the 32-bit boards). The balena image type is `raspberrypi3` (32-bit), not `raspberrypi3-64`. Tagging this image as `pi3-64bit` misdescribes what's in the .img.zst and would hide it from Pi 3 users if/when Pi Imager flips Pi 3 to `matching_type: exclusive` or adds a bitness filter. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/build-balena-disk-image.yaml | 2 +- tools/raspberry_pi_imager/tests/test_build_pi_imager_json.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-balena-disk-image.yaml b/.github/workflows/build-balena-disk-image.yaml index 78ed9fc5b..ae4d38bed 100644 --- a/.github/workflows/build-balena-disk-image.yaml +++ b/.github/workflows/build-balena-disk-image.yaml @@ -404,7 +404,7 @@ jobs: # array so the snippet still validates as schema-conformant. case "$BOARD" in pi2) DEVICES='["pi2-32bit"]' ;; - pi3) DEVICES='["pi3-64bit"]' ;; + pi3) DEVICES='["pi3-32bit"]' ;; pi4-64) DEVICES='["pi4-64bit"]' ;; pi5) DEVICES='["pi5-64bit"]' ;; x86) DEVICES='[]' ;; diff --git a/tools/raspberry_pi_imager/tests/test_build_pi_imager_json.py b/tools/raspberry_pi_imager/tests/test_build_pi_imager_json.py index 0a7ac35fb..c97c00fb6 100644 --- a/tools/raspberry_pi_imager/tests/test_build_pi_imager_json.py +++ b/tools/raspberry_pi_imager/tests/test_build_pi_imager_json.py @@ -64,7 +64,7 @@ BOARD_DEVICES = { 'pi2': ['pi2-32bit'], - 'pi3': ['pi3-64bit'], + 'pi3': ['pi3-32bit'], 'pi4-64': ['pi4-64bit'], 'pi5': ['pi5-64bit'], }