Stage 1.17.1.5 local release assets and document OTAFIX 2.4.6
This commit is contained in:
1 parent
49dcba5c6e
commit
2630379365
4 files changed
+51
-3
No files matched your search
@@ -163,6 +163,9 @@ Bluetooth DFU updates the Companion; the separate Bluetooth MOTA source
|
||||
service feeds packages to other mesh nodes.
|
||||
|
||||
For OTAFIX installations, use the exact board/storage profile from
|
||||
[OTAFIX 2.4.5](https://github.com/mikecarper/Adafruit_nRF52_Bootloader_OTAFIX/releases/tag/0.11.0-OTAFIX2.4.5).
|
||||
Its release notes cover the Bluetooth reconnect fix and upgrades from older
|
||||
bootloaders, including the special UF2-drive precautions for version 2.4.3.
|
||||
[OTAFIX 2.4.6](https://github.com/mikecarper/Adafruit_nRF52_Bootloader_OTAFIX/releases/tag/0.11.0-OTAFIX2.4.6).
|
||||
It includes the Bluetooth reconnect fix from 2.4.5. Its release notes cover
|
||||
upgrades from older bootloaders, including the special UF2-drive precautions
|
||||
for version 2.4.3. The new 64 KiB retained-RAM staging feature applies to
|
||||
qualified internal-flash nRF52840 receivers; Full Companions continue to
|
||||
serve host-supplied packages without needing that receiver storage layout.
|
||||
@@ -43,6 +43,17 @@ offline queue while retaining its MOTA and transport features.
|
||||
Since 1.17.1.4, this branch also includes USB session and contact-persistence
|
||||
fixes, automatic nRF52 ExtraFS recovery, improved Indicator setup/display
|
||||
handling, compressed MOTA transport, and measured ESP32 memory limits.
|
||||
Qualified internal-flash nRF52840 receivers now reserve 64 KiB of retained
|
||||
RAM to extend application MOTA staging beyond the available flash prefix.
|
||||
This handoff requires the exact board's
|
||||
[OTAFIX 2.4.6 bootloader](https://github.com/mikecarper/Adafruit_nRF52_Bootloader_OTAFIX/releases/tag/0.11.0-OTAFIX2.4.6).
|
||||
Install that bootloader before relying on LoRa updates from these new hybrid
|
||||
receiver images: they reject application containers larger than one flash
|
||||
page when the installed bootloader lacks the retained-RAM capability.
|
||||
QSPI/microSD roles and Full Companions retain their existing storage layouts.
|
||||
An interrupted hybrid download must be fetched again after an application
|
||||
restart or power cycle. See the [protocol](../ota_protocol.md) for the exact
|
||||
admission and reset checks.
|
||||
|
||||
## Build using option 3
|
||||
|
||||
@@ -73,3 +84,13 @@ infrastructure. Those outputs do not belong in this OTA-required release.
|
||||
Use a separate output directory when changing the radio or settings profile.
|
||||
Use a fresh release output directory after changing the source revision or
|
||||
firmware version so packaging cannot mix old and new binaries.
|
||||
|
||||
## Local release staging
|
||||
|
||||
After the matrix completes successfully, `scripts/package_cascade_release.py`
|
||||
groups the qualified images using the previous GitHub release naming. Each
|
||||
group includes the firmware files, capability manifests, TSV/JSON inventory,
|
||||
checksums, build notes, and Full Companion directions. The staging directory's
|
||||
root `FIRMWARE-PICKER-1.17.1.5.html` opens the local downloads; the copies inside
|
||||
each release group use the planned GitHub download URLs. Staging does not
|
||||
push commits, create remote tags, or publish a GitHub release.
|
||||
@@ -161,6 +161,13 @@ def main():
|
||||
summaries.append({**manifest, "files": [path.name for path in record["files"]]})
|
||||
rows.append(f"<tr><td>{html.escape(manifest['artifact_target'])}</td><td>{html.escape(manifest['build_profile'])}</td><td>{html.escape(methods)}</td><td>{' · '.join(file_links)}</td></tr>")
|
||||
(destination / "TARGET-MANIFEST.json").write_text(json.dumps(summaries, indent=2) + "\n")
|
||||
columns = ("artifact_target", "target", "platform", "build_profile",
|
||||
"ota_update_methods", "files")
|
||||
(destination / "TARGET-MANIFEST.tsv").write_text(
|
||||
"\t".join(columns) + "\n" + "".join(
|
||||
"\t".join(",".join(row.get(key, [])) if isinstance(row.get(key), list)
|
||||
else str(row.get(key, "")) for key in columns) + "\n"
|
||||
for row in summaries))
|
||||
guide = (ROOT / "docs/full_companion_features.md").read_text()
|
||||
guide = re.sub(r"\]\(([^)]+)\)",
|
||||
lambda match: "](" + urljoin(source_url + "/docs/", match[1]) + ")", guide)
|
||||
@@ -178,6 +185,7 @@ def main():
|
||||
"Firmware and capability checks passed in the build matrix. Hardware update testing across every board was not performed.\n\n"
|
||||
+ links + "\n")
|
||||
(args.output / (group["key"] + "-notes.md")).write_text(body)
|
||||
(destination / "BUILD-NOTES.txt").write_text(body)
|
||||
picker = ("<!doctype html><meta charset=utf-8><meta name=viewport content='width=device-width'>"
|
||||
f"<title>MeshCore {html.escape(args.version)} USA Cascade</title>"
|
||||
"<style>body{font:16px system-ui;margin:2rem}input{font:inherit;padding:.5rem;width:90%;max-width:40rem}table{border-collapse:collapse;width:100%}td,th{text-align:left;padding:.7rem;border-bottom:1px solid #ccc}td:first-child{overflow-wrap:anywhere}a{color:#165acb}</style>"
|
||||
@@ -188,6 +196,15 @@ def main():
|
||||
"<table><thead><tr><th>Target</th><th>Profile</th><th>Self-update</th><th>Downloads</th></tr></thead><tbody>"
|
||||
+ "\n".join(rows) + "</tbody></table>"
|
||||
"<script>document.getElementById('search').oninput=function(){const q=this.value.toLowerCase();document.querySelectorAll('tbody tr').forEach(r=>r.hidden=!r.textContent.toLowerCase().includes(q))}</script>")
|
||||
local_picker = picker
|
||||
for group in groups:
|
||||
local_picker = local_picker.replace(
|
||||
f"https://github.com/{args.repo}/releases/download/{group['tag']}/",
|
||||
group["key"] + "/")
|
||||
local_picker = local_picker.replace(
|
||||
f"{source_url}/docs/full_companion_features.md",
|
||||
groups[0]["key"] + "/FULL-COMPANION-FEATURES.md")
|
||||
(args.output / f"FIRMWARE-PICKER-{args.version}.html").write_text(local_picker)
|
||||
for group in groups:
|
||||
destination = args.output / group["key"]
|
||||
(destination / f"FIRMWARE-PICKER-{args.version}.html").write_text(picker)
|
||||
|
||||
@@ -83,6 +83,13 @@ class ReleaseQualificationTest(unittest.TestCase):
|
||||
self.assertEqual(plan["groups"][0]["tag"], label + "-deadbeef")
|
||||
self.assertTrue(plan["groups"][0]["prerelease"])
|
||||
assets = directory / "stage/companion"
|
||||
picker = (directory / "stage/FIRMWARE-PICKER-1.17.1.5.html").read_text()
|
||||
self.assertIn(f'companion/{stem}.bin', picker)
|
||||
self.assertNotIn("/releases/download/", picker)
|
||||
self.assertIn("companion/FULL-COMPANION-FEATURES.md", picker)
|
||||
self.assertIn("/releases/download/", (assets / "FIRMWARE-PICKER-1.17.1.5.html").read_text())
|
||||
self.assertIn(stem + ".bin", (assets / "TARGET-MANIFEST.tsv").read_text())
|
||||
self.assertIn("910.525", (assets / "BUILD-NOTES.txt").read_text())
|
||||
for line in (assets / "SHA256SUMS.txt").read_text().splitlines():
|
||||
digest, name = line.split(" ", 1)
|
||||
self.assertEqual(hashlib.sha256((assets / name).read_bytes()).hexdigest(), digest)
|
||||
|
||||
Reference in new issue
Block a user