fix: harden portal OTA release

This commit is contained in:
2026-09-20 19:44:05 +10:00
parent 7c5b50499e
commit 360b7cff86
20 changed files with 621 additions and 198 deletions
+10 -8
View File
@@ -33,17 +33,19 @@ the shared [ESP8266 linker-workaround note](https://github.com/alexhopeoconnor/a
For the pioarduino release-to-Core mapping and cache-collision diagnosis, see
[DeviceFramework's toolchain guide](https://github.com/alexhopeoconnor/DeviceFramework/blob/main/docs/TOOLCHAINS.md).
`./scripts/test.sh` and `./tools/portal-hardware ota --platform esp32` place
the ESP32 A/B fixture,
in a dedicated PlatformIO Core/cache directory, defaulting to
`${XDG_CACHE_HOME:-$HOME/.cache}/wifimanager-platformio/core-3.3.11`. That
keeps pioarduino's package-form `esptool` and generated environment separate
`./scripts/test.sh` and `./tools/portal-hardware ota --platform esp32` use the
PlatformIO Core/cache shared by the maintained framework repositories,
defaulting to `${XDG_CACHE_HOME:-$HOME/.cache}/arduino-framework-platformio/core-3.3.11`.
WiFiManager, DeviceFramework, DFTE, and ArduinoHA pin this same graph, so this
avoids downloading the same Core 3.3.11 inputs for each repository while
keeping pioarduino's package-form `esptool` and generated environment separate
from stale global `tool-esptoolpy` metadata. Override the location with
`WIFIMANAGER_PLATFORMIO_CORE_DIR`,
`WIFIMANAGER_PLATFORMIO_PACKAGES_DIR`, and
`WIFIMANAGER_PLATFORMIO_CACHE_DIR` when space belongs elsewhere. The first
first install is several GiB; reserve at least 4 GiB plus cache headroom. It is
persistent and is never cleared by normal test commands.
`WIFIMANAGER_PLATFORMIO_CACHE_DIR` when space belongs elsewhere or an isolated
diagnosis is needed. The first shared install is several GiB; reserve at least
4 GiB plus cache headroom. It is persistent and is never cleared by normal test
commands.
For a disposable cache investigation, point that variable at an exact temporary
directory, run the affected command, inspect the resolved graph, then remove
+1 -1
View File
@@ -26,7 +26,7 @@ void loop() {
```ini
lib_deps =
WiFiManager=https://github.com/alexhopeoconnor/WiFiManager.git#v3.2.4
WiFiManager=https://github.com/alexhopeoconnor/WiFiManager.git#v3.2.5
```
The package includes the asynchronous web and TCP dependencies required by the selected ESP8266 or ESP32 target. Add WiFiManager as the application’s direct dependency; do not copy its internal dependency list into your project.
+19 -7
View File
@@ -7,7 +7,7 @@ local Wi-Fi credentials, browser binary, or sibling checkout.
| Physical test harness | Transport | Host adapter | Secret source | Required proof |
| --- | --- | --- | --- | --- |
| Portal lifecycle suite | Serial flash + captive-portal HTTP/browser | Named secondary adapter | safe fixture AP password | Unity/lifecycle checks and portal UI/API coverage |
| Portal HTTP OTA | WiFiManager multipart `POST /u` | Named secondary adapter | safe fixture AP password | serial A → updater accepts/completes B → serial B, plus browser automatic-reboot/B-twice proof |
| Portal HTTP OTA | WiFiManager multipart `POST /u` | Named secondary adapter | safe fixture AP password | rendered upload succeeds, portal restarts automatically, and fixture marker changes A → B twice |
The selected secondary adapter is intentionally never used for normal LAN
testing. It is `never-default`, so the host's ordinary route remains intact.
@@ -40,6 +40,16 @@ artifacts, an ESP32 image larger than either 0x1F0000-byte app slot, or a
partition-table edit that breaks the required two-slot/no-filesystem layout. These checks
intentionally do not require attached hardware, a local network, or Docker.
Direct PlatformIO test-harness commands default to two compiler jobs. Set
`PLATFORMIO_RUN_JOBS=3` only for an explicit local run on an otherwise idle
host.
Physical portal commands lock the shared `192.168.4.0/24` portal network, the
selected secondary adapter, and the named serial device. These non-secret
resource locks are shared with DeviceFramework's portal harness, so a collision
fails before either runner changes a board or adapter while unrelated station
tests can use their own resources.
## Local hardware lifecycle tests
The Unity suite runs portal-only firmware with no Wi-Fi credentials, MQTT,
@@ -242,8 +252,10 @@ HTTP route authentication.
The test harness performs the following complete run:
1. Builds immutable A and B fixture images. Their marker is compiled into the
binary, not saved in WiFiManager settings or EEPROM.
1. Builds immutable A and B fixture images from one platform environment. A
generated harness-only header in an ignored, per-run private directory is
the only changed input, so their marker is compiled into the binary rather
than saved in WiFiManager settings or EEPROM.
2. Checks both ESP32 images against the explicit matching `app0`/`app1` slots;
ESP8266 validates B after A has booted against the exact aligned capacity
passed to `Update.begin()`.
@@ -261,10 +273,10 @@ The OTA command additionally requires Python with PySerial (the
no-reset `serial-ota.log` beside the browser artifacts. It attaches immediately
after serial-flashing A releases the port—before portal association and the A
marker check—and remains attached through the two B checks. A passing run
requires the log's ordered immutable A marker, WiFiManager's update-start and
update-complete lines, then immutable B marker. This preserves firmware-side
portal-start and DHCP evidence as well as OTA evidence, without manufacturing a
reset. OTA-only fixture images wait five seconds after their upload reset so
requires a healthy recorder, but its contents are diagnostic evidence rather
than a pass/fail comparison against product log strings. This preserves
firmware-side portal-start and DHCP evidence without manufacturing a reset.
OTA-only fixture images wait five seconds after their upload reset so
the passive recorder can attach before A/B boot evidence is emitted; ordinary
portal test-harness startup remains fast.