mirror of
https://github.com/esphome/esphome.git
synced 2026-06-24 16:04:55 +00:00
[core] Fix clean-all to handle custom build paths (#15146)
Co-authored-by: J. Nick Koston <nick+github@koston.org>
This commit is contained in:
@@ -18,7 +18,6 @@ from esphome.core import CORE, EsphomeError
|
||||
from esphome.helpers import (
|
||||
copy_file_if_changed,
|
||||
cpp_string_escape,
|
||||
get_str_env,
|
||||
is_ha_addon,
|
||||
read_file,
|
||||
rmtree,
|
||||
@@ -441,18 +440,42 @@ def clean_build(clear_pio_cache: bool = True):
|
||||
rmtree(cache_dir)
|
||||
|
||||
|
||||
def _get_custom_build_dir(item: Path, data_dir: Path) -> Path | None:
|
||||
"""Parse a YAML config to find a custom build directory."""
|
||||
from esphome import yaml_util
|
||||
|
||||
try:
|
||||
raw = yaml_util.load_yaml(item)
|
||||
except (EsphomeError, OSError) as e:
|
||||
_LOGGER.debug("Could not parse %s to find build_path: %s", item, e)
|
||||
return None
|
||||
if not isinstance(raw, dict):
|
||||
return None
|
||||
esphome_conf = raw.get("esphome", {})
|
||||
if not isinstance(esphome_conf, dict):
|
||||
return None
|
||||
if build_path := esphome_conf.get("build_path"):
|
||||
return data_dir / build_path
|
||||
return None
|
||||
|
||||
|
||||
def clean_all(configuration: list[str]):
|
||||
data_dirs = []
|
||||
for config in configuration:
|
||||
item = Path(config)
|
||||
if item.is_file() and item.suffix in (".yaml", ".yml"):
|
||||
data_dirs.append(item.parent / ".esphome")
|
||||
data_dir = item.parent / ".esphome"
|
||||
data_dirs.append(data_dir)
|
||||
if custom := _get_custom_build_dir(item, data_dir):
|
||||
data_dirs.append(custom)
|
||||
else:
|
||||
data_dirs.append(item / ".esphome")
|
||||
if is_ha_addon():
|
||||
data_dirs.append(Path("/data"))
|
||||
if "ESPHOME_DATA_DIR" in os.environ:
|
||||
data_dirs.append(Path(get_str_env("ESPHOME_DATA_DIR", None)))
|
||||
if env_data_dir := os.environ.get("ESPHOME_DATA_DIR"):
|
||||
data_dirs.append(Path(env_data_dir))
|
||||
if env_build_path := os.environ.get("ESPHOME_BUILD_PATH"):
|
||||
data_dirs.append(Path(env_build_path))
|
||||
|
||||
# Clean build dir
|
||||
for dir in data_dirs:
|
||||
|
||||
@@ -866,6 +866,130 @@ def test_clean_all_with_yaml_file(
|
||||
assert str(build_dir) in caplog.text
|
||||
|
||||
|
||||
@patch("esphome.writer.CORE")
|
||||
def test_clean_all_with_yaml_build_path(
|
||||
mock_core: MagicMock,
|
||||
tmp_path: Path,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test clean_all cleans absolute build_path specified in YAML config."""
|
||||
config_dir = tmp_path / "config"
|
||||
config_dir.mkdir()
|
||||
|
||||
# Create an absolute custom build path directory with contents
|
||||
custom_build = tmp_path / "custom_build"
|
||||
custom_build.mkdir()
|
||||
(custom_build / "firmware.bin").write_text("x")
|
||||
sub = custom_build / "subdir"
|
||||
sub.mkdir()
|
||||
(sub / "file.txt").write_text("x")
|
||||
|
||||
yaml_file = config_dir / "test.yaml"
|
||||
# Absolute build_path: data_dir / absolute = absolute (Python Path behavior)
|
||||
yaml_file.write_text(f"esphome:\n name: test\n build_path: {custom_build}\n")
|
||||
|
||||
# Also create the normal .esphome dir
|
||||
build_dir = config_dir / ".esphome"
|
||||
build_dir.mkdir()
|
||||
(build_dir / "dummy.txt").write_text("x")
|
||||
|
||||
from esphome.writer import clean_all
|
||||
|
||||
with caplog.at_level("INFO"):
|
||||
clean_all([str(yaml_file)])
|
||||
|
||||
# Both .esphome and custom build_path should be cleaned
|
||||
assert build_dir.exists()
|
||||
assert not (build_dir / "dummy.txt").exists()
|
||||
assert custom_build.exists()
|
||||
assert not (custom_build / "firmware.bin").exists()
|
||||
assert not sub.exists()
|
||||
|
||||
|
||||
@patch("esphome.writer.CORE")
|
||||
def test_clean_all_with_yaml_parse_error(
|
||||
mock_core: MagicMock,
|
||||
tmp_path: Path,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test clean_all still cleans .esphome when YAML parse fails."""
|
||||
config_dir = tmp_path / "config"
|
||||
config_dir.mkdir()
|
||||
yaml_file = config_dir / "test.yaml"
|
||||
yaml_file.write_text("invalid: yaml: content: [")
|
||||
|
||||
build_dir = config_dir / ".esphome"
|
||||
build_dir.mkdir()
|
||||
(build_dir / "dummy.txt").write_text("x")
|
||||
|
||||
from esphome.writer import clean_all
|
||||
|
||||
with caplog.at_level("INFO"):
|
||||
clean_all([str(yaml_file)])
|
||||
|
||||
# .esphome should still be cleaned despite YAML parse failure
|
||||
assert build_dir.exists()
|
||||
assert not (build_dir / "dummy.txt").exists()
|
||||
|
||||
|
||||
@patch("esphome.writer.CORE")
|
||||
def test_clean_all_with_env_build_path(
|
||||
mock_core: MagicMock,
|
||||
tmp_path: Path,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
) -> None:
|
||||
"""Test clean_all cleans ESPHOME_BUILD_PATH directory."""
|
||||
config_dir = tmp_path / "config"
|
||||
config_dir.mkdir()
|
||||
|
||||
build_dir = config_dir / ".esphome"
|
||||
build_dir.mkdir()
|
||||
(build_dir / "dummy.txt").write_text("x")
|
||||
|
||||
# Create env build path directory
|
||||
env_build = tmp_path / "env_build"
|
||||
env_build.mkdir()
|
||||
(env_build / "firmware.bin").write_text("x")
|
||||
|
||||
from esphome.writer import clean_all
|
||||
|
||||
with (
|
||||
caplog.at_level("INFO"),
|
||||
patch.dict(os.environ, {"ESPHOME_BUILD_PATH": str(env_build)}),
|
||||
):
|
||||
clean_all([str(config_dir)])
|
||||
|
||||
# Both should be cleaned
|
||||
assert not (build_dir / "dummy.txt").exists()
|
||||
assert env_build.exists()
|
||||
assert not (env_build / "firmware.bin").exists()
|
||||
|
||||
|
||||
@patch("esphome.writer.CORE")
|
||||
def test_clean_all_ignores_empty_env_vars(
|
||||
mock_core: MagicMock,
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""Test clean_all ignores empty ESPHOME_BUILD_PATH/ESPHOME_DATA_DIR."""
|
||||
config_dir = tmp_path / "config"
|
||||
config_dir.mkdir()
|
||||
|
||||
# Create a file in cwd that must NOT be cleaned
|
||||
marker = tmp_path / "important.txt"
|
||||
marker.write_text("do not delete")
|
||||
|
||||
from esphome.writer import clean_all
|
||||
|
||||
with patch.dict(
|
||||
os.environ,
|
||||
{"ESPHOME_BUILD_PATH": "", "ESPHOME_DATA_DIR": ""},
|
||||
):
|
||||
clean_all([str(config_dir)])
|
||||
|
||||
# Empty env vars must not cause cwd to be cleaned
|
||||
assert marker.exists()
|
||||
|
||||
|
||||
@patch("esphome.writer.CORE")
|
||||
def test_clean_all(
|
||||
mock_core: MagicMock,
|
||||
|
||||
Reference in New Issue
Block a user