From 2dbc023df4494a19b1628f745ba01554e9678f77 Mon Sep 17 00:00:00 2001 From: David Montero Date: Fri, 22 May 2026 15:15:53 +0200 Subject: [PATCH] fix(arduino-cli): pin ATTinyCore to 1.4.1 (azduino.com micronucleus host unreachable) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ATTinyCore >=1.5.0 declares ATTinyCore:micronucleus@2.5-azd1b as a tool dependency, hosted at https://azduino.com/bin/micronucleus/. That host has been unreachable (connection refused) for extended periods, causing every ATtiny85 compile to fail at the core-install step with: Download failed: performing HEAD request: ... dial tcp ...: connection refused Failed to install required core: ATTinyCore:avr micronucleus is only used for USB upload — never for compilation — but arduino-cli refuses to install a core whose tool deps cannot fetch. Pin to 1.4.1, the last release whose micronucleus binary is hosted on github.com (digistump release, reachable). The FQBN clock options we ship (clock=16pll on attinyx5, etc.) are unchanged across 1.4.x. - backend/app/services/arduino_cli.py: new CORE_INSTALL_VERSIONS map consulted by ensure_core_for_board so the runtime auto-install passes "ATTinyCore:avr@1.4.1" instead of unversioned latest. - backend/Dockerfile and docker/entrypoint.sh: same pin so a fresh image bakes 1.4.1 in and never hits the runtime fallback path. Existing regression tests in test/backend/unit/test_arduino_cli_attinycore.py still pass (they assert presence, not version). --- backend/Dockerfile | 12 ++++++++---- backend/app/services/arduino_cli.py | 19 ++++++++++++++++--- docker/entrypoint.sh | 2 +- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index 72918976..0725ed5a 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -22,15 +22,19 @@ ENV WASI_SDK=/opt/wasi-sdk # Initialize arduino-cli and install cores. # - arduino:avr → Uno / Nano / Mega / etc. (built-in core, always needed) -# - ATTinyCore:avr → ATtiny85 (Spence Konde's third-party core, drazzy.com index) -# Without this the FQBN ATTinyCore:avr:attinyx5:chip=85,clock=internal16mhz -# fails with "Platform 'ATTinyCore:avr' not found: platform not installed". +# - ATTinyCore:avr@1.4.1 → ATtiny85 (Spence Konde's third-party core, +# drazzy.com index). Pinned to 1.4.1 because >=1.5.0 depends on +# micronucleus hosted at azduino.com (frequently unreachable). 1.4.1's +# micronucleus tool comes from github.com/digistump and still supports +# the FQBN clock options we ship (clock=16pll, etc.). We never invoke +# micronucleus — it is only required for USB upload, not compilation — +# but arduino-cli will not install a core whose tool deps cannot fetch. RUN arduino-cli config init \ && arduino-cli config add board_manager.additional_urls \ http://drazzy.com/package_drazzy.com_index.json \ && arduino-cli core update-index \ && arduino-cli core install arduino:avr \ - && arduino-cli core install ATTinyCore:avr + && arduino-cli core install ATTinyCore:avr@1.4.1 WORKDIR /app diff --git a/backend/app/services/arduino_cli.py b/backend/app/services/arduino_cli.py index 9446cda5..b2de06a5 100644 --- a/backend/app/services/arduino_cli.py +++ b/backend/app/services/arduino_cli.py @@ -30,6 +30,17 @@ class ArduinoCLIService: "esp32": "esp32:esp32", } + # Version pins for `arduino-cli core install`. Keyed by core ID; if a core + # is in this map we pass `@` instead of just ``. + # ATTinyCore: >=1.5.0 depends on micronucleus hosted at azduino.com, which + # has been unreachable for extended periods. 1.4.1 is the last release + # whose micronucleus tool is on github.com (digistump release) AND still + # supports the FQBN options we use (clock=16pll, etc.). Compile-only — + # micronucleus itself is never invoked here. + CORE_INSTALL_VERSIONS: dict[str, str] = { + "ATTinyCore:avr": "1.4.1", + } + def __init__(self, cli_path: str = "arduino-cli"): self.cli_path = cli_path self._ensure_board_urls() @@ -147,12 +158,14 @@ class ArduinoCLIService: if self._is_core_installed(core_id): return {"needed": False, "installed": True, "core_id": core_id, "log": ""} - # Install the core - print(f"[arduino-cli] Auto-installing core {core_id} for board {fqbn}...") + # Install the core (optionally pinned to a specific version) + version = self.CORE_INSTALL_VERSIONS.get(core_id) + install_spec = f"{core_id}@{version}" if version else core_id + print(f"[arduino-cli] Auto-installing core {install_spec} for board {fqbn}...") def _install(): return subprocess.run( - [self.cli_path, "core", "install", core_id], + [self.cli_path, "core", "install", install_spec], capture_output=True, text=True ) diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index cecac073..bc9b2690 100644 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -35,7 +35,7 @@ fi arduino-cli core update-index 2>/dev/null || true arduino-cli core install arduino:avr 2>/dev/null || true arduino-cli core install rp2040:rp2040 2>/dev/null || true -arduino-cli core install ATTinyCore:avr 2>/dev/null || true +arduino-cli core install ATTinyCore:avr@1.4.1 2>/dev/null || true # ESP32 compilation now uses ESP-IDF instead of arduino-cli. # arduino-cli ESP32 core is no longer needed for QEMU-compatible builds.