diff --git a/esphome/platformio/library.py b/esphome/platformio/library.py index af2010711f..e551d8f1c0 100644 --- a/esphome/platformio/library.py +++ b/esphome/platformio/library.py @@ -620,8 +620,13 @@ def _make_registry_client() -> Any: elsewhere, not by the PlatformIO registry. """ from platformio.package.manager._registry import PackageManagerRegistryMixin + from platformio.project.helpers import get_project_cache_dir from platformio.registry.client import RegistryClient + # PlatformIO creates its HTTP cache dir without exist_ok, so two builds + # making their first registry lookup at once race on it + (Path(get_project_cache_dir()) / "http").mkdir(parents=True, exist_ok=True) + class _Registry(PackageManagerRegistryMixin): def __init__(self) -> None: self.pkg_type = "library" diff --git a/tests/unit_tests/test_platformio_library.py b/tests/unit_tests/test_platformio_library.py index dba8977f1a..658c7211ab 100644 --- a/tests/unit_tests/test_platformio_library.py +++ b/tests/unit_tests/test_platformio_library.py @@ -247,6 +247,20 @@ def test_make_registry_client_skips_private_package_probe(monkeypatch): assert RegistryClient.__dict__["allowed_private_packages"] is pio_probe +def test_make_registry_client_creates_http_cache_dir( + monkeypatch: pytest.MonkeyPatch, tmp_path: Path +) -> None: + """The HTTP cache dir exists before PlatformIO's non-exist_ok makedirs runs.""" + from platformio.project import helpers + + monkeypatch.setattr(helpers, "get_project_cache_dir", lambda: str(tmp_path)) + + lib._make_registry_client() + assert (tmp_path / "http").is_dir() + # A second client (another build) must not trip over the existing dir + lib._make_registry_client() + + def _patch_registry_resolve(monkeypatch: pytest.MonkeyPatch) -> None: """Stub the registry lookup so tests never touch the network.""" monkeypatch.setattr(