Complete v1.17.1.1 release matrix support
This commit is contained in:
1 parent
8decf0ba85
commit
772607eba1
6 files changed
+4611
-4574
No files matched your search
@@ -1975,6 +1975,26 @@ requires_dram_limited_neighbors() {
|
||||
# ESP32 MQTT observers exhaust internal DRAM, while the 256 KiB STM32WL
|
||||
# targets exhaust their fixed application region because initialized table
|
||||
# storage is part of the image.
|
||||
#
|
||||
# Classic ESP32 bridge targets only need the lower limit when the expanded
|
||||
# FULL profile also enables packet logging. Keep their ordinary and
|
||||
# non-logging FULL profiles at the protocol maximum.
|
||||
if [ "$ESP32_FULL_BUILD" = "1" ] \
|
||||
&& [ "${PACKET_LOGGING_OVERRIDE,,}" = "on" ]; then
|
||||
case "${1,,}" in
|
||||
generic_e22_sx1262_repeater_bridge_espnow \
|
||||
|generic_e22_sx1268_repeater_bridge_espnow \
|
||||
|heltec_v2_repeater_bridge_espnow \
|
||||
|lilygo_tlora_v2_1_1_6_repeater_bridge_espnow \
|
||||
|lilygo_tlora_v2_1_1_6_repeater_bridge_rs232 \
|
||||
|meshadventurer_sx1262_repeater_bridge_espnow \
|
||||
|meshadventurer_sx1268_repeater_bridge_espnow \
|
||||
|tbeam_sx1262_repeater_bridge_espnow \
|
||||
|tbeam_sx1276_repeater_bridge_espnow)
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
case "${1,,}" in
|
||||
tbeam_sx1262_repeater_observer_mqtt|tbeam_sx1262_room_server_observer_mqtt|tbeam_sx1276_repeater_observer_mqtt|tbeam_sx1276_room_server_observer_mqtt|rak_3x72_repeater|tiny_relay_repeater|wio-e5-mini_repeater|wio-e5-repeater_bridge_rs232|wio-e5_repeater) return 0 ;;
|
||||
*) return 1 ;;
|
||||
|
||||
@@ -33,6 +33,26 @@ hardware guidance; validates every referenced artifact; routes URLs to the
|
||||
correct GitHub release page; and replaces legacy FULL MQTT choices with the
|
||||
matching portable MQTT observer builds.
|
||||
|
||||
The logging catalog tracks the diagnostic and expanded-partition profiles in
|
||||
the supplemental v1.17.1.1 release pages. Update it from the same completed
|
||||
matrix with:
|
||||
|
||||
```text
|
||||
python3 mesh-america/update-logging-provider-release.py \
|
||||
--release-dir /path/to/release \
|
||||
--artifact-version v1.17.1.1-759a35fc \
|
||||
--logging-main-tag logging-v1.17.1.1-halo-keymind-cascade-759a35fc \
|
||||
--logging-utility-tag logging-utility-v1.17.1.1-halo-keymind-cascade-759a35fc \
|
||||
--full-tag full-profiles-v1.17.1.1-halo-keymind-cascade-759a35fc \
|
||||
--main-tag v1.17.1.1-halo-keymind-cascade-759a35fc \
|
||||
--advanced-tag lora-ota-v1.17.1.1-halo-keymind-cascade-759a35fc \
|
||||
--utility-tag kiss-v1.17.1.1-halo-keymind-cascade-759a35fc
|
||||
```
|
||||
|
||||
This updater preserves the logging catalog's curated role and hardware
|
||||
guidance, validates all 648 referenced firmware identities, and selects the
|
||||
release page that owns each file.
|
||||
|
||||
The older PowerShell generator remains available for the historical standard
|
||||
and logging source folders:
|
||||
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
+247
@@ -0,0 +1,247 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Update the curated Mesh America logging catalog from a complete matrix."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import importlib.util
|
||||
import json
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
|
||||
SCRIPT_DIR = Path(__file__).resolve().parent
|
||||
COMMON_PATH = SCRIPT_DIR / "update-provider-release.py"
|
||||
|
||||
|
||||
def load_common():
|
||||
spec = importlib.util.spec_from_file_location("provider_release_common", COMMON_PATH)
|
||||
if spec is None or spec.loader is None:
|
||||
raise RuntimeError(f"cannot load {COMMON_PATH}")
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
sys.modules[spec.name] = module
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
common = load_common()
|
||||
CONSTRAINED_LOGGING_TARGETS = {
|
||||
"Tiny_Relay_repeater",
|
||||
"RAK_3x72_repeater",
|
||||
"wio-e5_repeater",
|
||||
"wio-e5-repeater_bridge_rs232",
|
||||
"wio-e5-mini_companion_radio_usb",
|
||||
"wio-e5-mini_repeater",
|
||||
"wio-e5-mini_sensor",
|
||||
}
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument(
|
||||
"--catalog",
|
||||
type=Path,
|
||||
default=SCRIPT_DIR / "keymind-cascade-logging-v1.16.0-provider.json",
|
||||
)
|
||||
parser.add_argument("--release-dir", type=Path, required=True)
|
||||
parser.add_argument("--artifact-version", required=True)
|
||||
parser.add_argument("--logging-main-tag", required=True)
|
||||
parser.add_argument("--logging-utility-tag", required=True)
|
||||
parser.add_argument("--full-tag", required=True)
|
||||
parser.add_argument("--main-tag", required=True)
|
||||
parser.add_argument("--advanced-tag", required=True)
|
||||
parser.add_argument("--utility-tag", required=True)
|
||||
parser.add_argument("--repo", default="mikecarper/MeshCore")
|
||||
parser.add_argument("--output", type=Path)
|
||||
parser.add_argument("--check-only", action="store_true")
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def is_logging_utility(identity: str) -> bool:
|
||||
lowered = identity.lower()
|
||||
return any(marker in lowered for marker in ("kiss_modem", "terminal_chat", "sensor"))
|
||||
|
||||
|
||||
def is_constrained_logging(identity: str) -> bool:
|
||||
return (
|
||||
identity.endswith("-logging")
|
||||
and identity.removesuffix("-logging") in CONSTRAINED_LOGGING_TARGETS
|
||||
)
|
||||
|
||||
|
||||
def release_tag(identity: str, args: argparse.Namespace) -> str:
|
||||
lowered = identity.lower()
|
||||
if "full-logging-ota" in lowered:
|
||||
return args.full_tag
|
||||
if "-logging" in lowered:
|
||||
return args.logging_utility_tag if is_logging_utility(identity) else args.logging_main_tag
|
||||
if (
|
||||
"companion_radio_full" in lowered
|
||||
or "lora_ota" in lowered
|
||||
or "observer_mqtt" in lowered
|
||||
):
|
||||
return args.advanced_tag
|
||||
if is_logging_utility(identity):
|
||||
return args.utility_tag
|
||||
if any(marker in lowered for marker in ("companion", "repeater", "room_server")):
|
||||
return args.main_tag
|
||||
return args.utility_tag
|
||||
|
||||
|
||||
def update_catalog(catalog: dict, release_files: dict[str, list[Path]], args: argparse.Namespace) -> dict:
|
||||
display_version = args.artifact_version.rsplit("-", 1)[0]
|
||||
catalog["description"] = (
|
||||
f"Keymind Cascade MeshCore {display_version} firmware with Halo/Keymind "
|
||||
"retry tuning, Cascade defaults, and the USA/Canada 910.525 MHz / SF7 / "
|
||||
"BW62.5 / CR5 preset. This catalog contains packet-logging builds with "
|
||||
"USB debug enabled except on seven flash-constrained STM32 targets, "
|
||||
"selected non-logging utility and portable MQTT observer builds, "
|
||||
"and expanded-partition FULL USB-logging LoRa-OTA builds. Host software "
|
||||
"can consume the USB serial log and publish it separately. Open Release "
|
||||
"notes for role, hardware, installation, and partition requirements."
|
||||
)
|
||||
|
||||
entry_count = 0
|
||||
file_count = 0
|
||||
used_identities: set[str] = set()
|
||||
category_counts = {
|
||||
"logging-main": 0,
|
||||
"logging-utility": 0,
|
||||
"full-logging": 0,
|
||||
"standard-main": 0,
|
||||
"standard-advanced": 0,
|
||||
"standard-utility": 0,
|
||||
}
|
||||
|
||||
for device in catalog["device"]:
|
||||
device_type = device["type"]
|
||||
for firmware in device["firmware"]:
|
||||
old_keys = list(firmware["version"])
|
||||
old_version = next(iter(firmware["version"].values()))
|
||||
old_notes = old_version["notes"]
|
||||
identities = common.ordered_catalog_identities(firmware)
|
||||
paths: list[Path] = []
|
||||
for identity in identities:
|
||||
if identity not in release_files:
|
||||
raise ValueError(f"no v1.17.1.1 artifact matches {identity!r}")
|
||||
used_identities.add(identity)
|
||||
paths.extend(release_files[identity])
|
||||
|
||||
lowered = identity.lower()
|
||||
if "full-logging-ota" in lowered:
|
||||
category_counts["full-logging"] += 1
|
||||
elif "-logging" in lowered:
|
||||
key = "logging-utility" if is_logging_utility(identity) else "logging-main"
|
||||
category_counts[key] += 1
|
||||
elif (
|
||||
"companion_radio_full" in lowered
|
||||
or "lora_ota" in lowered
|
||||
or "observer_mqtt" in lowered
|
||||
):
|
||||
category_counts["standard-advanced"] += 1
|
||||
elif is_logging_utility(identity):
|
||||
category_counts["standard-utility"] += 1
|
||||
else:
|
||||
category_counts["standard-main"] += 1
|
||||
|
||||
paths.sort(key=lambda path: common.file_sort_key(path, device_type))
|
||||
files = []
|
||||
for path in paths:
|
||||
identity = common.artifact_identity(path.name)
|
||||
file_type, title = common.file_type_and_title(path, device_type)
|
||||
files.append(
|
||||
{
|
||||
"type": file_type,
|
||||
"name": path.name,
|
||||
"url": common.download_url(
|
||||
args.repo,
|
||||
release_tag(identity, args),
|
||||
path.name,
|
||||
),
|
||||
"title": title,
|
||||
}
|
||||
)
|
||||
|
||||
version_key = common.replacement_version_key(old_keys[0], args.artifact_version)
|
||||
notes = common.replace_release_version(old_notes, display_version)
|
||||
if "USA/Canada 910.525 MHz" not in notes:
|
||||
notes = notes.replace(
|
||||
"with Halo/Keymind retry tuning and Cascade defaults.",
|
||||
"with Halo/Keymind retry tuning, Cascade defaults, and the "
|
||||
"USA/Canada 910.525 MHz / SF7 / BW62.5 / CR5 preset.",
|
||||
1,
|
||||
)
|
||||
if any(is_constrained_logging(identity) for identity in identities):
|
||||
old_profile = "turns USB debug and packet logging ON."
|
||||
standard_profile = (
|
||||
"normal features for the selected role and hardware, with USB "
|
||||
"debug/packet logging and LoRa self-update OFF."
|
||||
)
|
||||
replacement = (
|
||||
"turns packet logging ON while keeping USB debug OFF to fit this "
|
||||
"flash-constrained STM32 target."
|
||||
)
|
||||
if old_profile in notes:
|
||||
notes = notes.replace(old_profile, replacement, 1)
|
||||
elif standard_profile in notes:
|
||||
notes = notes.replace(
|
||||
standard_profile,
|
||||
standard_profile
|
||||
+ " The -logging variant turns packet logging ON while keeping "
|
||||
"USB debug OFF to fit this flash-constrained STM32 target.",
|
||||
1,
|
||||
)
|
||||
else:
|
||||
raise ValueError(
|
||||
"cannot update constrained logging guidance for "
|
||||
f"{identities!r}"
|
||||
)
|
||||
firmware["version"] = {version_key: {"notes": notes, "files": files}}
|
||||
entry_count += 1
|
||||
file_count += len(files)
|
||||
|
||||
if entry_count != 596:
|
||||
raise ValueError(f"expected 596 curated firmware entries, found {entry_count}")
|
||||
if len(used_identities) != 648:
|
||||
raise ValueError(f"expected 648 curated identities, found {len(used_identities)}")
|
||||
if file_count == 0:
|
||||
raise ValueError("catalog update produced no files")
|
||||
print(
|
||||
json.dumps(
|
||||
{
|
||||
"catalog_entries": entry_count,
|
||||
"catalog_files": file_count,
|
||||
"release_identities_used": len(used_identities),
|
||||
"identity_categories": category_counts,
|
||||
},
|
||||
sort_keys=True,
|
||||
)
|
||||
)
|
||||
return catalog
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = parse_args()
|
||||
output = args.output or args.catalog
|
||||
try:
|
||||
if not args.artifact_version.startswith("v"):
|
||||
raise ValueError("--artifact-version must start with 'v'")
|
||||
with args.catalog.open(encoding="utf-8") as stream:
|
||||
catalog = json.load(stream)
|
||||
release_files = common.index_release_files(args.release_dir, args.artifact_version)
|
||||
updated = update_catalog(catalog, release_files, args)
|
||||
if not args.check_only:
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
output.write_text(
|
||||
json.dumps(updated, ensure_ascii=True, indent=2) + "\n",
|
||||
encoding="ascii",
|
||||
)
|
||||
print(f"Wrote {output}")
|
||||
except (OSError, ValueError, KeyError, json.JSONDecodeError) as exc:
|
||||
print(f"error: {exc}", file=sys.stderr)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -129,6 +129,12 @@ def hardware_id_for_env(env_name: str) -> str:
|
||||
r"[_-](?:repeater|repeatr|room_server|room_svr|sensor|terminal_chat|kiss_modem|"
|
||||
r"companion_radio|companion|comp_radio)(?=[_-]|$)", env_name, re.IGNORECASE)
|
||||
family = (env_name[:role.start()] if role else env_name).strip("_-") or env_name.strip("_-")
|
||||
# Match the explicit MOTA_HW_ID shared by every role in these variant
|
||||
# families. The environment spelling is not always the physical-board tag.
|
||||
family = {
|
||||
"Heltec_t114_without_display": "Heltec_t114",
|
||||
"RAK_4631": "RAK4631",
|
||||
}.get(family, family)
|
||||
if len(family) <= 32:
|
||||
return family
|
||||
suffix = hashlib.sha256(family.encode()).hexdigest()[:8]
|
||||
|
||||
@@ -52,8 +52,12 @@ def test_ota_target_generation_honors_explicit_disable():
|
||||
|
||||
|
||||
def test_hardware_id_for_env():
|
||||
assert ml.hardware_id_for_env("RAK_4631_repeater") == "RAK_4631"
|
||||
assert ml.hardware_id_for_env("RAK_4631_companion_radio_usb") == "RAK_4631"
|
||||
assert ml.hardware_id_for_env("RAK_4631_repeater") == "RAK4631"
|
||||
assert ml.hardware_id_for_env("RAK_4631_companion_radio_usb") == "RAK4631"
|
||||
assert (
|
||||
ml.hardware_id_for_env("Heltec_t114_without_display_repeater")
|
||||
== "Heltec_t114"
|
||||
)
|
||||
assert ml.hardware_id_for_env("ThinkNode_M2_Repeater_bridge_espnow") == "ThinkNode_M2"
|
||||
assert ml.hardware_id_for_env("wio-e5-repeater_bridge_rs232") == "wio-e5"
|
||||
long_env = "ikoka_handheld_nrf_e22_30dbm_096_rotated_room_server"
|
||||
|
||||
Reference in new issue
Block a user