Unlink a damaged build_info.json so regeneration completes, and log the replacement

The widened except left non-UTF-8 damage half-fixed: the staleness branch
fired, but write_file_if_changed then re-read the damaged file and raised
EsphomeError. Unlinking in the handler makes the recovery self-sufficient,
and the warning makes a repeatedly unreadable file visible instead of
silently costing a rebuild each run. The non-UTF-8 case now runs end to
end as a third row of the parametrized damage test, replacing the stubbed
decision-only test.
This commit is contained in:
J. Nick Koston
2026-08-23 09:40:35 -05:00
parent 7e179e6795
commit 8e4e541d7e
2 changed files with 9 additions and 33 deletions
+7 -2
View File
@@ -1,3 +1,4 @@
from contextlib import suppress
import importlib
import json
import logging
@@ -343,8 +344,12 @@ def copy_src_tree():
):
# Non-object JSON is stale like every other damage case
sources_changed = True
except (ValueError, OSError):
# ValueError covers both JSONDecodeError and UnicodeDecodeError
except (ValueError, OSError) as err:
# ValueError covers both JSONDecodeError and UnicodeDecodeError;
# unlink so the regenerating write never re-reads the damage
_LOGGER.warning("Replacing damaged build_info.json: %s", err)
with suppress(OSError):
build_info_json_path.unlink(missing_ok=True)
sources_changed = True
# Write build_info header and JSON metadata
+2 -31
View File
@@ -2118,8 +2118,8 @@ def test_copy_src_tree_detects_version_change(
@pytest.mark.parametrize(
"damage",
(b"invalid json {{{", b"[]"),
ids=("invalid-json", "non-object"),
(b"invalid json {{{", b"[]", b'\xff{"config_hash": 1}'),
ids=("invalid-json", "non-object", "non-utf8"),
)
@patch("esphome.writer.CORE")
@patch("esphome.writer.iter_components")
@@ -2143,35 +2143,6 @@ def test_copy_src_tree_regenerates_damaged_build_info(
assert new_json["config_hash"] == 0xDEADBEEF
@patch("esphome.writer.write_file_if_changed", return_value=False)
@patch("esphome.writer.CORE")
@patch("esphome.writer.iter_components")
@patch("esphome.writer.walk_files")
def test_copy_src_tree_non_utf8_build_info_reads_as_stale(
mock_walk_files: MagicMock,
mock_iter_components: MagicMock,
mock_core: MagicMock,
mock_write: MagicMock,
tmp_path: Path,
) -> None:
"""Non-UTF-8 build_info.json fires the staleness branch instead of raising.
Writes are stubbed (returning False, so nothing else can set
sources_changed): regenerating over the damaged file needs
write_file_if_changed's own recovery, which lands separately.
"""
build_info_json_path = _setup_build_info_mocks(
mock_core, mock_iter_components, mock_walk_files, tmp_path
)
core_path = tmp_path / "src" / "esphome" / "core"
(core_path / "build_info_data.h").write_text("// old")
(core_path / "build_info_data.cpp").write_text("// old")
build_info_json_path.write_bytes(b'\xff{"config_hash": 1}')
_run_copy_src_tree()
written = [call.args[0] for call in mock_write.call_args_list]
assert build_info_json_path in written
@patch("esphome.writer.CORE")
@patch("esphome.writer.iter_components")
@patch("esphome.writer.walk_files")