diff --git a/esphome/espidf/framework.py b/esphome/espidf/framework.py index 9d5d7f9c1a..b40ea8cd39 100644 --- a/esphome/espidf/framework.py +++ b/esphome/espidf/framework.py @@ -759,9 +759,11 @@ def _prefetch_idf_tool_archives( entries.append(entry) if not entries: return + cached = sum((dist_path / entry["dest"]).is_file() for entry in entries) _LOGGER.info( - "Downloading %d ESP-IDF tool archive(s): %s", + "Downloading %d ESP-IDF tool archive(s)%s: %s", len(entries), + f" ({cached} cached, verifying)" if cached else "", ", ".join(entry["name"] for entry in entries), ) diff --git a/esphome/espidf/install_tool_archives.py b/esphome/espidf/install_tool_archives.py index a1104d5c40..052080ca6b 100644 --- a/esphome/espidf/install_tool_archives.py +++ b/esphome/espidf/install_tool_archives.py @@ -88,9 +88,14 @@ def main() -> None: ex.submit(install_one, tool, name, version) for (name, version), tool in pending.items() ] + try: + results = [future.result() for future in futures] + except BaseException: # pragma: no cover + # Ctrl-C: drop queued extractions; in-flight ones finish whole + ex.shutdown(wait=True, cancel_futures=True) + raise # A survivor could fool the installer; every job failing is systematic. # Either way a nonzero exit makes the caller warn - results = [future.result() for future in futures] failed = sum(result is not True for result in results) if failed: print(f"{failed} of {len(results)} pre-extractions failed", file=sys.stderr) diff --git a/tests/unit_tests/test_espidf_framework.py b/tests/unit_tests/test_espidf_framework.py index 6dbb5ebe9f..1c15427712 100644 --- a/tests/unit_tests/test_espidf_framework.py +++ b/tests/unit_tests/test_espidf_framework.py @@ -1026,13 +1026,16 @@ def test_prefetch_downloads_archives_concurrently(tmp_path: Path) -> None: assert download.call_count == 6 -def test_prefetch_reverifies_already_downloaded_archives(tmp_path: Path) -> None: +def test_prefetch_reverifies_already_downloaded_archives( + tmp_path: Path, caplog: pytest.LogCaptureFixture +) -> None: """A pre-existing archive is not skipped: download_with_resume keeps it only when the sha256 matches, so the pre-extraction can trust it.""" dist = get_idf_tools_path() / "dist" dist.mkdir(parents=True) (dist / "cmake-3.30.2.tar.gz").write_bytes(b"cached") with ( + caplog.at_level(logging.INFO), patch( "esphome.espidf.framework.run_command", return_value=(True, _PREFETCH_JSON, ""), @@ -1046,6 +1049,8 @@ def test_prefetch_reverifies_already_downloaded_archives(tmp_path: Path) -> None dist / "cmake-3.30.2.tar.gz", dist / "ninja.zip", ] + # The log distinguishes verifying cached archives from real downloads + assert "Downloading 2 ESP-IDF tool archive(s) (1 cached, verifying)" in caplog.text @pytest.mark.parametrize(