Merge remote-tracking branch 'review/observer-firmware' into keymindCascade

# Conflicts:
#	.github/workflows/build-observer-firmwares.yml
#	.gitignore
#	MQTT_IMPLEMENTATION.md
#	MQTT_INTERNALS.md
#	README.md
#	STABILITY_TESTABILITY_HANDOFF.md
#	docs/cli_commands.md
#	examples/companion_radio/MyMesh.cpp
#	examples/simple_repeater/MyMesh.cpp
#	examples/simple_repeater/UITask.cpp
#	examples/simple_room_server/MyMesh.cpp
#	examples/simple_room_server/UITask.cpp
#	examples/simple_sensor/SensorMesh.cpp
#	platformio.ini
#	scripts/generate_webconfig_html.py
#	scripts/webconfig_mock_server.py
#	src/helpers/CommonCLI.cpp
#	src/helpers/CommonCLI.h
#	src/helpers/JWTHelper.cpp
#	src/helpers/MQTTDefaults.h
#	src/helpers/MQTTMessageBuilder.cpp
#	src/helpers/MQTTPayloadBuilder.h
#	src/helpers/MQTTPresets.h
#	src/helpers/bridges/MQTTBridge.h
#	src/helpers/esp32/WebConfigServer.cpp
#	src/helpers/esp32/WebConfigServer.h
#	src/helpers/ui/SH1106Display.cpp
#	variants/lilygo_tbeam_SX1262/platformio.ini
#	variants/lilygo_tbeam_SX1276/platformio.ini
#	variants/lilygo_tlora_v2_1/platformio.ini
#	variants/thinknode_m7/platformio.ini
#	webui/index.html
This commit is contained in:
mikecarper committed 2026-08-14 08:53:02 -07:00
commit 7605b12cc7
74 files changed
+7857 -2483

No files matched your search

