[cli] Use pio run -t idedata to set up libretiny penv on cold hosts

The prior commit invoked 'pio pkg install' for the on-demand prep step,
which installs the libretiny platform package but does NOT recreate the
~/.platformio/penv/.libretiny/ virtualenv -- that's set up by libretiny's
ConfigurePythonVenv SCons step, which only fires during 'pio run'. So a
cold host (penv missing) still failed to find ltchiptool after the install
returned 'Already up-to-date'.

Wet test against bw15-device.yaml on a host with the libretiny penv
deleted:
  esphome upload bw15-device.yaml --device /dev/null --prebuilt-dir ...
  INFO ltchiptool not found on this host; installing the PlatformIO
       rtl87xx platform package ...
  Resolving bw15-device dependencies...
  Already up-to-date.
  ERROR ltchiptool not found. ...

Replace pkg install with 'pio run -t idedata'. The idedata target runs
SConscript without the actual compile target, so penv creation (libretiny)
and tool-package install (RP2040) happen as side effects of the SCons
configure phase -- but the build itself is skipped. Cost is a few seconds
of SCons configure, paid once per cold host.

Rename run_pkg_install -> prepare_platform_for_upload to match the new
semantics.

Issue: esphome/device-builder#572
Issue: esphome/device-builder#570
This commit is contained in:
J. Nick Koston
2026-05-11 00:30:06 -05:00
parent aab7177f07
commit 5cc7719ae4
3 changed files with 31 additions and 22 deletions
+8 -8
View File
@@ -1621,8 +1621,8 @@ def test_upload_program_prebuilt_dir_installs_libretiny_platform_if_missing(
patch("esphome.__main__.get_ltchiptool_path", return_value=None) as mock_find,
patch("esphome.__main__.write_cpp", return_value=0) as mock_write_cpp,
patch(
"esphome.platformio_api.run_pkg_install", return_value=0
) as mock_pkg_install,
"esphome.platformio_api.prepare_platform_for_upload", return_value=0
) as mock_prep,
patch("esphome.__main__.upload_using_ltchiptool", return_value=0),
):
exit_code, _ = upload_program({}, args, ["/dev/ttyUSB0"])
@@ -1630,7 +1630,7 @@ def test_upload_program_prebuilt_dir_installs_libretiny_platform_if_missing(
assert exit_code == 0
mock_find.assert_called()
mock_write_cpp.assert_called_once()
mock_pkg_install.assert_called_once()
mock_prep.assert_called_once()
def test_upload_program_prebuilt_dir_skips_install_when_tool_present(
@@ -1655,14 +1655,14 @@ def test_upload_program_prebuilt_dir_skips_install_when_tool_present(
return_value=tmp_path / "ltchiptool",
),
patch("esphome.__main__.write_cpp") as mock_write_cpp,
patch("esphome.platformio_api.run_pkg_install") as mock_pkg_install,
patch("esphome.platformio_api.prepare_platform_for_upload") as mock_prep,
patch("esphome.__main__.upload_using_ltchiptool", return_value=0),
):
exit_code, _ = upload_program({}, args, ["/dev/ttyUSB0"])
assert exit_code == 0
mock_write_cpp.assert_not_called()
mock_pkg_install.assert_not_called()
mock_prep.assert_not_called()
def test_upload_program_prebuilt_dir_write_cpp_failure_aborts_upload(
@@ -1684,14 +1684,14 @@ def test_upload_program_prebuilt_dir_write_cpp_failure_aborts_upload(
with (
patch("esphome.__main__.get_ltchiptool_path", return_value=None),
patch("esphome.__main__.write_cpp", return_value=3),
patch("esphome.platformio_api.run_pkg_install") as mock_pkg_install,
patch("esphome.platformio_api.prepare_platform_for_upload") as mock_prep,
patch("esphome.__main__.upload_using_ltchiptool") as mock_upload,
):
exit_code, host = upload_program({}, args, ["/dev/ttyUSB0"])
assert exit_code == 3
assert host is None
mock_pkg_install.assert_not_called()
mock_prep.assert_not_called()
mock_upload.assert_not_called()
@@ -1713,7 +1713,7 @@ def test_upload_program_prebuilt_dir_pkg_install_failure_aborts_upload(
with (
patch("esphome.__main__._find_picotool", return_value=None),
patch("esphome.__main__.write_cpp", return_value=0),
patch("esphome.platformio_api.run_pkg_install", return_value=2),
patch("esphome.platformio_api.prepare_platform_for_upload", return_value=2),
patch("esphome.__main__.upload_using_picotool") as mock_upload,
patch("esphome.__main__._rp2040_serial_reset_to_bootsel") as mock_reset,
):