Skip to content
This repository was archived by the owner on Jun 26, 2026. It is now read-only.

Fix webflasher DFU reliability, config UX, and batch EEPROM commits (… #32

Fix webflasher DFU reliability, config UX, and batch EEPROM commits (…

Fix webflasher DFU reliability, config UX, and batch EEPROM commits (… #32

Workflow file for this run

name: Release firmware
on:
push:
tags:
- 'v*'
workflow_dispatch:
# Least privilege at the top level: the build job only needs to read the
# repo and upload workflow artifacts (artifacts don't need contents:write).
# The publish job re-declares contents:write at the job level (below), so
# only that one job can edit releases / push to master.
permissions:
contents: read
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
build:
name: Build ${{ matrix.board }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
board:
- Faketec
- RAK4631
- XIAO_nRF52840
- Heltec_T114
- RAK3401
- T-Echo
- T1000E
steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Set up Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: '3.11'
- name: Cache PlatformIO toolchains and libraries
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: |
~/.cache/pip
~/.platformio/.cache
~/.platformio/packages
~/.platformio/platforms
key: ${{ runner.os }}-pio-${{ hashFiles('platformio.ini') }}
restore-keys: |
${{ runner.os }}-pio-
- name: Install PlatformIO Core
run: |
python -m pip install --upgrade pip
pip install --upgrade platformio
- name: Build ${{ matrix.board }}
run: pio run -e ${{ matrix.board }}
- name: Stage release assets
id: stage
run: |
set -euo pipefail
mkdir -p release-assets
if [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ]; then
VERSION="manual-$(date +%Y%m%d-%H%M%S)"
else
VERSION="${GITHUB_REF_NAME}"
fi
BOARD="${{ matrix.board }}"
BASE="reticulum-rnode-${BOARD}-${VERSION}"
cp ".pio/build/${BOARD}/firmware.zip" "release-assets/${BASE}.zip"
cp ".pio/build/${BOARD}/firmware.hex" "release-assets/${BASE}.hex"
python scripts/hex2uf2.py ".pio/build/${BOARD}/firmware.hex" "release-assets/${BASE}.uf2"
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "base=${BASE}" >> "$GITHUB_OUTPUT"
ls -la release-assets/
- name: Upload workflow artifacts
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: firmware-${{ matrix.board }}-${{ steps.stage.outputs.version }}
path: release-assets/*
retention-days: 30
publish:
needs: build
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
# Manual approval gate. This job publishes UNSIGNED firmware to the public
# web flasher (docs/firmware/ on GitHub Pages), so it must not run without
# a human in the loop. Configure the gate once in:
# Settings -> Environments -> "firmware-release" -> Required reviewers.
# Until a reviewer is added there, this only namespaces the job; ADD THE
# REVIEWER to actually arm the gate.
environment: firmware-release
permissions:
contents: write
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
GH_TOKEN: ${{ github.token }}
steps:
- name: Checkout master
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
ref: master
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: '3.11'
- name: Download all board artifacts
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
path: downloaded-artifacts
pattern: firmware-*
- name: Stage release assets (flat layout)
run: |
set -euo pipefail
mkdir -p release-assets
find downloaded-artifacts -type f \( -name '*.zip' -o -name '*.hex' -o -name '*.uf2' \) \
-exec cp -v {} release-assets/ \;
echo "Release assets staged:"
ls -la release-assets/
- name: Generate SHA256 checksums for all firmware artifacts
# The firmware is flashed UNSIGNED, so a checksums manifest is the only
# integrity reference a user (or the web flasher) has. Generated over
# every staged artifact and shipped both as a release asset and under
# docs/firmware/<tag>/ (copied by the staging step below, since it's
# inside release-assets/).
run: |
set -euo pipefail
cd release-assets
sha256sum * > SHA256SUMS.txt
echo "Checksums:"
cat SHA256SUMS.txt
- name: Delete existing Release for a clean slate
# Only delete when a release already exists for this tag (so the FIRST
# release of a tag doesn't fail), and make the substitution LOUD — a
# re-pushed tag replacing already-downloaded assets is a distribution-
# integrity foot-gun, so we warn rather than swallow it. No `|| true`:
# an unexpected delete failure now fails the job instead of hiding.
run: |
set -euo pipefail
if gh release view "${GITHUB_REF_NAME}" >/dev/null 2>&1; then
echo "::warning::Release ${GITHUB_REF_NAME} already exists — deleting and recreating. Assets published under this tag are being SUBSTITUTED."
gh release delete "${GITHUB_REF_NAME}" --yes --cleanup-tag=false
else
echo "No existing release for ${GITHUB_REF_NAME}; creating fresh."
fi
- name: Create GitHub Release with all board assets
run: |
set -euo pipefail
TAG="${GITHUB_REF_NAME}"
PRERELEASE_FLAG=""
if [[ "$TAG" == *-* ]]; then
PRERELEASE_FLAG="--prerelease"
fi
cat > release-body.md <<'EOF'
## Flashing
The easiest way to flash is the **web flasher** — visit
https://thatSFguy.github.io/reticulum-rnode/
and pick this version from the dropdown. No toolchain
install required, works in any Chromium-based browser.
## Manual flashing
Per-board assets attached to this release:
| File | How to use |
|-------------------------|-------------------------------------------------------------------------|
| `*.zip` | `adafruit-nrfutil dfu serial -pkg <zip> -p <port>`, or the web flasher |
| `*.uf2` | Drag-and-drop onto NICENANO/XIAOBOOT USB drive (double-tap reset first) |
| `*.hex` | nRFConnect Programmer, J-Link Commander, or any SWD tool |
Put the board in **bootloader mode** (double-tap reset within
~500 ms) before flashing over serial or UF2.
EOF
gh release create "$TAG" \
--title "$TAG" \
--notes-file release-body.md \
$PRERELEASE_FLAG \
release-assets/*
- name: Stage firmware under docs/firmware/<tag>/ for webflasher
run: |
set -euo pipefail
TAG="${GITHUB_REF_NAME}"
TARGET="docs/firmware/${TAG}"
mkdir -p "${TARGET}"
cp -v release-assets/* "${TARGET}/"
ls -la "${TARGET}"
- name: Regenerate manifest
run: python scripts/gen_firmware_manifest.py
- name: Commit webflasher assets and push
run: |
set -euo pipefail
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add docs/firmware/
if git diff --cached --quiet; then
echo "No firmware changes to commit."
exit 0
fi
git commit -m "ci: publish ${GITHUB_REF_NAME} firmware to docs/firmware/"
git push origin HEAD:master