From daea0cc9f37f92154ca796bfd16cfa41e2a7308a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 21 Aug 2026 17:26:11 -0500 Subject: [PATCH] Warn on missing decode tools on both toolchains, fail the CI key resolver loudly The PlatformIO branch of _decode_pc now routes through the same rate-limited warning as the native one; raw undecoded crash addresses with no stated reason were undiagnosable at default log level. The CI cache key uses the assignment form so errexit catches a resolver failure instead of echo swallowing it into a degenerate key, with a non-empty check, and the cache comments stop claiming a ccache store the seed job never populates (it saves before any compile runs). --- .github/workflows/ci.yml | 17 ++++++++++++---- esphome/components/esp8266/__init__.py | 7 ++++++- .../esp8266/test_toolchain_validation.py | 20 +++++++++++++++++++ 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 83d3692b76..36c074003f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -199,7 +199,11 @@ jobs: id: esp8266-native-cache-key run: | . venv/bin/activate - echo "key=esp8266-native-$(python -c 'from esphome.components.esp8266 import RECOMMENDED_ARDUINO_FRAMEWORK_VERSION as f; from esphome.arduino8266.framework import TOOLCHAIN_VERSION as t; print(f"{f}-{t}")')" >> $GITHUB_OUTPUT + # Assignment form so errexit catches a resolver failure; a nested + # $(...) inside echo would silently yield a degenerate key + key=$(python -c 'from esphome.components.esp8266 import RECOMMENDED_ARDUINO_FRAMEWORK_VERSION as f; from esphome.arduino8266.framework import TOOLCHAIN_VERSION as t; print(f"{f}-{t}")') + [ -n "$key" ] || exit 1 + echo "key=esp8266-native-$key" >> $GITHUB_OUTPUT - name: Cache the native toolchain id: esp8266-native-cache uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 @@ -1187,7 +1191,8 @@ jobs: python-version: ${{ env.DEFAULT_PYTHON }} cache-key: ${{ needs.common.outputs.cache-key }} - # ~110 MB of framework + toolchain plus the ccache store. The versions + # ~110 MB of framework + toolchain (no ccache: the seed job saves + # before any compile runs, so the store would always be empty). The versions # are pinned in code, not in a hashable file, so resolve them for the # key (actions/cache never overwrites a key, so a bump must change it). # PRs are restore-only; the shared entry is seeded on pushes to dev by @@ -1196,9 +1201,13 @@ jobs: id: esp8266-native-cache-key run: | . venv/bin/activate - echo "key=esp8266-native-$(python -c 'from esphome.components.esp8266 import RECOMMENDED_ARDUINO_FRAMEWORK_VERSION as f; from esphome.arduino8266.framework import TOOLCHAIN_VERSION as t; print(f"{f}-{t}")')" >> $GITHUB_OUTPUT + # Assignment form so errexit catches a resolver failure; a nested + # $(...) inside echo would silently yield a degenerate key + key=$(python -c 'from esphome.components.esp8266 import RECOMMENDED_ARDUINO_FRAMEWORK_VERSION as f; from esphome.arduino8266.framework import TOOLCHAIN_VERSION as t; print(f"{f}-{t}")') + [ -n "$key" ] || exit 1 + echo "key=esp8266-native-$key" >> $GITHUB_OUTPUT - - name: Restore the native toolchain and ccache + - name: Restore the native toolchain uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: ~/.cache/esphome/arduino8266 diff --git a/esphome/components/esp8266/__init__.py b/esphome/components/esp8266/__init__.py index dafb9b3cc6..1bcc8c0ad3 100644 --- a/esphome/components/esp8266/__init__.py +++ b/esphome/components/esp8266/__init__.py @@ -649,7 +649,12 @@ def _decode_pc(config, addr): idedata = toolchain.get_idedata(config) if not idedata.addr2line_path or not idedata.firmware_elf_path: - _LOGGER.debug("decode_pc no addr2line") + # Same visibility as the native branch: raw undecoded addresses + # with no stated reason are undiagnosable at default log level + _warn_decode_problem( + "no-addr2line", + "Cannot decode crash addresses: no addr2line or ELF in idedata", + ) return addr2line, elf = idedata.addr2line_path, idedata.firmware_elf_path command = [addr2line, "-pfiaC", "-e", elf, addr] diff --git a/tests/unit_tests/components/esp8266/test_toolchain_validation.py b/tests/unit_tests/components/esp8266/test_toolchain_validation.py index 188169efb1..889f8d7666 100644 --- a/tests/unit_tests/components/esp8266/test_toolchain_validation.py +++ b/tests/unit_tests/components/esp8266/test_toolchain_validation.py @@ -139,6 +139,26 @@ def test_decode_pc_native_missing_tools_warns_once( esp8266._DECODE_WARNED_AT.clear() +def test_decode_pc_platformio_missing_tools_warns_once( + caplog: pytest.LogCaptureFixture, +) -> None: + """The PlatformIO branch reports a missing addr2line/ELF at the same + warning level as the native one; raw undecoded addresses with no + stated reason are undiagnosable at default log level.""" + from types import SimpleNamespace + + from esphome.components import esp8266 + + esp8266._DECODE_WARNED_AT.clear() + CORE.toolchain = Toolchain.PLATFORMIO + idedata = SimpleNamespace(addr2line_path=None, firmware_elf_path=None) + with patch("esphome.platformio.toolchain.get_idedata", return_value=idedata): + esp8266._decode_pc({}, "40201234") + esp8266._decode_pc({}, "40201238") + assert caplog.text.count("Cannot decode crash addresses") == 1 + esp8266._DECODE_WARNED_AT.clear() + + def test_resolve_toolchain_rejects_unsupported() -> None: """ESP8266 rejects a CLI toolchain it cannot serve, like every platform.""" from esphome.components.esp8266 import _resolve_toolchain