Run package install with retry logic for network errors. On Windows, this runs pio via cmd.exe shell with a clean environment (no Git Bash indicators) to prevent ESP-IDF tooling from aborting. Args: cmd: Command to execute environment: Environment name (for status updat
(
cmd: list[str],
environment: str | None,
project_dir: str,
caller_pid: str | int = "unknown",
caller_cwd: str = "unknown",
max_retries: int = MAX_NETWORK_RETRIES,
)
| 389 | |
| 390 | |
| 391 | def run_package_install_with_retry( |
| 392 | cmd: list[str], |
| 393 | environment: str | None, |
| 394 | project_dir: str, |
| 395 | caller_pid: str | int = "unknown", |
| 396 | caller_cwd: str = "unknown", |
| 397 | max_retries: int = MAX_NETWORK_RETRIES, |
| 398 | ) -> tuple[int, str]: |
| 399 | """Run package install with retry logic for network errors. |
| 400 | |
| 401 | On Windows, this runs pio via cmd.exe shell with a clean environment |
| 402 | (no Git Bash indicators) to prevent ESP-IDF tooling from aborting. |
| 403 | |
| 404 | Args: |
| 405 | cmd: Command to execute |
| 406 | environment: Environment name (for status updates), or None for default |
| 407 | project_dir: Project directory (for status updates) |
| 408 | caller_pid: Caller process PID (for status updates) |
| 409 | caller_cwd: Caller working directory (for status updates) |
| 410 | max_retries: Maximum retry attempts (default: 3) |
| 411 | |
| 412 | Returns: |
| 413 | (returncode, full_output) |
| 414 | """ |
| 415 | # Determine if we should use shell on Windows |
| 416 | is_windows = platform.system() == "Windows" |
| 417 | |
| 418 | # On Windows: use cmd.exe shell with clean environment (no Git Bash) |
| 419 | # On Linux/Mac: use direct execution with inherited environment |
| 420 | if is_windows: |
| 421 | # Use shell=True on Windows to invoke via cmd.exe |
| 422 | # Convert list to string for shell execution |
| 423 | cmd_str = " ".join(cmd) |
| 424 | shell = True |
| 425 | env = get_clean_windows_env() |
| 426 | logging.info("Windows detected: using cmd.exe shell with clean environment") |
| 427 | logging.debug(f"Command string: {cmd_str}") |
| 428 | else: |
| 429 | # Non-Windows: use direct execution (no shell) |
| 430 | cmd_str = cmd |
| 431 | shell = False |
| 432 | env = None # Inherit environment |
| 433 | logging.info("Non-Windows platform: using direct execution") |
| 434 | |
| 435 | for attempt in range(max_retries): |
| 436 | if attempt > 0: |
| 437 | retry_msg = f"Retry {attempt}/{max_retries - 1} after network error" |
| 438 | logging.info(retry_msg) |
| 439 | update_status( |
| 440 | DaemonState.INSTALLING, |
| 441 | retry_msg, |
| 442 | environment=environment, |
| 443 | project_dir=project_dir, |
| 444 | caller_pid=caller_pid, |
| 445 | caller_cwd=caller_cwd, |
| 446 | current_operation=retry_msg, |
| 447 | ) |
| 448 | time.sleep(5) # Brief delay before retry |
no test coverage detected