Retire internal-only SolarXiao Room release variants

This commit is contained in:
mikecarper committed 2026-10-16 23:24:36 -07:00
1 parent 1dc2d75ace
commit bf07999e12
11 files changed
+373 -11

No files matched your search

+8 -5
View File
@@ -17,7 +17,8 @@ import tempfile
from build_esp32_partition_migration import bundle_release, verify_archive
from package_esp32_partition_migration import BOARDS, resolve_pio_build_dir
from package_cascade_release import category, collect_artifacts, nrf52_sensor_profile
from package_cascade_release import (category, collect_artifacts, nrf52_sensor_profile,
omit_internal_solar_room_records)
from station_g3_profiles import omit_redundant_slot_records
@@ -248,10 +249,11 @@ def select_ordinary_full_records(records: list[dict]) -> list[dict]:
Explicit alternate images and old partition identities still belong in
their separately built migration packages or direct --full-exact builds.
This also removes stale sibling artifacts when a matrix build is resumed.
nRF52 Full/reduced sensor OTA records always pass through independently,
even when their logical target ID and storage layout are the same. The
MeshTower SD primary replaces stale internal-only artifacts on resume;
those files must never be relabeled with the primary's different OTA ID.
nRF52 Full/reduced sensor OTA records pass through independently, even
when their logical target ID and storage layout are the same. MeshTower
SD and SolarXiao external-QSPI primaries replace stale internal-only
artifacts on resume; those files must never be relabeled with a primary's
different OTA ID.
"""
# Resumed directories can contain portable or transport-specific images
# from older release policies. They are recovery inputs, not ordinary
@@ -289,6 +291,7 @@ def select_ordinary_full_records(records: list[dict]) -> list[dict]:
records = omit_redundant_slot_records(
[record for record in records if ordinary_record(record)])
records = omit_internal_solar_room_records(records)
tower_primary = "heltec_tower_v2_sdcard_repeater_lora_ota_no_external_sensors"
tower_legacy = {
+37
View File
@@ -55,6 +55,42 @@ def nrf52_sensor_profile(manifest):
return next(iter(choices))
def omit_internal_solar_room_records(records):
"""Omit resumed internal-only Solar Room packages from ordinary releases.
Call after package qualification. Keep the original files and OTA
identities intact, and require each retired board's matching qualified
external-QSPI Full/Reduced pair before excluding its recovery packages.
"""
legacy_targets = {
"solarxiao_30s_room_server": "solarxiao_30s_room_server_lora_ota",
"solarxiao_33s_room_server": "solarxiao_33s_room_server_lora_ota",
}
targets = {record["manifest"]["target"].lower() for record in records}
for legacy, primary in legacy_targets.items():
if legacy not in targets:
continue
# collect_artifacts() has already checked the packaged OTA identity
# and layout-matched sensor pair. Do not let internal-only leftovers
# conceal a missing normal release pair for this exact hardware.
replacements = [record["manifest"] for record in records
if record["manifest"]["target"].lower() == primary]
error = (legacy + ": ordinary SolarXiao Room release requires the qualified "
"external-QSPI Full/Reduced pair for " + primary
+ "; internal-only artifacts cannot substitute for its distinct OTA identity")
try:
profiles = [nrf52_sensor_profile(manifest) for manifest in replacements]
except ValueError as cause:
raise ValueError(error) from cause
if (sorted(profiles) != ["full", "reduced"]
or any((manifest.get("ota_update_requirements") or {})
.get("lora", {}).get("storage") != "external_qspi"
for manifest in replacements)):
raise ValueError(error)
return [record for record in records
if record["manifest"]["target"].lower() not in legacy_targets]
def release_profile_label(manifest):
sensor = nrf52_sensor_profile(manifest)
if sensor:
@@ -378,6 +414,7 @@ def main():
raise ValueError("matrix did not use Cascade runtime defaults")
radio = {key: status["radio_" + key] for key in ("frequency", "bandwidth", "sf", "cr")}
records = collect_artifacts(args.input, version)
records = omit_internal_solar_room_records(records)
if args.output.exists() and any(args.output.iterdir()):
raise ValueError("staging directory must be empty; existing releases are never overwritten")
base_tag = version