diff --git a/.github/workflows/build-balena-disk-image.yaml b/.github/workflows/build-balena-disk-image.yaml index bba5d546c..ae4d38bed 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-32bit"]' ;; + 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..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 @@ -62,6 +62,14 @@ } +BOARD_DEVICES = { + 'pi2': ['pi2-32bit'], + 'pi3': ['pi3-32bit'], + '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 # ---------------------------------------------------------------------------