1
0
mirror of https://github.com/esphome/esphome.git synced 2025-09-28 08:02:23 +01:00

[core] Rename to clean-platform to clean-all (#10876)

This commit is contained in:
Jonathan Swoboda
2025-09-25 11:55:43 -04:00
committed by GitHub
parent 549626bee2
commit 74f09a2b59
5 changed files with 89 additions and 85 deletions

View File

@@ -731,11 +731,11 @@ def command_clean_mqtt(args: ArgsProtocol, config: ConfigType) -> int | None:
return clean_mqtt(config, args) return clean_mqtt(config, args)
def command_clean_platform(args: ArgsProtocol, config: ConfigType) -> int | None: def command_clean_all(args: ArgsProtocol) -> int | None:
try: try:
writer.clean_platform() writer.clean_all(args.configuration)
except OSError as err: except OSError as err:
_LOGGER.error("Error deleting platform files: %s", err) _LOGGER.error("Error cleaning all files: %s", err)
return 1 return 1
_LOGGER.info("Done!") _LOGGER.info("Done!")
return 0 return 0
@@ -931,6 +931,7 @@ PRE_CONFIG_ACTIONS = {
"dashboard": command_dashboard, "dashboard": command_dashboard,
"vscode": command_vscode, "vscode": command_vscode,
"update-all": command_update_all, "update-all": command_update_all,
"clean-all": command_clean_all,
} }
POST_CONFIG_ACTIONS = { POST_CONFIG_ACTIONS = {
@@ -941,7 +942,6 @@ POST_CONFIG_ACTIONS = {
"run": command_run, "run": command_run,
"clean": command_clean, "clean": command_clean,
"clean-mqtt": command_clean_mqtt, "clean-mqtt": command_clean_mqtt,
"clean-platform": command_clean_platform,
"mqtt-fingerprint": command_mqtt_fingerprint, "mqtt-fingerprint": command_mqtt_fingerprint,
"idedata": command_idedata, "idedata": command_idedata,
"rename": command_rename, "rename": command_rename,
@@ -951,7 +951,6 @@ POST_CONFIG_ACTIONS = {
SIMPLE_CONFIG_ACTIONS = [ SIMPLE_CONFIG_ACTIONS = [
"clean", "clean",
"clean-mqtt", "clean-mqtt",
"clean-platform",
"config", "config",
] ]
@@ -1156,11 +1155,9 @@ def parse_args(argv):
"configuration", help="Your YAML configuration file(s).", nargs="+" "configuration", help="Your YAML configuration file(s).", nargs="+"
) )
parser_clean = subparsers.add_parser( parser_clean_all = subparsers.add_parser("clean-all", help="Clean all files.")
"clean-platform", help="Delete all platform files." parser_clean_all.add_argument(
) "configuration", help="Your YAML configuration directory.", nargs="*"
parser_clean.add_argument(
"configuration", help="Your YAML configuration file(s).", nargs="+"
) )
parser_dashboard = subparsers.add_parser( parser_dashboard = subparsers.add_parser(
@@ -1209,7 +1206,7 @@ def parse_args(argv):
parser_update = subparsers.add_parser("update-all") parser_update = subparsers.add_parser("update-all")
parser_update.add_argument( parser_update.add_argument(
"configuration", help="Your YAML configuration file directories.", nargs="+" "configuration", help="Your YAML configuration file or directory.", nargs="+"
) )
parser_idedata = subparsers.add_parser("idedata") parser_idedata = subparsers.add_parser("idedata")

View File

@@ -479,10 +479,12 @@ class EsphomeCleanMqttHandler(EsphomeCommandWebSocket):
return [*DASHBOARD_COMMAND, "clean-mqtt", config_file] return [*DASHBOARD_COMMAND, "clean-mqtt", config_file]
class EsphomeCleanPlatformHandler(EsphomeCommandWebSocket): class EsphomeCleanAllHandler(EsphomeCommandWebSocket):
async def build_command(self, json_message: dict[str, Any]) -> list[str]: async def build_command(self, json_message: dict[str, Any]) -> list[str]:
config_file = settings.rel_path(json_message["configuration"]) clean_build_dir = json_message.get("clean_build_dir", True)
return [*DASHBOARD_COMMAND, "clean-platform", config_file] if clean_build_dir:
return [*DASHBOARD_COMMAND, "clean-all", settings.config_dir]
return [*DASHBOARD_COMMAND, "clean-all"]
class EsphomeCleanHandler(EsphomeCommandWebSocket): class EsphomeCleanHandler(EsphomeCommandWebSocket):
@@ -1319,7 +1321,7 @@ def make_app(debug=get_bool_env(ENV_DEV)) -> tornado.web.Application:
(f"{rel}compile", EsphomeCompileHandler), (f"{rel}compile", EsphomeCompileHandler),
(f"{rel}validate", EsphomeValidateHandler), (f"{rel}validate", EsphomeValidateHandler),
(f"{rel}clean-mqtt", EsphomeCleanMqttHandler), (f"{rel}clean-mqtt", EsphomeCleanMqttHandler),
(f"{rel}clean-platform", EsphomeCleanPlatformHandler), (f"{rel}clean-all", EsphomeCleanAllHandler),
(f"{rel}clean", EsphomeCleanHandler), (f"{rel}clean", EsphomeCleanHandler),
(f"{rel}vscode", EsphomeVscodeHandler), (f"{rel}vscode", EsphomeVscodeHandler),
(f"{rel}ace", EsphomeAceEditorHandler), (f"{rel}ace", EsphomeAceEditorHandler),

View File

@@ -335,13 +335,15 @@ def clean_build():
shutil.rmtree(cache_dir) shutil.rmtree(cache_dir)
def clean_platform(): def clean_all(configuration: list[str]):
import shutil import shutil
# Clean entire build dir # Clean entire build dir
if CORE.build_path.is_dir(): for dir in configuration:
_LOGGER.info("Deleting %s", CORE.build_path) buid_dir = Path(dir) / ".esphome"
shutil.rmtree(CORE.build_path) if buid_dir.is_dir():
_LOGGER.info("Deleting %s", buid_dir)
shutil.rmtree(buid_dir)
# Clean PlatformIO project files # Clean PlatformIO project files
try: try:

View File

@@ -17,7 +17,7 @@ from esphome import platformio_api
from esphome.__main__ import ( from esphome.__main__ import (
Purpose, Purpose,
choose_upload_log_host, choose_upload_log_host,
command_clean_platform, command_clean_all,
command_rename, command_rename,
command_update_all, command_update_all,
command_wizard, command_wizard,
@@ -1857,33 +1857,31 @@ esp32:
assert "can only concatenate str" not in clean_output assert "can only concatenate str" not in clean_output
def test_command_clean_platform_success( def test_command_clean_all_success(
caplog: pytest.LogCaptureFixture, caplog: pytest.LogCaptureFixture,
) -> None: ) -> None:
"""Test command_clean_platform when writer.clean_platform() succeeds.""" """Test command_clean_all when writer.clean_all() succeeds."""
args = MockArgs() args = MockArgs(configuration=["/path/to/config1", "/path/to/config2"])
config = {}
# Set logger level to capture INFO messages # Set logger level to capture INFO messages
with ( with (
caplog.at_level(logging.INFO), caplog.at_level(logging.INFO),
patch("esphome.writer.clean_platform") as mock_clean_platform, patch("esphome.writer.clean_all") as mock_clean_all,
): ):
result = command_clean_platform(args, config) result = command_clean_all(args)
assert result == 0 assert result == 0
mock_clean_platform.assert_called_once() mock_clean_all.assert_called_once_with(["/path/to/config1", "/path/to/config2"])
# Check that success message was logged # Check that success message was logged
assert "Done!" in caplog.text assert "Done!" in caplog.text
def test_command_clean_platform_oserror( def test_command_clean_all_oserror(
caplog: pytest.LogCaptureFixture, caplog: pytest.LogCaptureFixture,
) -> None: ) -> None:
"""Test command_clean_platform when writer.clean_platform() raises OSError.""" """Test command_clean_all when writer.clean_all() raises OSError."""
args = MockArgs() args = MockArgs(configuration=["/path/to/config1"])
config = {}
# Create a mock OSError with a specific message # Create a mock OSError with a specific message
mock_error = OSError("Permission denied: cannot delete directory") mock_error = OSError("Permission denied: cannot delete directory")
@@ -1891,30 +1889,27 @@ def test_command_clean_platform_oserror(
# Set logger level to capture ERROR and INFO messages # Set logger level to capture ERROR and INFO messages
with ( with (
caplog.at_level(logging.INFO), caplog.at_level(logging.INFO),
patch( patch("esphome.writer.clean_all", side_effect=mock_error) as mock_clean_all,
"esphome.writer.clean_platform", side_effect=mock_error
) as mock_clean_platform,
): ):
result = command_clean_platform(args, config) result = command_clean_all(args)
assert result == 1 assert result == 1
mock_clean_platform.assert_called_once() mock_clean_all.assert_called_once_with(["/path/to/config1"])
# Check that error message was logged # Check that error message was logged
assert ( assert (
"Error deleting platform files: Permission denied: cannot delete directory" "Error cleaning all files: Permission denied: cannot delete directory"
in caplog.text in caplog.text
) )
# Should not have success message # Should not have success message
assert "Done!" not in caplog.text assert "Done!" not in caplog.text
def test_command_clean_platform_oserror_no_message( def test_command_clean_all_oserror_no_message(
caplog: pytest.LogCaptureFixture, caplog: pytest.LogCaptureFixture,
) -> None: ) -> None:
"""Test command_clean_platform when writer.clean_platform() raises OSError without message.""" """Test command_clean_all when writer.clean_all() raises OSError without message."""
args = MockArgs() args = MockArgs(configuration=["/path/to/config1"])
config = {}
# Create a mock OSError without a message # Create a mock OSError without a message
mock_error = OSError() mock_error = OSError()
@@ -1922,34 +1917,33 @@ def test_command_clean_platform_oserror_no_message(
# Set logger level to capture ERROR and INFO messages # Set logger level to capture ERROR and INFO messages
with ( with (
caplog.at_level(logging.INFO), caplog.at_level(logging.INFO),
patch( patch("esphome.writer.clean_all", side_effect=mock_error) as mock_clean_all,
"esphome.writer.clean_platform", side_effect=mock_error
) as mock_clean_platform,
): ):
result = command_clean_platform(args, config) result = command_clean_all(args)
assert result == 1 assert result == 1
mock_clean_platform.assert_called_once() mock_clean_all.assert_called_once_with(["/path/to/config1"])
# Check that error message was logged (should show empty string for OSError without message) # Check that error message was logged (should show empty string for OSError without message)
assert "Error deleting platform files:" in caplog.text assert "Error cleaning all files:" in caplog.text
# Should not have success message # Should not have success message
assert "Done!" not in caplog.text assert "Done!" not in caplog.text
def test_command_clean_platform_args_and_config_ignored() -> None: def test_command_clean_all_args_used() -> None:
"""Test that command_clean_platform ignores args and config parameters.""" """Test that command_clean_all uses args.configuration parameter."""
# Test with various args and config to ensure they don't affect the function # Test with different configuration paths
args1 = MockArgs(name="test1", file="test.bin") args1 = MockArgs(configuration=["/path/to/config1"])
config1 = {"wifi": {"ssid": "test"}} args2 = MockArgs(configuration=["/path/to/config2", "/path/to/config3"])
args2 = MockArgs(name="test2", dashboard=True) with patch("esphome.writer.clean_all") as mock_clean_all:
config2 = {"api": {}, "ota": {}} result1 = command_clean_all(args1)
result2 = command_clean_all(args2)
with patch("esphome.writer.clean_platform") as mock_clean_platform:
result1 = command_clean_platform(args1, config1)
result2 = command_clean_platform(args2, config2)
assert result1 == 0 assert result1 == 0
assert result2 == 0 assert result2 == 0
assert mock_clean_platform.call_count == 2 assert mock_clean_all.call_count == 2
# Verify the correct configuration paths were passed
mock_clean_all.assert_any_call(["/path/to/config1"])
mock_clean_all.assert_any_call(["/path/to/config2", "/path/to/config3"])

View File

@@ -738,16 +738,24 @@ def test_write_cpp_with_duplicate_markers(
@patch("esphome.writer.CORE") @patch("esphome.writer.CORE")
def test_clean_platform( def test_clean_all(
mock_core: MagicMock, mock_core: MagicMock,
tmp_path: Path, tmp_path: Path,
caplog: pytest.LogCaptureFixture, caplog: pytest.LogCaptureFixture,
) -> None: ) -> None:
"""Test clean_platform removes build and PlatformIO dirs.""" """Test clean_all removes build and PlatformIO dirs."""
# Create build directory # Create build directories for multiple configurations
build_dir = tmp_path / "build" config1_dir = tmp_path / "config1"
build_dir.mkdir() config2_dir = tmp_path / "config2"
(build_dir / "dummy.txt").write_text("x") config1_dir.mkdir()
config2_dir.mkdir()
build_dir1 = config1_dir / ".esphome"
build_dir2 = config2_dir / ".esphome"
build_dir1.mkdir()
build_dir2.mkdir()
(build_dir1 / "dummy.txt").write_text("x")
(build_dir2 / "dummy.txt").write_text("x")
# Create PlatformIO directories # Create PlatformIO directories
pio_cache = tmp_path / "pio_cache" pio_cache = tmp_path / "pio_cache"
@@ -758,9 +766,6 @@ def test_clean_platform(
d.mkdir() d.mkdir()
(d / "keep").write_text("x") (d / "keep").write_text("x")
# Setup CORE
mock_core.build_path = build_dir
# Mock ProjectConfig # Mock ProjectConfig
with patch( with patch(
"platformio.project.config.ProjectConfig.get_instance" "platformio.project.config.ProjectConfig.get_instance"
@@ -780,13 +785,14 @@ def test_clean_platform(
mock_config.get.side_effect = cfg_get mock_config.get.side_effect = cfg_get
# Call # Call
from esphome.writer import clean_platform from esphome.writer import clean_all
with caplog.at_level("INFO"): with caplog.at_level("INFO"):
clean_platform() clean_all([str(config1_dir), str(config2_dir)])
# Verify deletions # Verify deletions
assert not build_dir.exists() assert not build_dir1.exists()
assert not build_dir2.exists()
assert not pio_cache.exists() assert not pio_cache.exists()
assert not pio_packages.exists() assert not pio_packages.exists()
assert not pio_platforms.exists() assert not pio_platforms.exists()
@@ -794,7 +800,8 @@ def test_clean_platform(
# Verify logging mentions each # Verify logging mentions each
assert "Deleting" in caplog.text assert "Deleting" in caplog.text
assert str(build_dir) in caplog.text assert str(build_dir1) in caplog.text
assert str(build_dir2) in caplog.text
assert "PlatformIO cache" in caplog.text assert "PlatformIO cache" in caplog.text
assert "PlatformIO packages" in caplog.text assert "PlatformIO packages" in caplog.text
assert "PlatformIO platforms" in caplog.text assert "PlatformIO platforms" in caplog.text
@@ -802,28 +809,29 @@ def test_clean_platform(
@patch("esphome.writer.CORE") @patch("esphome.writer.CORE")
def test_clean_platform_platformio_not_available( def test_clean_all_platformio_not_available(
mock_core: MagicMock, mock_core: MagicMock,
tmp_path: Path, tmp_path: Path,
caplog: pytest.LogCaptureFixture, caplog: pytest.LogCaptureFixture,
) -> None: ) -> None:
"""Test clean_platform when PlatformIO is not available.""" """Test clean_all when PlatformIO is not available."""
# Build dir # Build dirs
build_dir = tmp_path / "build" config_dir = tmp_path / "config"
config_dir.mkdir()
build_dir = config_dir / ".esphome"
build_dir.mkdir() build_dir.mkdir()
mock_core.build_path = build_dir
# PlatformIO dirs that should remain untouched # PlatformIO dirs that should remain untouched
pio_cache = tmp_path / "pio_cache" pio_cache = tmp_path / "pio_cache"
pio_cache.mkdir() pio_cache.mkdir()
from esphome.writer import clean_platform from esphome.writer import clean_all
with ( with (
patch.dict("sys.modules", {"platformio.project.config": None}), patch.dict("sys.modules", {"platformio.project.config": None}),
caplog.at_level("INFO"), caplog.at_level("INFO"),
): ):
clean_platform() clean_all([str(config_dir)])
# Build dir removed, PlatformIO dirs remain # Build dir removed, PlatformIO dirs remain
assert not build_dir.exists() assert not build_dir.exists()
@@ -834,14 +842,15 @@ def test_clean_platform_platformio_not_available(
@patch("esphome.writer.CORE") @patch("esphome.writer.CORE")
def test_clean_platform_partial_exists( def test_clean_all_partial_exists(
mock_core: MagicMock, mock_core: MagicMock,
tmp_path: Path, tmp_path: Path,
) -> None: ) -> None:
"""Test clean_platform when only build dir exists.""" """Test clean_all when only some build dirs exist."""
build_dir = tmp_path / "build" config_dir = tmp_path / "config"
config_dir.mkdir()
build_dir = config_dir / ".esphome"
build_dir.mkdir() build_dir.mkdir()
mock_core.build_path = build_dir
with patch( with patch(
"platformio.project.config.ProjectConfig.get_instance" "platformio.project.config.ProjectConfig.get_instance"
@@ -853,8 +862,8 @@ def test_clean_platform_partial_exists(
tmp_path / "does_not_exist" tmp_path / "does_not_exist"
) )
from esphome.writer import clean_platform from esphome.writer import clean_all
clean_platform() clean_all([str(config_dir)])
assert not build_dir.exists() assert not build_dir.exists()