Match Halo Keymind Cascade dev release naming and include WM1110 DFU packages
This commit is contained in:
1 parent
02020513f4
commit
d4c8890eae
4 files changed
+84
-15
No files matched your search
+9
-1
@@ -28,4 +28,12 @@ env.AddCustomTarget(
|
||||
title="Create UF2 file",
|
||||
description="Use uf2conv to convert hex binary into uf2",
|
||||
always_build=True,
|
||||
)
|
||||
)
|
||||
|
||||
# Debug-probe upload defaults (for example WM1110's J-Link) must not omit the
|
||||
# application DFU ZIP from releases. Depend on the HEX builder so all its
|
||||
# post-actions, including EndF, finish before the DFU package is generated.
|
||||
# This does not change the board's selected upload protocol.
|
||||
if env.subst("$UPLOAD_PROTOCOL") != "nrfutil":
|
||||
dfu_package = env.PackageDfu("$BUILD_DIR/${PROGNAME}", firmware_hex)
|
||||
env.Alias("buildprog", dfu_package)
|
||||
@@ -1,4 +1,10 @@
|
||||
# MeshCore 1.17.1.5 — USA Cascade
|
||||
# MeshCore 1.17.1.5 Dev — USA Cascade
|
||||
|
||||
The firmware identifier and main release tag follow
|
||||
`v1.17.1.5-halo-keymind-cascade-dev-<commit>`, matching the 1.17.1.4 naming
|
||||
convention. Supplemental pages retain the `repeater-room-`, `utility-`,
|
||||
`logging-`, `lora-ota-`, and `full-profiles-` prefixes where those outputs
|
||||
exist. All pages are development prereleases.
|
||||
|
||||
This release uses the USA/Canada radio preset and Cascade runtime defaults.
|
||||
The release build records the resolved frequency, bandwidth, spreading factor,
|
||||
@@ -34,15 +40,16 @@ handling, compressed MOTA transport, and measured ESP32 memory limits.
|
||||
The equivalent reproducible command is:
|
||||
|
||||
```bash
|
||||
OUTPUT_DIR=.releases/1.17.1.5-usa-cascade \
|
||||
OUTPUT_DIR=.releases/1.17.1.5-halo-keymind-cascade-dev \
|
||||
bash build.sh build-firmwares-logging-matrix \
|
||||
--firmware-version v1.17.1.5 \
|
||||
--firmware-version v1.17.1.5-halo-keymind-cascade-dev \
|
||||
--radio-preset usa-cascadia --profile cascade \
|
||||
--require-ota --skip-kiss --resume --background
|
||||
```
|
||||
|
||||
Or run `bash build.sh`, select option 3, set version `v1.17.1.5`, and retain
|
||||
USA Cascadia/Cascade. Option 3 enables the infrastructure OTA requirement by
|
||||
Or run `bash build.sh`, select option 3, set version
|
||||
`v1.17.1.5-halo-keymind-cascade-dev`, and retain USA Cascadia/Cascade.
|
||||
Option 3 enables the infrastructure OTA requirement by
|
||||
default. It merges supported runtime USB logging into canonical artifacts and
|
||||
builds the expanded ESP32 profiles. It runs one PlatformIO process at a time.
|
||||
|
||||
|
||||
@@ -8,8 +8,9 @@ import hashlib
|
||||
import html
|
||||
import json
|
||||
from pathlib import Path
|
||||
import re
|
||||
import shutil
|
||||
from urllib.parse import quote
|
||||
from urllib.parse import quote, urljoin
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
@@ -79,7 +80,11 @@ def category(record):
|
||||
return "full-profiles"
|
||||
if "lora_ota" in manifest["target"].lower():
|
||||
return "lora-ota"
|
||||
return "infrastructure"
|
||||
if "logging" in manifest["artifact_target"].lower():
|
||||
return "logging"
|
||||
if any(role in manifest["target"].lower() for role in ("sensor", "terminal")):
|
||||
return "utility"
|
||||
return "repeater-room"
|
||||
|
||||
|
||||
def sha256(path):
|
||||
@@ -102,18 +107,22 @@ def main():
|
||||
output_directory = Path(status["working_directory"]) / status["output_directory"]
|
||||
if output_directory.resolve() != args.input.resolve():
|
||||
raise ValueError("build status belongs to another output directory")
|
||||
if status.get("source_commit") != args.commit or status.get("firmware_version") != "v" + args.version:
|
||||
firmware_label = f"v{args.version}-halo-keymind-cascade-dev"
|
||||
if status.get("source_commit") != args.commit or status.get("firmware_version") != firmware_label:
|
||||
raise ValueError("build status belongs to another source revision or version")
|
||||
if status.get("firmware_profile") != "cascade":
|
||||
raise ValueError("matrix did not use Cascade runtime defaults")
|
||||
radio = {key: status["radio_" + key] for key in ("frequency", "bandwidth", "sf", "cr")}
|
||||
if args.output.exists() and any(args.output.iterdir()):
|
||||
raise ValueError("staging directory must be empty; existing releases are never overwritten")
|
||||
version = f"v{args.version}-{args.commit[:8]}"
|
||||
version = f"{firmware_label}-{args.commit[:8]}"
|
||||
records = collect_artifacts(args.input, version)
|
||||
base_tag = f"v{args.version}-usa-cascade-{args.commit[:8]}"
|
||||
base_tag = version
|
||||
groups = []
|
||||
for name in ("companion", "infrastructure", "lora-ota", "full-profiles"):
|
||||
titles = {"companion": "Companion builds", "repeater-room": "Repeater and Room Server builds",
|
||||
"utility": "Sensor and Terminal utilities", "logging": "USB packet logging builds",
|
||||
"lora-ota": "LoRa OTA builds", "full-profiles": "Expanded FULL ESP32 profiles"}
|
||||
for name in titles:
|
||||
current = []
|
||||
count = 0
|
||||
chunks = []
|
||||
@@ -128,7 +137,10 @@ def main():
|
||||
for index, chunk in enumerate(chunks):
|
||||
key = name + (f"-{index + 1}" if index else "")
|
||||
tag = base_tag if key == "companion" else f"{key}-{base_tag}"
|
||||
groups.append({"key": key, "tag": tag, "records": chunk})
|
||||
title = f"MeshCore {args.version} Dev - {titles[name]}"
|
||||
if index:
|
||||
title += f" (part {index + 1})"
|
||||
groups.append({"key": key, "tag": tag, "title": title, "prerelease": True, "records": chunk})
|
||||
links = "\n".join(f"- [{g['key']}](https://github.com/{args.repo}/releases/tag/{g['tag']})" for g in groups)
|
||||
source_url = f"https://github.com/{args.repo}/blob/{args.commit}"
|
||||
rows = []
|
||||
@@ -149,11 +161,15 @@ 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")
|
||||
shutil.copy2(ROOT / "docs/full_companion_features.md", destination / "FULL-COMPANION-FEATURES.md")
|
||||
guide = (ROOT / "docs/full_companion_features.md").read_text()
|
||||
guide = re.sub(r"\]\(([^)]+)\)",
|
||||
lambda match: "](" + urljoin(source_url + "/docs/", match[1]) + ")", guide)
|
||||
(destination / "FULL-COMPANION-FEATURES.md").write_text(guide)
|
||||
exclusions = args.input / "ota-excluded-targets.txt"
|
||||
if exclusions.is_file():
|
||||
shutil.copy2(exclusions, destination / exclusions.name)
|
||||
body = (f"# MeshCore {args.version} — USA Cascade\n\n"
|
||||
body = (f"> **Development prerelease.** Firmware identifier: **{base_tag}**.\n\n"
|
||||
f"# MeshCore {args.version} Dev — USA Cascade\n\n"
|
||||
f"Source: `{args.commit}`. USA/Canada: **{radio['frequency']} MHz, BW{radio['bandwidth']}, SF{radio['sf']}, CR{radio['cr']}**; Cascade defaults.\n\n"
|
||||
f"This page contains **{len(summaries)} qualified firmware profiles**. Full Companions send MOTA and normally update over USB. Infrastructure has a verified wireless update path; see each capability manifest.\n\n"
|
||||
f"[Feature on/off and update directions]({source_url}/docs/full_companion_features.md) · "
|
||||
|
||||
@@ -4,6 +4,9 @@ import json
|
||||
from pathlib import Path
|
||||
import tempfile
|
||||
import unittest
|
||||
import subprocess
|
||||
import sys
|
||||
import hashlib
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
spec = importlib.util.spec_from_file_location("package_cascade_release", ROOT / "scripts/package_cascade_release.py")
|
||||
@@ -49,6 +52,41 @@ class ReleaseQualificationTest(unittest.TestCase):
|
||||
with self.assertRaisesRegex(ValueError, "pair incomplete"):
|
||||
package.collect_artifacts(directory, "v1.0-deadbeef")
|
||||
|
||||
def test_stages_named_prerelease_and_checksums_only_after_completion(self):
|
||||
with tempfile.TemporaryDirectory() as temp:
|
||||
directory = Path(temp)
|
||||
inputs = directory / "input"
|
||||
inputs.mkdir()
|
||||
commit = "deadbeef" + "0" * 32
|
||||
label = "v1.17.1.5-halo-keymind-cascade-dev"
|
||||
stem = "test_companion_radio_usb-" + label + "-deadbeef"
|
||||
manifest = self.manifest("test_companion_radio_usb", ota_update_verified=False,
|
||||
artifact_target="test_companion_radio_usb", build_profile="standard")
|
||||
(inputs / (stem + ".capabilities.json")).write_text(json.dumps(manifest))
|
||||
(inputs / (stem + ".bin")).write_bytes(b"test application")
|
||||
(inputs / (stem + "-merged.bin")).write_bytes(b"test merged image")
|
||||
status = directory / "status"
|
||||
settings = (f"exit_code=0\nworking_directory={directory}\noutput_directory=input\n"
|
||||
f"source_commit={commit}\nfirmware_version={label}\nfirmware_profile=cascade\n"
|
||||
"radio_frequency=910.525\nradio_bandwidth=62.5\nradio_sf=7\nradio_cr=5\n")
|
||||
status.write_text("state=running\n" + settings)
|
||||
command = [sys.executable, str(ROOT / "scripts/package_cascade_release.py"),
|
||||
"--input", str(inputs), "--output", str(directory / "stage"),
|
||||
"--build-status", str(status), "--commit", commit]
|
||||
result = subprocess.run(command, capture_output=True, text=True)
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertFalse((directory / "stage").exists())
|
||||
status.write_text("state=completed\n" + settings)
|
||||
result = subprocess.run(command, capture_output=True, text=True)
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
plan = json.loads((directory / "stage/release-plan.json").read_text())
|
||||
self.assertEqual(plan["groups"][0]["tag"], label + "-deadbeef")
|
||||
self.assertTrue(plan["groups"][0]["prerelease"])
|
||||
assets = directory / "stage/companion"
|
||||
for line in (assets / "SHA256SUMS.txt").read_text().splitlines():
|
||||
digest, name = line.split(" ", 1)
|
||||
self.assertEqual(hashlib.sha256((assets / name).read_bytes()).hexdigest(), digest)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user