@@ -38,11 +38,17 @@ on:
- 'LICENSE'
- '.vscode/**'
- '.claude/**'
# Throwaway build worktrees never affect the binaries.
- '.build-wt-*'
- '.build-wt-*/**'
# Shared with build-observer-firmwares.yml and sync-flasher-content.yml so the
# workflows never push to the flasher repo at the same time.
# Own group: serialize beta builds against each other only. Concurrency groups are
# repo-wide, not per-branch, so sharing one with the production workflows let an
# in-flight beta build force a production build into the pending state, where the
# next run queued into the group cancelled it outright. Concurrent flasher pushes
# are instead made safe by the rebase-retry in "Commit & Push Beta Artifacts".
concurrency:
group: flasher-publish
group: observer-firmware-beta-build
cancel-in-progress: false
env:
@@ -50,7 +56,7 @@ env:
# OTA comparison treats a different base version as "always an update", so a
# distinct base here would make every beta node think it is permanently behind.
# Channels are separated by manifest URL, not by base version.
FIRMWARE_VERSION: v1.16.0
FIRMWARE_VERSION: v1.17.0
# Beta-only rolling release. A separate tag is required, not cosmetic: the
# publish step prunes all but the KEEP_BUILDS most recent build hashes within
@@ -346,4 +352,26 @@ jobs:
git config user.name "meshcore-bot"
git config user.email "noreply@gessaman.com"
git commit -m "Update BETA observer firmware to ${{ steps.sha.outputs.short }} (build ${FIRMWARE_VERSION}.${{ needs.enumerate.outputs.build_number }})"
git push
# The production workflows can be pushing to the flasher repo right now
# (no shared concurrency group any more), so a non-fast-forward is expected
# rather than fatal: rebase onto their commit and retry. The scoped add
# above keeps this commit inside the beta paths, so a conflict with
# production's files is not possible.
br=$(git rev-parse --abbrev-ref HEAD)
for attempt in 1 2 3; do
if git push origin "HEAD:$br"; then
exit 0
fi
echo "push rejected (attempt $attempt); rebasing onto origin/$br"
git fetch origin "$br"
# --autostash: the scoped add above leaves unrelated edits in the
# checkout unstaged on purpose, and plain rebase refuses to run with a
# dirty tree. They stay unstaged, so they still never get published.
git rebase --autostash "origin/$br" || {
git rebase --abort || true
echo "::error::flasher rebase conflicted; re-run this workflow to republish"
exit 1
}
done
echo "::error::could not push beta changes after 3 attempts"
exit 1
+53 -15
View File
@@ -7,7 +7,7 @@ on:
workflow_dispatch:
push:
branches:
- mqtt-bridge-implementation-flex
- observer-firmware
# Only rebuild firmware when something that affects the binaries changes.
# Docs, the changelog, the changelog generator, CI files, and repo-meta
# files do not change firmware output - docs/changelog are handled by
@@ -25,19 +25,29 @@ on:
- 'LICENSE'
- '.vscode/**'
- '.claude/**'
# Throwaway build worktrees never affect the binaries.
- '.build-wt-*'
- '.build-wt-*/**'
# Serialize with sync-flasher-content.yml so the two workflows never push to the
# flasher repo at the same time (shared group name across both workflows).
# Own group: serialize builds against each other only. Do NOT share this group
# with sync-flasher-content.yml -- a mixed code+docs commit triggers both, and a
# run that is still pending when its sibling is queued gets cancelled, which
# silently skipped the firmware build. Concurrent flasher pushes are instead made
# safe by the rebase-retry in "Commit & Push Flasher Config".
concurrency:
group: flasher-publish
group: observer-firmware-build
cancel-in-progress: false
env:
# Version embedded in firmware filenames; must match the version key in the
# flasher's config.json. Bump here when the observer version changes.
FIRMWARE_VERSION: v1.16.0
FIRMWARE_VERSION: v1.17.0
# Rolling release tag that hosts the latest observer binaries.
RELEASE_TAG: observer-mqtt-latest
# Download host serving RELEASE_TAG's assets (cloudflare-worker in the flasher
# repo). Baked into the slim OTA manifests' file URLs; must stay consistent
# with config.json's staticPath.
STATIC_PATH: https://observer-fw.gessaman.com
jobs:
@@ -185,6 +195,13 @@ jobs:
# not published as release assets (and they'd break the hash-based prune).
gh release upload "$RELEASE_TAG" $(find out -maxdepth 1 -type f ! -name '*.partsig') --clobber
# Keep the release body in sync with the repo's notes source: the
# /releases feed serves it as the flasher dropdown's changelog
# (config.json no longer carries per-version notes). Non-fatal --
# stale notes beat a red build whose binaries are already live.
gh release edit "$RELEASE_TAG" --notes-file firmware-notes.html \
|| echo "WARNING: release notes sync failed" >&2
# Prune old assets, but RETAIN the most recent KEEP_BUILDS build hashes
# (not just the current one). The flasher SPA reads config.json once at
# page load and the embedded git short-hash is what links it to assets;
@@ -222,22 +239,23 @@ jobs:
token: ${{ secrets.FLASHER_DISPATCH_TOKEN }}
path: flasher
- name: Update Flasher Config
run: |
python3 flasher/scripts/update-firmware.py \
"${{ steps.sha.outputs.short }}" \
"$GITHUB_WORKSPACE/firmware-notes.html"
# update-firmware.py is no longer called here: config.json's observer
# entries are github release defs (no embedded filenames to rewrite), the
# flasher's versions come from the Worker's /releases feed, and the
# changelog notes ride the release body (see the publish step).
- name: Generate Slim Per-Variant Manifests + Persist Build Counter
env:
BUILD_NUMBER: ${{ needs.enumerate.outputs.build_number }}
run: |
# Derive the slim per-variant manifests (flasher/v/<env>.json) that the
# firmware fetches for `ota check`/`ota update`, from the just-updated
# config.json (single source of truth), stamping this build's number.
# Then persist the counter so the next run increments from here.
# firmware fetches for `ota check`/`ota update`, from the build output
# in out/ -- the assets actually uploaded to the release -- stamping this
# build's number. Then persist the counter so the next run increments
# from here.
python3 flasher/scripts/gen-slim-manifests.py \
--config flasher/config.json \
--bin-dir out \
--static-path "$STATIC_PATH" \
--out-dir flasher/v \
--base-version "$FIRMWARE_VERSION" \
--build "$BUILD_NUMBER" \
@@ -294,4 +312,24 @@ jobs:
git config user.name "meshcore-bot"
git config user.email "noreply@gessaman.com"
git commit -m "Update observer firmware to ${{ steps.sha.outputs.short }} (build ${FIRMWARE_VERSION}.${{ needs.enumerate.outputs.build_number }})"
git push
# sync-flasher-content.yml can be pushing the same docs/changelog right
# now (a mixed code+docs commit triggers both), so a non-fast-forward is
# expected rather than fatal: rebase onto the sibling's commit and retry.
# If the rebase leaves nothing, git reports up-to-date and we exit clean.
br=$(git rev-parse --abbrev-ref HEAD)
for attempt in 1 2 3; do
if git push origin "HEAD:$br"; then
exit 0
fi
echo "push rejected (attempt $attempt); rebasing onto origin/$br"
git fetch origin "$br"
# --autostash so a dirty tree can never make the rebase refuse to run;
# nothing unstaged gets published either way.
git rebase --autostash "origin/$br" || {
git rebase --abort || true
echo "::error::flasher rebase conflicted; re-run this workflow to republish"
exit 1
}
done
echo "::error::could not push flasher changes after 3 attempts"
exit 1
+45 -5
View File
@@ -14,16 +14,21 @@ on:
workflow_dispatch:
push:
branches:
- mqtt-bridge-implementation-flex
- observer-firmware
paths:
- '**.md'
- 'docs/**'
- 'scripts/gen_changelog.py'
# /webconfig frames the real portal page against a browser simulator, so
# the page itself is a synced doc asset like the .md files above.
- 'webui/index.html'
# Shared with build-observer-firmwares.yml so the two never push to the flasher
# repo concurrently (a mixed code+docs commit can trigger both).
# Own group: serialize doc syncs against each other only. Deliberately NOT shared
# with build-observer-firmwares.yml -- a shared group let one workflow cancel the
# other's pending run (a mixed code+docs commit triggers both). Concurrent flasher
# pushes are handled by the rebase-retry in "Commit & Push Flasher Content".
concurrency:
group: flasher-publish
group: flasher-docs-sync
cancel-in-progress: false
jobs:
@@ -57,6 +62,21 @@ jobs:
fi
done
- name: Sync the WebConfig Portal Page into Flasher
run: |
# flasher/webconfig.html embeds the REAL portal page in a device
# surround and runs it against lib/webconfig-sim.js, which intercepts
# fetch() so no device is needed. Syncing the page is what keeps that
# demo honest -- the alternative is screenshots, which go stale without
# anyone noticing. The build script only injects a <script> tag; it
# refuses a page that already carries the simulator.
if [ -f webui/index.html ]; then
python3 flasher/scripts/build-webconfig-demo.py webui/index.html
echo "rebuilt flasher/webconfig-demo.html from webui/index.html"
else
echo "WARNING: webui/index.html not found; leaving the demo as-is" >&2
fi
- name: Generate Changelog
run: |
# Append-only and idempotent; preserves the hand-curated history and
@@ -73,4 +93,24 @@ jobs:
git config user.name "meshcore-bot"
git config user.email "noreply@gessaman.com"
git commit -am "Sync docs & changelog from MeshCore ${GITHUB_SHA::7}"
git push
# build-observer-firmwares.yml can be pushing the same docs/changelog
# right now (a mixed code+docs commit triggers both), so a non-fast-forward
# is expected rather than fatal: rebase onto its commit and retry. If the
# rebase leaves nothing, git reports up-to-date and we exit clean.
br=$(git rev-parse --abbrev-ref HEAD)
for attempt in 1 2 3; do
if git push origin "HEAD:$br"; then
exit 0
fi
echo "push rejected (attempt $attempt); rebasing onto origin/$br"
git fetch origin "$br"
# --autostash so a dirty tree can never make the rebase refuse to run;
# nothing unstaged gets published either way.
git rebase --autostash "origin/$br" || {
git rebase --abort || true
echo "::error::flasher rebase conflicted; re-run this workflow to resync"
exit 1
}
done
echo "::error::could not push flasher changes after 3 attempts"
exit 1