Consolidate same-partition ESP32 releases

This commit is contained in:
mikecarper
2026-09-18 14:20:59 -07:00
parent e0c75ddf7f
commit e232c8fc6f
2 changed files with 111 additions and 33 deletions
+73 -21
View File
@@ -2387,6 +2387,30 @@ get_unified_full_infrastructure_target() {
return 1
}
# A same-partition FULL release keeps the logical target's mOTA identity while
# it may compile the matching observer recipe for its MQTT/TLS/ESP-NOW sources.
# That lets a deployed ordinary role update to the complete image without
# treating an observer environment name as a different target. If a board has
# no matching observer recipe, its own source tree remains the Full build base.
get_exact_identity_full_pio_env() {
local env_name=$1
local candidate=""
is_esp32_full_only_bulk_target "$env_name" || {
printf '%s\n' "$env_name"
return 0
}
candidate=$(get_unified_full_infrastructure_target "$env_name") || candidate=""
if [ -n "$candidate" ] \
&& is_esp32_full_only_bulk_target "$candidate" \
&& [ "${PIO_ENV_BOARD_BY_NAME[$candidate]:-}" = "${PIO_ENV_BOARD_BY_NAME[$env_name]:-}" ]; then
printf '%s\n' "$candidate"
else
printf '%s\n' "$env_name"
fi
}
apply_esp32_full_shared_bridge_profile() {
local env_name=$1
@@ -2956,31 +2980,49 @@ supports_esp32_full_build() {
&& ! is_lora_ota_only_target "$env_name"
}
# These targets have an exact FULL partition identity and were audited to have
# no target-specific contact, neighbour, channel, or queue capacity reduction.
# In bulk release builds their portable image is therefore redundant: publish
# the FULL image under the same environment identity instead. Do not add an
# environment here unless both its partition history and capacity limits have
# been reviewed. An explicit `--standard` build remains the recovery route.
# These ESP32 targets keep their exact partition table when the FULL overlay is
# applied. In bulk release builds the portable artifact is therefore redundant:
# publish FULL under the same mOTA identity. This inventory is derived from the
# resolved board recipe, including per-environment partition overrides. An
# explicit `--standard` build remains the recovery route. Keep targets whose
# FULL build changes partitions out of this list: mOTA correctly rejects those
# migrations.
is_esp32_full_only_bulk_target() {
local env_name=${1,,}
[ "${PIO_ENV_PLATFORM_BY_NAME[$1]:-}" = "ESP32_PLATFORM" ] || return 1
case "$env_name" in
heltec_rc32_repeater|heltec_rc32_repeater_bridge_espnow|heltec_rc32_room_server|heltec_rc32_sensor|heltec_rc32_companion_radio_full|\
heltec_rc32_without_display_repeater|heltec_rc32_without_display_repeater_bridge_espnow|heltec_rc32_without_display_room_server|heltec_rc32_without_display_sensor|heltec_rc32_without_display_companion_radio_full|\
thinknode_m9_repeater_|thinknode_m9_room_server_|thinknode_m9_companion_radio_full_|\
station_g3_esp32_repeater|station_g3_esp32_logging_repeater|station_g3_esp32_room_server|station_g3_esp32_repeater_observer_mqtt|station_g3_esp32_room_server_observer_mqtt|station_g3_esp32_companion_radio_full|\
lilygo_teth_elite_sx1262_repeater|lilygo_teth_elite_sx1262_room_server|lilygo_teth_elite_sx1262_companion_radio_full|\
heltec_e290_repeater|heltec_e290_repeater_bridge_espnow|heltec_e290_room_server|\
heltec_v3_repeater|heltec_v3_repeater_bridge_rs232|heltec_v3_repeater_bridge_espnow|heltec_v3_repeater_observer_mqtt|heltec_v3_repeater_observer_mqtt_sim|heltec_v3_room_server|heltec_v3_room_server_observer_mqtt|heltec_v3_sensor|\
heltec_wsl3_repeater|heltec_wsl3_repeater_bridge_rs232|heltec_wsl3_repeater_bridge_espnow|heltec_wsl3_repeater_observer_mqtt|heltec_wsl3_room_server|heltec_wsl3_room_server_observer_mqtt|heltec_wsl3_sensor|\
lilygo_tbeam_1w_repeater|lilygo_tbeam_1w_repeater_bridge_espnow|lilygo_tbeam_1w_repeater_observer_mqtt|lilygo_tbeam_1w_room_server|lilygo_tbeam_1w_room_server_observer_mqtt|\
heltec_rc32_repeater|heltec_rc32_repeater_bridge_espnow|heltec_rc32_room_server|heltec_rc32_sensor|\
heltec_rc32_without_display_repeater|heltec_rc32_without_display_repeater_bridge_espnow|heltec_rc32_without_display_room_server|heltec_rc32_without_display_sensor|\
heltec_wireless_tracker_repeater|heltec_wireless_tracker_repeater_bridge_espnow|heltec_wireless_tracker_room_server|\
thinknode_m9_repeater_|thinknode_m9_room_server_|\
heltec_t190_repeater_|heltec_t190_repeater_observer_mqtt|heltec_t190_repeater_bridge_espnow_|heltec_t190_room_server_|heltec_t190_room_server_observer_mqtt|\
meshnology_w12_repeater|meshnology_w12_repeater_bridge_espnow|meshnology_w12_room_server|meshnology_w12_sensor|\
mke_s3_repeater|mke_s3_repeater_bridge_rs232|mke_s3_repeater_bridge_espnow|mke_s3_room_server|mke_s3_sensor|\
heltec_v2_repeater|heltec_v2_repeater_bridge_espnow|heltec_v2_room_server|heltec_v2_companion_radio_wifi|\
heltec_wireless_paper_repeater|heltec_wireless_paper_repeater_bridge_espnow|heltec_wireless_paper_room_server|\
heltec_tracker_v1_1_repeater_observer_mqtt|heltec_tracker_v1_1_room_server_observer_mqtt|\
heltec_tracker_v2_repeater|heltec_tracker_v2_repeater_bridge_espnow|heltec_tracker_v2_repeater_observer_mqtt|heltec_tracker_v2_room_server|heltec_tracker_v2_room_server_observer_mqtt|heltec_tracker_v2_sensor|\
t_beam_s3_supreme_sx1262_repeater|t_beam_s3_supreme_sx1262_repeater_bridge_espnow|t_beam_s3_supreme_sx1262_repeater_observer_mqtt|t_beam_s3_supreme_sx1262_room_server|t_beam_s3_supreme_sx1262_room_server_observer_mqtt|\
heltec_e213_repeater|heltec_e213_repeater_bridge_espnow|heltec_e213_room_server|\
station_g2_repeater_observer_mqtt|station_g2_room_server_observer_mqtt|\
sensecapindicator-lora-n16r2_companion_radio_full|\
t_beam_s3_supreme_sx1262_repeater|t_beam_s3_supreme_sx1262_repeater_bridge_espnow|t_beam_s3_supreme_sx1262_repeater_observer_mqtt|t_beam_s3_supreme_sx1262_room_server|t_beam_s3_supreme_sx1262_room_server_observer_mqtt|t_beam_s3_supreme_sx1262_companion_radio_full|\
thinknode_m7_repeater|thinknode_m7_room_server|thinknode_m7_repeater_observer_mqtt|thinknode_m7_room_server_observer_mqtt|thinknode_m7_companion_radio_full|\
why2025_badge_repeater_|why2025_badge_companion_radio_full_|\
sensecapindicator-lora_companion_radio_full|\
m5stack_unit_c6l_companion_radio_full|lilygo_tlora_c6_companion_radio_full_|meshimi_companion_radio_full_|xiao_c6_companion_radio_full_|\
lilygo_tlora_v2_1_1_6_repeater|lilygo_tlora_v2_1_1_6_room_server|lilygo_tlora_v2_1_1_6_repeater_bridge_rs232|lilygo_tlora_v2_1_1_6_repeater_bridge_espnow)
xiao_s3_repeater|xiao_s3_repeater_bridge_espnow|xiao_s3_room_server|xiao_s3_sensor|\
heltec_v4_repeater|heltec_v4_repeater_bridge_espnow|heltec_v4_repeater_observer_mqtt|heltec_v4_room_server|heltec_v4_room_server_observer_mqtt|heltec_v4_sensor|\
heltec_v4_expansionkit_repeater|heltec_v4_expansionkit_repeater_observer_mqtt|heltec_v4_expansionkit_room_server_observer_mqtt|\
heltec_v4_tft_repeater|heltec_v4_tft_repeater_bridge_espnow|heltec_v4_tft_room_server|heltec_v4_tft_sensor|\
lilygo_teth_elite_sx1262_repeater|lilygo_teth_elite_sx1262_room_server|\
station_g3_esp32_repeater|station_g3_esp32_logging_repeater|station_g3_esp32_room_server|station_g3_esp32_repeater_observer_mqtt|station_g3_esp32_room_server_observer_mqtt|\
rak_3112_repeater|rak_3112_repeater_bridge_rs232|rak_3112_repeater_bridge_espnow|rak_3112_repeater_observer_mqtt|rak_3112_room_server|rak_3112_room_server_observer_mqtt|rak_3112_sensor|\
xiao_s3_wio_repeater|xiao_s3_wio_repeater_bridge_espnow|xiao_s3_wio_repeater_observer_mqtt|xiao_s3_wio_room_server|xiao_s3_wio_room_server_observer_mqtt|xiao_s3_wio_sensor|\
lilygo_tdeck_repeater|\
thinknode_m7_repeater|thinknode_m7_room_server|thinknode_m7_repeater_observer_mqtt|thinknode_m7_room_server_observer_mqtt|\
heltec_v4_r8_repeater|heltec_v4_r8_repeater_observer_mqtt|heltec_v4_r8_room_server|heltec_v4_r8_room_server_observer_mqtt|heltec_v4_r8_sensor|\
heltec_v4_r8_tft_repeater|heltec_v4_r8_tft_repeater_observer_mqtt|heltec_v4_r8_tft_portrait_repeater_observer_mqtt|heltec_v4_r8_tft_room_server|heltec_v4_r8_tft_room_server_observer_mqtt|heltec_v4_r8_tft_portrait_room_server_observer_mqtt|heltec_v4_r8_tft_sensor)
return 0
;;
esac
@@ -3042,6 +3084,7 @@ declare_build_capability_contract() {
local env_platform=$2
local env_name_lc=${env_name,,}
local pio_env_name
local exact_identity_full_pio_env
declare_full_logging_application_contract "$env_name"
record_build_capability "profile.${BUILD_PROFILE_FOR_TARGET}"
@@ -3274,7 +3317,7 @@ apply_esp32_full_size_profile() {
return 0
fi
# The FULL artifact uses expanded dual-OTA slots, so restore features that
# The FULL artifact uses the board's dual-OTA slots, so restore features that
# target or legacy-slot profiles disabled only to save application space.
append_platformio_build_unflags "-DWEBCONFIG_DISABLED=1"
export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS} -UWEBCONFIG_DISABLED -DWIFI_OTA_SEEDER=1 -DMESHCORE_EXPANDED_PARTITION_PROFILE=1"
@@ -4296,11 +4339,20 @@ build_firmware() {
echo "Promoting ${env_name} to FULL so the complete CLI is retained."
fi
if [ "$ESP32_FULL_BUILD" = "1" ] \
&& is_esp32_full_only_bulk_target "$env_name"; then
exact_identity_full_pio_env=$(get_exact_identity_full_pio_env "$env_name")
if [ "$exact_identity_full_pio_env" != "$pio_env_name" ]; then
echo "FULL ${env_name} retains its mOTA identity while compiling observer feature base ${exact_identity_full_pio_env}."
pio_env_name=$exact_identity_full_pio_env
fi
fi
if [ "$ESP32_FULL_BUILD" = "1" ] \
|| is_companion_radio_full_target "$env_name"; then
BUILD_PROFILE_FOR_TARGET="full"
fi
configure_unified_full_infrastructure_output "$env_name"
configure_unified_full_infrastructure_output "$pio_env_name"
echo "Effective feature profile for ${env_name}: ${BUILD_PROFILE_FOR_TARGET}"
commit_hash=$(git rev-parse --short HEAD)
@@ -4393,7 +4445,7 @@ build_firmware() {
apply_esp32_lora_ota_size_profile "$env_name"
apply_esp32_constrained_companion_size_profile "$env_name"
apply_esp32_full_size_profile "$env_name"
apply_esp32_full_shared_bridge_profile "$env_name"
apply_esp32_full_shared_bridge_profile "$pio_env_name"
apply_esp32_full_async_tcp_profile "$env_name"
apply_repeater_neighbor_capacity "$env_name"
apply_nrf52_size_profile "$env_name"
+38 -12
View File
@@ -42,29 +42,36 @@ assert_auto_unified Heltec_T190_repeater_ Heltec_T190_repeater_observer_mqtt
assert_auto_unified LilyGo_TLora_V2_1_1_6_room_server LilyGo_TLora_V2_1_1_6_room_server_observer_mqtt_
assert_auto_unified heltec_v4_r8_repeater_observer_mqtt heltec_v4_r8_repeater_observer_mqtt
# Only targets whose exact FULL environment was audited for partition history
# and every capacity limit may omit their redundant portable bulk artifact.
# Only targets whose standard and FULL board recipes retain the exact same
# partition table may omit their redundant portable bulk artifact.
full_only_count=0
for target in "${!PIO_ENV_PLATFORM_BY_NAME[@]}"; do
if is_esp32_full_only_bulk_target "$target"; then
full_only_count=$((full_only_count + 1))
fi
done
[ "$full_only_count" -eq 47 ] || fail "expected 47 audited FULL-only targets, found $full_only_count"
[ "$full_only_count" -eq 129 ] || fail "expected 129 same-partition FULL-only targets, found $full_only_count"
for target in heltec_rc32_repeater Station_G2_repeater_observer_mqtt \
SenseCapIndicator-LoRa-N16R2_companion_radio_full \
LilyGo_Tlora_C6_companion_radio_full_ \
LilyGo_TLora_V2_1_1_6_repeater_bridge_espnow; do
is_esp32_full_only_bulk_target "$target" || fail "missed audited FULL-only target $target"
Heltec_v3_repeater heltec_v4_r8_repeater \
heltec_v4_tft_repeater RAK_3112_repeater; do
is_esp32_full_only_bulk_target "$target" || fail "missed same-partition FULL-only target $target"
done
for target in LilyGo_TLora_V2_1_1_6_repeater_observer_mqtt_ \
LilyGo_TLora_V2_1_1_6_companion_radio_wifi heltec_v4_r8_repeater; do
LilyGo_TLora_V2_1_1_6_companion_radio_wifi \
LilyGo_TLora_V2_1_1_6_repeater_bridge_espnow WHY2025_badge_repeater_; do
if is_esp32_full_only_bulk_target "$target"; then
fail "consolidated target with a capacity or partition caveat: $target"
fail "consolidated target with a partition migration: $target"
fi
done
[ "$(get_exact_identity_full_pio_env heltec_v4_r8_repeater)" \
= heltec_v4_r8_repeater_observer_mqtt ] \
|| fail "V4 R8 Full lost its observer feature base"
[ "$(get_exact_identity_full_pio_env heltec_v4_r8_sensor)" \
= heltec_v4_r8_sensor ] \
|| fail "V4 R8 sensor should retain its own feature base"
# Portable images and exact reduced OTA identities must not be consolidated.
for target in heltec_v4_r8_repeater heltec_v4_r8_room_server \
heltec_v4_r8_repeater_lora_ota_no_external_sensors; do
@@ -150,6 +157,24 @@ done
[[ "$observed_filename" == heltec_rc32_repeater-full-logging-ota-vtest-* ]] \
|| fail "FULL-only bulk target lost its exact identity"
)
(
ESP32_FULL_BUILD=0
BUILD_PROFILE_EFFECTIVE=standard
BUILD_PROFILE_OVERRIDE=auto
BUILD_PROFILE_EXPLICIT=0
BATCH_BUILD_MODE=1
FIRMWARE_VERSION=vtest
RESUME_BUILD_OUTPUT=1
FIRMWARE_FILENAME_INFIX=""
observed_filename=""
build_artifacts_exist() {
observed_filename=$2
return 0
}
build_firmware heltec_v4_r8_repeater >/dev/null
[[ "$observed_filename" == heltec_v4_r8_repeater-full-usb-wifi-ota-vtest-* ]] \
|| fail "V4 Full did not keep its identity and unified feature recipe"
)
(
ESP32_FULL_BUILD=0
BUILD_PROFILE_EFFECTIVE=standard
@@ -177,9 +202,10 @@ done
run_logging_matrix_build_targets heltec_v4_r8_repeater \
heltec_v4_r8_repeater_lora_ota_no_external_sensors \
heltec_v4_r8_repeater_observer_mqtt >/dev/null
[ "$standard_built" = 'heltec_v4_r8_repeater heltec_v4_r8_repeater_lora_ota_no_external_sensors' ] \
|| fail "matrix removed a portable build"
[ "$full_built" = heltec_v4_r8_repeater_observer_mqtt ] || fail "matrix duplicated Full"
[ "$standard_built" = heltec_v4_r8_repeater_lora_ota_no_external_sensors ] \
|| fail "matrix changed the distinct reduced OTA build"
[ "$full_built" = 'heltec_v4_r8_repeater heltec_v4_r8_repeater_observer_mqtt' ] \
|| fail "matrix did not retain V4's exact and legacy Full identities"
[ "$ESP32_LORA_OTA_APP_LIMIT" = 1310720 ] || fail "changed the 1.25 MiB portable limit"
)