Link ESP32 picker to exact partition expansion packages
This commit is contained in:
1 parent
ba2187db2c
commit
fd7b93c52d
7 files changed
+296
-11
No files matched your search
@@ -1,5 +1,101 @@
|
||||
{
|
||||
"familyTag": "v1.17.1.7-halo-keymind-cascade-dev-2d03e098",
|
||||
"partitionMigrations": {
|
||||
"Ebyte_EoRa-S3_Repeater": "ebyte-eora-s3-repeater",
|
||||
"Ebyte_EoRa-S3_room_server": "ebyte-eora-s3-room-server",
|
||||
"Generic_E22_sx1262_repeater": "generic-e22-sx1262-repeater",
|
||||
"Generic_E22_sx1268_repeater": "generic-e22-sx1268-repeater",
|
||||
"Heltec_E213_repeater": "heltec-e213-repeater",
|
||||
"Heltec_E213_room_server": "heltec-e213-room-server",
|
||||
"Heltec_E290_repeater": "heltec-e290-repeater",
|
||||
"Heltec_E290_room_server": "heltec-e290-room-server",
|
||||
"Heltec_T190_repeater_": "heltec-t190-repeater",
|
||||
"Heltec_T190_room_server_": "heltec-t190-room-server",
|
||||
"Heltec_WSL3_repeater": "heltec-wsl3-repeater",
|
||||
"Heltec_WSL3_room_server": "heltec-wsl3-room-server",
|
||||
"Heltec_WSL3_sensor": "heltec-wsl3-sensor",
|
||||
"Heltec_Wireless_Paper_repeater": "heltec-wireless-paper-repeater",
|
||||
"Heltec_Wireless_Paper_room_server": "heltec-wireless-paper-room-server",
|
||||
"Heltec_Wireless_Tracker_repeater": "heltec-wireless-tracker-repeater",
|
||||
"Heltec_Wireless_Tracker_room_server": "heltec-wireless-tracker-room-server",
|
||||
"Heltec_ct62_repeater": "heltec-ct62-repeater",
|
||||
"Heltec_ct62_sensor": "heltec-ct62-sensor",
|
||||
"Heltec_v2_repeater": "heltec-v2-repeater",
|
||||
"Heltec_v2_room_server": "heltec-v2-room-server",
|
||||
"Heltec_v3_repeater": "heltec-v3-repeater",
|
||||
"Heltec_v3_room_server": "heltec-v3-room-server",
|
||||
"Heltec_v3_sensor": "heltec-v3-sensor",
|
||||
"LilyGo_T3S3_sx1262_repeater": "t3s3-sx1262-repeater",
|
||||
"LilyGo_T3S3_sx1262_room_server": "t3s3-sx1262-room-server",
|
||||
"LilyGo_T3S3_sx1276_repeater": "t3s3-sx1276-repeater",
|
||||
"LilyGo_T3S3_sx1276_room_server": "t3s3-sx1276-room-server",
|
||||
"LilyGo_TBeam_1W_repeater": "lilygo-tbeam-1w-repeater",
|
||||
"LilyGo_TBeam_1W_room_server": "lilygo-tbeam-1w-room-server",
|
||||
"LilyGo_TDeck_repeater": "lilygo-tdeck-repeater",
|
||||
"LilyGo_TETH_Elite_sx1262_repeater": "lilygo-teth-elite-sx1262-repeater",
|
||||
"LilyGo_TETH_Elite_sx1262_room_server": "lilygo-teth-elite-sx1262-room-server",
|
||||
"MKE_s3_repeater": "mke-s3-repeater",
|
||||
"MKE_s3_room_server": "mke-s3-room-server",
|
||||
"MKE_s3_sensor": "mke-s3-sensor",
|
||||
"RAK_3112_repeater": "rak-3112-repeater",
|
||||
"RAK_3112_room_server": "rak-3112-room-server",
|
||||
"RAK_3112_sensor": "rak-3112-sensor",
|
||||
"Station_G2_logging_repeater": "station-g2-logging-repeater",
|
||||
"Station_G2_repeater": "station-g2-repeater",
|
||||
"Station_G2_room_server": "station-g2-room-server",
|
||||
"Station_G3_ESP32_logging_repeater": "station-g3-esp32-logging-repeater",
|
||||
"Station_G3_ESP32_repeater": "station-g3-esp32-repeater",
|
||||
"Station_G3_ESP32_room_server": "station-g3-esp32-room-server",
|
||||
"T_Beam_S3_Supreme_SX1262_repeater": "t-beam-s3-supreme-sx1262-repeater",
|
||||
"T_Beam_S3_Supreme_SX1262_room_server": "t-beam-s3-supreme-sx1262-room-server",
|
||||
"Tenstar_C3_sx1262_repeater": "tenstar-c3-sx1262-repeater",
|
||||
"Tenstar_C3_sx1268_repeater": "tenstar-c3-sx1268-repeater",
|
||||
"ThinkNode_M2_Repeater": "thinknode-m2-repeater",
|
||||
"ThinkNode_M2_room_server": "thinknode-m2-room-server",
|
||||
"ThinkNode_M5_Repeater": "thinknode-m5-repeater",
|
||||
"ThinkNode_M5_room_server": "thinknode-m5-room-server",
|
||||
"ThinkNode_M7_repeater": "thinknode-m7-repeater",
|
||||
"ThinkNode_M7_room_server": "thinknode-m7-room-server",
|
||||
"ThinkNode_M9_repeater_": "thinknode-m9-repeater",
|
||||
"ThinkNode_M9_room_server_": "thinknode-m9-room-server",
|
||||
"Xiao_C3_repeater": "xiao-c3-repeater",
|
||||
"Xiao_C3_room_server": "xiao-c3-room-server",
|
||||
"Xiao_S3_WIO_repeater": "xiao-s3-wio",
|
||||
"Xiao_S3_WIO_room_server": "xiao-s3-wio-room-server",
|
||||
"Xiao_S3_WIO_sensor": "xiao-s3-wio-sensor",
|
||||
"Xiao_S3_repeater": "xiao-s3-repeater",
|
||||
"Xiao_S3_room_server": "xiao-s3-room-server",
|
||||
"Xiao_S3_sensor": "xiao-s3-sensor",
|
||||
"heltec_rc32_repeater": "heltec-rc32-repeater",
|
||||
"heltec_rc32_room_server": "heltec-rc32-room-server",
|
||||
"heltec_rc32_sensor": "heltec-rc32-sensor",
|
||||
"heltec_rc32_without_display_repeater": "heltec-rc32-no-display-repeater",
|
||||
"heltec_rc32_without_display_room_server": "heltec-rc32-no-display-room-server",
|
||||
"heltec_rc32_without_display_sensor": "heltec-rc32-no-display-sensor",
|
||||
"heltec_tracker_v2_repeater": "heltec-tracker-v2-repeater",
|
||||
"heltec_tracker_v2_room_server": "heltec-tracker-v2-room-server",
|
||||
"heltec_tracker_v2_sensor": "heltec-tracker-v2-sensor",
|
||||
"heltec_v4_expansionkit_repeater": "heltec-v4-expansionkit-repeater",
|
||||
"heltec_v4_r8_repeater": "heltec-v4-r8-repeater",
|
||||
"heltec_v4_r8_room_server": "heltec-v4-r8-room-server",
|
||||
"heltec_v4_r8_sensor": "heltec-v4-r8-sensor",
|
||||
"heltec_v4_r8_tft_repeater": "heltec-v4-r8-tft-repeater",
|
||||
"heltec_v4_r8_tft_room_server": "heltec-v4-r8-tft-room-server",
|
||||
"heltec_v4_r8_tft_sensor": "heltec-v4-r8-tft-sensor",
|
||||
"heltec_v4_repeater": "heltec-v4",
|
||||
"heltec_v4_room_server": "heltec-v4-room-server",
|
||||
"heltec_v4_sensor": "heltec-v4-sensor",
|
||||
"heltec_v4_tft_repeater": "heltec-v4-tft-repeater",
|
||||
"heltec_v4_tft_room_server": "heltec-v4-tft-room-server",
|
||||
"heltec_v4_tft_sensor": "heltec-v4-tft-sensor",
|
||||
"meshnology_w12_repeater": "meshnology-w12-repeater",
|
||||
"meshnology_w12_room_server": "meshnology-w12-room-server",
|
||||
"meshnology_w12_sensor": "meshnology-w12-sensor",
|
||||
"nibble_screen_connect_repeater_": "nibble-screen-repeater",
|
||||
"nibble_screen_connect_room_server_": "nibble-screen-room-server",
|
||||
"nibble_zero_connect_repeater_": "nibble-zero-repeater",
|
||||
"nibble_zero_connect_room_server_": "nibble-zero-room-server"
|
||||
},
|
||||
"profiles": {
|
||||
"Ebyte_EoRa-S3_Repeater": {
|
||||
"display": true,
|
||||
|
||||
@@ -779,6 +779,10 @@
|
||||
controlData.profiles && controlData.profiles[profile.target]) {
|
||||
profile.controls = controlData.profiles[profile.target];
|
||||
applyReleaseRuntimeCapabilities(profile, profile.controls);
|
||||
profile.migrationPackage = migrationPackageForProfile(
|
||||
profile, controlData.partitionMigrations
|
||||
);
|
||||
profile.migrationAsset = migrationAssetForProfile(profile, rows);
|
||||
if (isFullCompanion(profile) && profile.controls.mqtt &&
|
||||
!Array.isArray(profile.controls.loggingModes)) {
|
||||
profile.loggingModes = ["none", "usb", "wifi", "both"];
|
||||
@@ -1060,6 +1064,46 @@
|
||||
return url !== asset.releaseUrl && isSafeGithubUrl(url) ? url : "";
|
||||
}
|
||||
|
||||
function migrationPackageForProfile(profile, lookup) {
|
||||
if (!isExpandedEsp32Infrastructure(profile) || !lookup) return "";
|
||||
const target = profile.target.replace(/-full-(?:logging|usb-wifi)$/i, "");
|
||||
// The packages name canonical physical board/role identities. Observer
|
||||
// Full profiles use that same board/role's recipe; the package README
|
||||
// still governs the installed OTA target/layout. Preserve every hardware
|
||||
// variant (including logging hardware and trailing underscores).
|
||||
const canonical = target.replace(/_observer_mqtt(?=_?$)/i, "");
|
||||
const key = Object.prototype.hasOwnProperty.call(lookup, target)
|
||||
? target : canonical;
|
||||
const packageName = Object.prototype.hasOwnProperty.call(lookup, key)
|
||||
? lookup[key] : "";
|
||||
return typeof packageName === "string" && /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(packageName)
|
||||
? packageName : "";
|
||||
}
|
||||
|
||||
function migrationAssetForProfile(profile, rows) {
|
||||
if (!profile.migrationPackage) return null;
|
||||
const releaseUrl = migrationReleaseUrl(profile, profile.files[0]);
|
||||
if (!releaseUrl) return null;
|
||||
const name = profile.migrationPackage + "-" + profile.releaseFamily + "-migration.zip";
|
||||
const downloadUrl = releaseUrl.replace("/releases/tag/", "/releases/download/") +
|
||||
"/" + encodeURIComponent(name);
|
||||
const matches = rows.filter(function (row) {
|
||||
return row.name === name && row.releaseTag === "utility-" + profile.releaseFamily &&
|
||||
row.releaseUrl === releaseUrl && row.url === downloadUrl;
|
||||
});
|
||||
// Only offer a download actually published for this release and exact
|
||||
// board/role. Missing, unsafe, or ambiguous assets retain the fallback.
|
||||
return matches.length === 1 ? matches[0] : null;
|
||||
}
|
||||
|
||||
function migrationLink(profile, asset) {
|
||||
const fallback = migrationReleaseUrl(profile, asset);
|
||||
if (!fallback) return null;
|
||||
return profile.migrationAsset
|
||||
? { url: profile.migrationAsset.url, label: "Download exact partition-expansion ZIP" }
|
||||
: { url: fallback, label: "Check partition-expansion availability" };
|
||||
}
|
||||
|
||||
function installSteps(profile, kind) {
|
||||
const common = [
|
||||
"Verify that the hardware name and every displayed variant match the physical board.",
|
||||
@@ -1111,7 +1155,7 @@
|
||||
const extra = [];
|
||||
if (isExpandedEsp32Infrastructure(profile) && kind === "bin") {
|
||||
extra.push(
|
||||
"If the node still has a smaller layout, first flash the matching merged image over USB or, if listed, use the exact board/role migration ZIP from the utility release and follow its README. Never send this app-only image across a partition change."
|
||||
"If the node still has a smaller layout, first flash the matching merged image over USB or use the linked exact board/role migration ZIP, when available. Follow its README to check the installed OTA target and source layout before starting. Never send this app-only image across a partition change."
|
||||
);
|
||||
}
|
||||
if (profile.variant.includes("w25q16")) {
|
||||
@@ -1449,10 +1493,10 @@
|
||||
const release = createElement("a", "Open release notes");
|
||||
release.href = asset.releaseUrl;
|
||||
actions.appendChild(release);
|
||||
const migrationUrl = migrationReleaseUrl(profile, asset);
|
||||
if (migrationUrl) {
|
||||
const migration = createElement("a", "Check exact ESP32 migration ZIP");
|
||||
migration.href = migrationUrl;
|
||||
const migrationAction = migrationLink(profile, asset);
|
||||
if (migrationAction) {
|
||||
const migration = createElement("a", migrationAction.label);
|
||||
migration.href = migrationAction.url;
|
||||
actions.appendChild(migration);
|
||||
}
|
||||
card.appendChild(actions);
|
||||
@@ -1918,6 +1962,9 @@
|
||||
renderRuntimeDirections: renderRuntimeDirections,
|
||||
installSteps: installSteps,
|
||||
migrationReleaseUrl: migrationReleaseUrl,
|
||||
migrationPackageForProfile: migrationPackageForProfile,
|
||||
migrationAssetForProfile: migrationAssetForProfile,
|
||||
migrationLink: migrationLink,
|
||||
humanizeHardware: humanizeHardware,
|
||||
humanizeVariant: humanizeVariant,
|
||||
labelFor: labelFor,
|
||||
|
||||
+14
-4
@@ -17,7 +17,7 @@ The picker reads public release metadata from GitHub. It does not upload device
|
||||
information. Hardware names, target names, and download links come directly
|
||||
from the published firmware assets.
|
||||
|
||||
<div class="firmware-picker" data-firmware-picker data-release-repo="mikecarper/MeshCore" data-controls-url="../_data/firmware_controls.json?v=1.17.1.7-runtime-1" data-share-url="https://mikecarper.github.io/MeshCore/firmware_picker/">
|
||||
<div class="firmware-picker" data-firmware-picker data-release-repo="mikecarper/MeshCore" data-controls-url="../_data/firmware_controls.json?v=1.17.1.7-runtime-2" data-share-url="https://mikecarper.github.io/MeshCore/firmware_picker/">
|
||||
<div class="firmware-picker-intro" role="note">
|
||||
<strong>Current release set</strong>
|
||||
<p data-role="release-set">Loading release information...</p>
|
||||
@@ -29,9 +29,11 @@ from the published firmware assets.
|
||||
</p>
|
||||
<p>
|
||||
Upgrading an existing ESP32 infrastructure node from a smaller partition
|
||||
layout? The selected Full result links to the utility release. If an
|
||||
exact board and role migration ZIP is listed, follow its included README
|
||||
for a supported Wi-Fi or LoRa route. USB installation uses the matching
|
||||
layout? The selected Full result links directly to its exact board and
|
||||
role partition-expansion ZIP when published. Follow its included README
|
||||
to check the installed OTA target and source layout for a supported Wi-Fi
|
||||
or LoRa route. If no unique matching ZIP is available, the picker links
|
||||
to the utility release to check availability. USB installation uses the matching
|
||||
merged image instead. An app-only .bin cannot change the partition table.
|
||||
If the installed firmware supports it, run <code>get storage.layout</code>
|
||||
to see the live flash size and OTA slots. Firmware version alone does not
|
||||
@@ -481,6 +483,14 @@ python3 scripts/generate_picker_controls.py \
|
||||
Refresh the `data-controls-url` version query when publishing the generated
|
||||
metadata so browsers fetch the new release's controls.
|
||||
|
||||
The generated `partitionMigrations` lookup comes from `BOARDS` in
|
||||
`scripts/package_esp32_partition_migration.py`, the same exact board/role table
|
||||
used to build the migration packages. Expanded ESP32 Full results use this
|
||||
lookup (including canonical board/role recipes for Observer profiles), then
|
||||
require one matching published ZIP in the same release family's utility page.
|
||||
Hardware variants and roles remain distinct. Unlisted targets, missing or
|
||||
ambiguous assets, and stale metadata never produce a guessed download link.
|
||||
|
||||
For the downloadable version, save the release family's public GitHub release
|
||||
objects as a JSON array, then package the same picker UI and controls:
|
||||
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ extra_css:
|
||||
|
||||
extra_javascript:
|
||||
- https://unpkg.com/leaflet@1.9.4/dist/leaflet.js
|
||||
- _javascript/firmware_picker.js?v=20260927-3
|
||||
- _javascript/firmware_picker.js?v=20260927-4
|
||||
- _javascript/telemetry_decoder.js
|
||||
- _javascript/management_decoder.js
|
||||
- _javascript/filter_tool.js
|
||||
|
||||
@@ -9,9 +9,22 @@ import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
from package_esp32_partition_migration import BOARDS
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def partition_migrations():
|
||||
"""Use the packager's exact board/role lookup, not guessed ZIP slugs."""
|
||||
result = {}
|
||||
for package, spec in BOARDS.items():
|
||||
target = spec['target']
|
||||
if target in result:
|
||||
raise ValueError('Ambiguous partition migration target: ' + target)
|
||||
result[target] = package
|
||||
return result
|
||||
|
||||
|
||||
def capacity_note(reductions, defines):
|
||||
notes = []
|
||||
for reduction in reductions:
|
||||
@@ -141,7 +154,8 @@ def generate(stage, config):
|
||||
raise ValueError('Conflicting target metadata: ' + target)
|
||||
profiles[target] = controls
|
||||
family = next(g['tag'] for g in plan['groups'] if g['key'] == 'companion')
|
||||
return {'familyTag': family, 'source': plan['source'], 'profiles': profiles}
|
||||
return {'familyTag': family, 'source': plan['source'], 'profiles': profiles,
|
||||
'partitionMigrations': partition_migrations()}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -1134,6 +1134,110 @@ assert.strictEqual(commands(combinedDirections).filter(command => command === 's
|
||||
assert.strictEqual(commands(combinedDirections).filter(command => command === 'set bridge.enabled on').length, 1);
|
||||
console.log('current release metadata and capacity directions tests passed');
|
||||
|
||||
// Partition expansion uses the packager's lookup and published utility assets,
|
||||
// not hardware labels or guessed slugs (several historical names differ).
|
||||
const migrationFamily = currentControls.familyTag;
|
||||
const utilityTag = 'utility-' + migrationFamily;
|
||||
const utilityUrl = 'https://github.com/mikecarper/MeshCore/releases/tag/' + utilityTag;
|
||||
function migrationZip(packageName, tag = migrationFamily) {
|
||||
const name = packageName + '-' + tag + '-migration.zip';
|
||||
return {...asset(name), browser_download_url:
|
||||
'https://github.com/mikecarper/MeshCore/releases/download/utility-' + tag + '/' + name};
|
||||
}
|
||||
const migrationAssets = Object.values(currentControls.partitionMigrations).map(name => migrationZip(name));
|
||||
function migrationCatalog(assets = migrationAssets, metadata = currentControls, utilityOverrides = {}) {
|
||||
return picker.buildCatalog([
|
||||
release(migrationFamily, '2026-09-13T00:00:00Z', currentAssets),
|
||||
release(utilityTag, '2026-09-13T00:00:01Z', assets, utilityOverrides),
|
||||
], metadata);
|
||||
}
|
||||
const withMigrations = migrationCatalog();
|
||||
assert.strictEqual(withMigrations.rows.length, currentCatalog.rows.length,
|
||||
'Migration ZIPs must not become firmware/Serial DFU choices');
|
||||
const g2Target = 'Station_G2_repeater_observer_mqtt-full-usb-wifi';
|
||||
function g2MigrationLink(catalog) {
|
||||
const profile = catalog.profiles.find(item => item.target === g2Target);
|
||||
return picker.migrationLink(profile, profile.files[0]);
|
||||
}
|
||||
const directG2Link = {
|
||||
url: migrationZip('station-g2-repeater').browser_download_url,
|
||||
label: 'Download exact partition-expansion ZIP',
|
||||
};
|
||||
const fallbackMigrationLink = {
|
||||
url: utilityUrl, label: 'Check partition-expansion availability',
|
||||
};
|
||||
assert.deepStrictEqual(g2MigrationLink(withMigrations), directG2Link);
|
||||
const g2PickerUrl = 'https://mikecarper.github.io/MeshCore/firmware_picker/' +
|
||||
'?chipFamily=esp32&hardwareFamily=Station_G2&hardware=Station_G2&role=repeater&mode=espnow&install=bin&chipAuto=1';
|
||||
const g2Selection = picker.selectionFromUrl(g2PickerUrl, withMigrations.profiles);
|
||||
assert.deepStrictEqual(g2Selection.unavailable, []);
|
||||
const g2Selected = withMigrations.profiles.filter(profile =>
|
||||
picker.profileMatchesFacets(profile, g2Selection.filters));
|
||||
assert.deepStrictEqual(g2Selected.map(profile => profile.target), [g2Target]);
|
||||
assert.deepStrictEqual(picker.migrationLink(g2Selected[0], g2Selected[0].files[0]), directG2Link);
|
||||
|
||||
// Exercise every row in the packager lookup, even recipes whose canonical
|
||||
// image lives only inside its ZIP rather than the ordinary firmware matrix.
|
||||
for (const [target, packageName] of Object.entries(currentControls.partitionMigrations)) {
|
||||
for (const suffix of ['-full-logging', '-full-usb-wifi']) {
|
||||
const fullTarget = target + suffix;
|
||||
const profile = Object.assign(picker.parseTargetProfile(fullTarget), {
|
||||
releaseFamily: migrationFamily, controls: {platform: 'ESP32_PLATFORM'},
|
||||
files: [{releaseUrl: 'https://github.com/mikecarper/MeshCore/releases/tag/' + migrationFamily}],
|
||||
});
|
||||
profile.migrationPackage = picker.migrationPackageForProfile(profile, currentControls.partitionMigrations);
|
||||
profile.migrationAsset = picker.migrationAssetForProfile(profile, migrationAssets.map(item => ({
|
||||
name: item.name, url: item.browser_download_url, releaseTag: utilityTag, releaseUrl: utilityUrl,
|
||||
})));
|
||||
assert.strictEqual(profile.migrationPackage, packageName, fullTarget);
|
||||
assert.strictEqual(picker.migrationLink(profile, profile.files[0]).url,
|
||||
migrationZip(packageName).browser_download_url, fullTarget);
|
||||
}
|
||||
}
|
||||
const currentMigrationProfiles = withMigrations.profiles.filter(profile => profile.migrationPackage);
|
||||
assert(currentMigrationProfiles.length > 0);
|
||||
for (const profile of currentMigrationProfiles) {
|
||||
assert(profile.migrationAsset, profile.target);
|
||||
assert.strictEqual(picker.migrationLink(profile, profile.files[0]).url,
|
||||
migrationZip(profile.migrationPackage).browser_download_url, profile.target);
|
||||
}
|
||||
assert.strictEqual(picker.migrationLink(ordinaryEsp32, ordinaryEsp32.files[0]), null);
|
||||
assert.strictEqual(picker.migrationLink(ikokaNormal, ikokaNormal.files[0]), null);
|
||||
const unmappedVariant = withMigrations.profiles.find(profile =>
|
||||
profile.target === 'Station_G3_ESP32_r2_repeater_observer_mqtt-full-usb-wifi');
|
||||
assert(unmappedVariant);
|
||||
assert.deepStrictEqual(picker.migrationLink(unmappedVariant, unmappedVariant.files[0]), fallbackMigrationLink,
|
||||
'An unlisted board revision must not borrow another board recipe');
|
||||
|
||||
const noG2Assets = migrationAssets.filter(item => item.name !== migrationZip('station-g2-repeater').name);
|
||||
assert.deepStrictEqual(g2MigrationLink(migrationCatalog(noG2Assets)), fallbackMigrationLink,
|
||||
'Room-server and logging-hardware packages must not stand in for G2 repeater');
|
||||
assert.deepStrictEqual(g2MigrationLink(migrationCatalog([
|
||||
...noG2Assets, migrationZip('station-g2-repeater', family),
|
||||
])), fallbackMigrationLink, 'A ZIP for another release is not a match');
|
||||
assert.deepStrictEqual(g2MigrationLink(migrationCatalog(migrationAssets, currentControls,
|
||||
{tag_name: 'utility-' + family})), fallbackMigrationLink, 'Ignore other release families');
|
||||
assert.deepStrictEqual(g2MigrationLink(migrationCatalog(migrationAssets, currentControls,
|
||||
{tag_name: 'logging-' + migrationFamily})), fallbackMigrationLink, 'Require the utility page');
|
||||
assert.deepStrictEqual(g2MigrationLink(migrationCatalog([
|
||||
...migrationAssets, migrationZip('station-g2-repeater'),
|
||||
])), fallbackMigrationLink, 'Duplicate matches must fail closed');
|
||||
for (const badUrl of ['javascript:alert(1)', 'https://example.com/download.zip',
|
||||
migrationZip('station-g2-repeater').browser_download_url.replace('/mikecarper/', '/someone-else/'),
|
||||
migrationZip('station-g2-room-server').browser_download_url]) {
|
||||
assert.deepStrictEqual(g2MigrationLink(migrationCatalog([
|
||||
...noG2Assets, {...migrationZip('station-g2-repeater'), browser_download_url: badUrl},
|
||||
])), fallbackMigrationLink, badUrl);
|
||||
}
|
||||
assert.deepStrictEqual(g2MigrationLink(migrationCatalog(migrationAssets, currentControls,
|
||||
{html_url: utilityUrl.replace('/mikecarper/', '/someone-else/')})), fallbackMigrationLink);
|
||||
assert.deepStrictEqual(g2MigrationLink(migrationCatalog(migrationAssets,
|
||||
{...currentControls, partitionMigrations: undefined})), fallbackMigrationLink,
|
||||
'Older controls without a lookup retain a clearly labeled fallback');
|
||||
assert.strictEqual(g2MigrationLink(migrationCatalog(migrationAssets,
|
||||
{...currentControls, familyTag: family})), null, 'Stale controls must not supply a migration');
|
||||
console.log('exact partition-expansion links passed for all ' + migrationAssets.length + ' lookup recipes');
|
||||
|
||||
assert.strictEqual(nrf.chipFamily, 'nrf52');
|
||||
assert.strictEqual(mqttCompanion.chipFamily, 'esp32');
|
||||
assert.deepStrictEqual(picker.facetValues(controlled.profiles, {chipFamily: 'nrf52'}, 'hardwareFamily'), ['RAK_4631']);
|
||||
|
||||
@@ -1,16 +1,29 @@
|
||||
import importlib.util
|
||||
import json
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT / 'scripts'))
|
||||
SPEC = importlib.util.spec_from_file_location('picker_controls', ROOT / 'scripts/generate_picker_controls.py')
|
||||
GENERATOR = importlib.util.module_from_spec(SPEC)
|
||||
SPEC.loader.exec_module(GENERATOR)
|
||||
|
||||
|
||||
class PickerControlsTests(unittest.TestCase):
|
||||
def test_partition_migrations_follow_the_complete_packager_lookup(self):
|
||||
expected = {spec['target']: package
|
||||
for package, spec in GENERATOR.BOARDS.items()}
|
||||
self.assertEqual(len(expected), len(GENERATOR.BOARDS))
|
||||
self.assertEqual(GENERATOR.partition_migrations(), expected)
|
||||
checked_in = json.loads((ROOT / 'docs/_data/firmware_controls.json').read_text())
|
||||
self.assertEqual(checked_in['partitionMigrations'], expected)
|
||||
self.assertEqual(expected['Station_G2_repeater'], 'station-g2-repeater')
|
||||
self.assertEqual(expected['Station_G2_logging_repeater'], 'station-g2-logging-repeater')
|
||||
self.assertEqual(expected['nibble_zero_connect_room_server_'], 'nibble-zero-room-server')
|
||||
|
||||
def test_runtime_metadata_uses_verified_image_capabilities(self):
|
||||
source = 'a' * 40
|
||||
manifest = dict(source_commit=source,
|
||||
@@ -51,6 +64,7 @@ class PickerControlsTests(unittest.TestCase):
|
||||
(stage / 'companion/TARGET-MANIFEST.json').write_text(json.dumps([manifest]))
|
||||
config = [('env:sample', [('build_flags', list(flags))])]
|
||||
result = GENERATOR.generate(stage, config)
|
||||
self.assertEqual(result['partitionMigrations'], GENERATOR.partition_migrations())
|
||||
return result['profiles']['sample_companion_radio_full']
|
||||
|
||||
def test_compiled_capacity_overrides_unreduced_config_and_binds_repair_source(self):
|
||||
|
||||
Reference in new issue
Block a user