1
0
mirror of https://github.com/esphome/esphome.git synced 2025-11-17 07:15:48 +00:00

[ci] Fix memory impact analysis to filter incompatible platform components (#11706)

This commit is contained in:
J. Nick Koston
2025-11-03 19:23:04 -06:00
committed by GitHub
parent 59326f137e
commit 6220084fe6
2 changed files with 140 additions and 1 deletions

View File

@@ -94,6 +94,22 @@ class Platform(StrEnum):
MEMORY_IMPACT_FALLBACK_COMPONENT = "api" # Representative component for core changes
MEMORY_IMPACT_FALLBACK_PLATFORM = Platform.ESP32_IDF # Most representative platform
# Platform-specific components that can only be built on their respective platforms
# These components contain platform-specific code and cannot be cross-compiled
# Regular components (wifi, logger, api, etc.) are cross-platform and not listed here
PLATFORM_SPECIFIC_COMPONENTS = frozenset(
{
"esp32", # ESP32 platform implementation
"esp8266", # ESP8266 platform implementation
"rp2040", # Raspberry Pi Pico / RP2040 platform implementation
"bk72xx", # Beken BK72xx platform implementation (uses LibreTiny)
"rtl87xx", # Realtek RTL87xx platform implementation (uses LibreTiny)
"ln882x", # Winner Micro LN882x platform implementation (uses LibreTiny)
"host", # Host platform (for testing on development machine)
"nrf52", # Nordic nRF52 platform implementation
}
)
# Platform preference order for memory impact analysis
# This order is used when no platform-specific hints are detected from filenames
# Priority rationale:
@@ -568,6 +584,20 @@ def detect_memory_impact_config(
)
platform = _select_platform_by_count(platform_counts)
# Filter out platform-specific components that are incompatible with selected platform
# Platform components (esp32, esp8266, rp2040, etc.) can only build on their own platform
# Other components (wifi, logger, etc.) are cross-platform and can build anywhere
compatible_components = [
component
for component in components_with_tests
if component not in PLATFORM_SPECIFIC_COMPONENTS
or platform in component_platforms_map.get(component, set())
]
# If no components are compatible with the selected platform, don't run
if not compatible_components:
return {"should_run": "false"}
# Debug output
print("Memory impact analysis:", file=sys.stderr)
print(f" Changed components: {sorted(changed_component_set)}", file=sys.stderr)
@@ -579,10 +609,11 @@ def detect_memory_impact_config(
print(f" Platform hints from filenames: {platform_hints}", file=sys.stderr)
print(f" Common platforms: {sorted(common_platforms)}", file=sys.stderr)
print(f" Selected platform: {platform}", file=sys.stderr)
print(f" Compatible components: {compatible_components}", file=sys.stderr)
return {
"should_run": "true",
"components": components_with_tests,
"components": compatible_components,
"platform": platform,
"use_merged_config": "true",
}

View File

@@ -1130,3 +1130,111 @@ def test_main_core_files_changed_still_detects_components(
assert "select" in output["changed_components"]
assert "api" in output["changed_components"]
assert len(output["changed_components"]) > 0
def test_detect_memory_impact_config_filters_incompatible_esp32_on_esp8266(
tmp_path: Path,
) -> None:
"""Test that ESP32 components are filtered out when ESP8266 platform is selected.
This test verifies the fix for the issue where ESP32 components were being included
when ESP8266 was selected as the platform, causing build failures in PR 10387.
"""
# Create test directory structure
tests_dir = tmp_path / "tests" / "components"
# esp32 component only has esp32-idf tests (NOT compatible with esp8266)
esp32_dir = tests_dir / "esp32"
esp32_dir.mkdir(parents=True)
(esp32_dir / "test.esp32-idf.yaml").write_text("test: esp32")
(esp32_dir / "test.esp32-s3-idf.yaml").write_text("test: esp32")
# esp8266 component only has esp8266-ard test (NOT compatible with esp32)
esp8266_dir = tests_dir / "esp8266"
esp8266_dir.mkdir(parents=True)
(esp8266_dir / "test.esp8266-ard.yaml").write_text("test: esp8266")
# Mock changed_files to return both esp32 and esp8266 component changes
# Include esp8266-specific filename to trigger esp8266 platform hint
with (
patch.object(determine_jobs, "root_path", str(tmp_path)),
patch.object(helpers, "root_path", str(tmp_path)),
patch.object(determine_jobs, "changed_files") as mock_changed_files,
):
mock_changed_files.return_value = [
"tests/components/esp32/common.yaml",
"tests/components/esp8266/test.esp8266-ard.yaml",
"esphome/core/helpers_esp8266.h", # ESP8266-specific file to hint platform
]
determine_jobs._component_has_tests.cache_clear()
result = determine_jobs.detect_memory_impact_config()
# Memory impact should run
assert result["should_run"] == "true"
# Platform should be esp8266-ard (due to ESP8266 filename hint)
assert result["platform"] == "esp8266-ard"
# CRITICAL: Only esp8266 component should be included, not esp32
# This prevents trying to build ESP32 components on ESP8266 platform
assert result["components"] == ["esp8266"], (
"When esp8266-ard platform is selected, only esp8266 component should be included, "
"not esp32. This prevents trying to build ESP32 components on ESP8266 platform."
)
assert result["use_merged_config"] == "true"
def test_detect_memory_impact_config_filters_incompatible_esp8266_on_esp32(
tmp_path: Path,
) -> None:
"""Test that ESP8266 components are filtered out when ESP32 platform is selected.
This is the inverse of the ESP8266 test - ensures filtering works both ways.
"""
# Create test directory structure
tests_dir = tmp_path / "tests" / "components"
# esp32 component only has esp32-idf tests (NOT compatible with esp8266)
esp32_dir = tests_dir / "esp32"
esp32_dir.mkdir(parents=True)
(esp32_dir / "test.esp32-idf.yaml").write_text("test: esp32")
(esp32_dir / "test.esp32-s3-idf.yaml").write_text("test: esp32")
# esp8266 component only has esp8266-ard test (NOT compatible with esp32)
esp8266_dir = tests_dir / "esp8266"
esp8266_dir.mkdir(parents=True)
(esp8266_dir / "test.esp8266-ard.yaml").write_text("test: esp8266")
# Mock changed_files to return both esp32 and esp8266 component changes
# Include MORE esp32-specific filenames to ensure esp32-idf wins the hint count
with (
patch.object(determine_jobs, "root_path", str(tmp_path)),
patch.object(helpers, "root_path", str(tmp_path)),
patch.object(determine_jobs, "changed_files") as mock_changed_files,
):
mock_changed_files.return_value = [
"tests/components/esp32/common.yaml",
"tests/components/esp8266/test.esp8266-ard.yaml",
"esphome/components/wifi/wifi_component_esp_idf.cpp", # ESP-IDF hint
"esphome/components/ethernet/ethernet_esp32.cpp", # ESP32 hint
]
determine_jobs._component_has_tests.cache_clear()
result = determine_jobs.detect_memory_impact_config()
# Memory impact should run
assert result["should_run"] == "true"
# Platform should be esp32-idf (due to more ESP32-IDF hints)
assert result["platform"] == "esp32-idf"
# CRITICAL: Only esp32 component should be included, not esp8266
# This prevents trying to build ESP8266 components on ESP32 platform
assert result["components"] == ["esp32"], (
"When esp32-idf platform is selected, only esp32 component should be included, "
"not esp8266. This prevents trying to build ESP8266 components on ESP32 platform."
)
assert result["use_merged_config"] == "true"