Refactor firmware flashing process by updating EspFlasher component to support new props for GitHub links and download functionality. Modify buildFlashParts to handle optional LittleFS images based on resetDeviceStorage flag. Enhance emit-flash-manifest.py to exclude factory images from USB bundles. Update CI workflows to improve artifact staging and documentation generation.

This commit is contained in:
Ben Allfree
2026-04-10 17:34:12 -07:00
parent 8618c201f2
commit a9e1e38b74
9 changed files with 546 additions and 349 deletions
+13 -42
View File
@@ -6,7 +6,9 @@ If BUILD_DIR/flash-manifest.json already exists (project-supplied), merge in
targetFamily from PlatformIO when missing.
Otherwise, if Meshtastic-style *.mt.json is present, synthesize a manifest
from partition table + on-disk artifacts.
from partition table + on-disk split artifacts (no *.factory.bin — USB bundles
omit the merged image). Only LittleFS rows use optional:true (Mesh Forge:
Reset device storage).
Optional args: PROJECT_ROOT TARGET_ENV — merged PIO config for that env fills
targetFamily (and platform/board) for the USB flasher UI.
@@ -148,20 +150,6 @@ def part_offset_for_slot(parts: list[dict], part_name: str) -> int | None:
return None
def factory_app_offset(parts: list[dict]) -> int:
for p in parts:
if str(p.get("subtype", "")) == "factory":
o = parse_offset(p.get("offset"))
if o is not None:
return o
for p in parts:
if p.get("type") == "app" and p.get("subtype") == "ota_0":
o = parse_offset(p.get("offset"))
if o is not None:
return o
return 0x10000
def ota1_offset(parts: list[dict]) -> int | None:
for p in parts:
if str(p.get("subtype", "")) == "ota_1":
@@ -179,27 +167,19 @@ def _offset_int(im: dict) -> int:
def _dedupe_same_offset(images: list[dict]) -> list[dict]:
"""
One esptool image per physical offset. Meshtastic maps both firmware-*.bin (app0)
and firmware-*.factory.bin to the same ota_0 slot — keep factory, drop the duplicate.
"""
"""One esptool image per physical offset (prefer non-optional, then lexicographic file)."""
buckets: dict[int, list[dict]] = {}
for im in images:
buckets.setdefault(_offset_int(im), []).append(im)
def rank(im: dict) -> tuple:
f = im["file"]
opt = im.get("optional") is True
if re.search(r"\.factory\.bin$", f, re.I):
return (0, 0 if not opt else 1, f)
if not opt:
return (1, 0, f)
return (2, 0, f)
out: list[dict] = []
for off in sorted(buckets):
group = buckets[off]
out.append(group[0] if len(group) == 1 else min(group, key=rank))
out.append(
group[0]
if len(group) == 1
else min(group, key=lambda im: (im.get("optional") is True, str(im.get("file", ""))))
)
return out
@@ -233,20 +213,11 @@ def emit_from_mt(build_dir: str, mt: dict) -> dict | None:
off = part_offset_for_slot(parts, str(part_name))
if off is None:
continue
opt = bool(
fname.startswith("littlefs-")
or re.match(r"^mt-.+-ota\.bin$", fname, re.I)
or (
re.match(r"^firmware-.+\.bin$", fname, re.I)
and not re.search(r"\.factory\.bin$", fname, re.I)
)
)
# Only LittleFS is optional in the bundle; Mesh Forge flashes it when the user enables
# "Reset device storage". Bootloader, partitions, app, and BLE OTA are always flashed when present.
opt = bool(fname.startswith("littlefs-"))
add(fname, off, optional=opt)
factory_bins = sorted(n for n in names if re.match(r"^firmware-.+\.factory\.bin$", n, re.I))
if factory_bins:
add(factory_bins[0], factory_app_offset(parts))
for n in sorted(names):
if n.startswith("littlefs-") and n.endswith(".bin"):
off = spiffs_offset(parts)
@@ -257,7 +228,7 @@ def emit_from_mt(build_dir: str, mt: dict) -> dict | None:
if re.match(r"^mt-.+-ota\.bin$", n, re.I):
off = ota1_offset(parts)
if off is not None:
add(n, off, optional=True)
add(n, off, optional=False)
if not images:
return None
+21
View File
@@ -0,0 +1,21 @@
#!/usr/bin/env bash
# Copy README and license files from repo root into the firmware tarball stage when present.
set -euo pipefail
ROOT="${1:?root dir}"
STAGE="${2:?stage dir}"
for f in \
README \
README.md \
README.rst \
README.txt \
readme.md \
LICENSE \
LICENSE.md \
LICENSE.txt \
COPYING \
COPYRIGHT; do
if [[ -f "$ROOT/$f" ]]; then
cp -a "$ROOT/$f" "$STAGE/"
fi
done