From cb2fd34e04f8a18fb160e05a3e0b400990f66e27 Mon Sep 17 00:00:00 2001 From: Leynos Date: Thu, 18 Sep 2025 18:34:21 +0100 Subject: [PATCH 1/4] Fix rust host detection timeout and Windows bin path --- .github/actions/rust-build-release/src/runtime.py | 8 +++++++- .github/actions/setup-windows-gnu/action.yml | 3 ++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/actions/rust-build-release/src/runtime.py b/.github/actions/rust-build-release/src/runtime.py index cfaafc36..7ddb9c34 100644 --- a/.github/actions/rust-build-release/src/runtime.py +++ b/.github/actions/rust-build-release/src/runtime.py @@ -102,8 +102,14 @@ def detect_host_target( capture_output=True, text=True, check=True, + timeout=10, ) - except (FileNotFoundError, subprocess.CalledProcessError, OSError): + except ( + FileNotFoundError, + subprocess.CalledProcessError, + subprocess.TimeoutExpired, + OSError, + ): return default triple = next( diff --git a/.github/actions/setup-windows-gnu/action.yml b/.github/actions/setup-windows-gnu/action.yml index 3d3a82bb..3fed254f 100644 --- a/.github/actions/setup-windows-gnu/action.yml +++ b/.github/actions/setup-windows-gnu/action.yml @@ -54,7 +54,8 @@ runs: Remove-Item $destination -Recurse -Force } Expand-Archive $archive -DestinationPath $destination -Force - $binPath = Join-Path $destination "llvm-mingw-$version-ucrt-x86_64\bin" + $toolRoot = Join-Path $destination "llvm-mingw-$version-ucrt-x86_64" + $binPath = Join-Path $toolRoot "bin" if (-not (Test-Path $binPath)) { throw "llvm-mingw bin directory not found at $binPath" } From 348773be14686d9a1bc26948562f2f9cdc16878f Mon Sep 17 00:00:00 2001 From: Leynos Date: Thu, 18 Sep 2025 18:50:09 +0100 Subject: [PATCH 2/4] Add timeout coverage for rust host detection --- .../actions/rust-build-release/src/runtime.py | 1 - .../rust-build-release/tests/test_runtime.py | 59 +++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/.github/actions/rust-build-release/src/runtime.py b/.github/actions/rust-build-release/src/runtime.py index 7ddb9c34..24dd3143 100644 --- a/.github/actions/rust-build-release/src/runtime.py +++ b/.github/actions/rust-build-release/src/runtime.py @@ -105,7 +105,6 @@ def detect_host_target( timeout=10, ) except ( - FileNotFoundError, subprocess.CalledProcessError, subprocess.TimeoutExpired, OSError, diff --git a/.github/actions/rust-build-release/tests/test_runtime.py b/.github/actions/rust-build-release/tests/test_runtime.py index 079c963f..accbf0b6 100644 --- a/.github/actions/rust-build-release/tests/test_runtime.py +++ b/.github/actions/rust-build-release/tests/test_runtime.py @@ -189,3 +189,62 @@ def fake_run( harness.monkeypatch.setattr(runtime_module, "run_validated", fake_run) assert runtime_module.detect_host_target() == "custom-triple" + + +def test_detect_host_target_returns_default_on_timeout( + runtime_module: ModuleType, module_harness: HarnessFactory +) -> None: + """Falls back to the default triple when rustc probing times out.""" + harness = module_harness(runtime_module) + harness.patch_shutil_which( + lambda name: "/usr/bin/rustc" if name == "rustc" else None + ) + harness.patch_attr("ensure_allowed_executable", lambda path, allowed: path) + + def fake_run( + executable: str, + args: list[str], + *, + allowed_names: tuple[str, ...], + **_: object, + ) -> subprocess.CompletedProcess[str]: + _ = (executable, args, allowed_names) + raise subprocess.TimeoutExpired([executable, *args], 10) + + harness.monkeypatch.setattr(runtime_module, "run_validated", fake_run) + + assert ( + runtime_module.detect_host_target(default="fallback-triple") + == "fallback-triple" + ) + + +def test_detect_host_target_passes_timeout_to_run_validated( + runtime_module: ModuleType, module_harness: HarnessFactory +) -> None: + """Ensures rustc probing is bounded via the timeout parameter.""" + harness = module_harness(runtime_module) + harness.patch_shutil_which( + lambda name: "/usr/bin/rustc" if name == "rustc" else None + ) + harness.patch_attr("ensure_allowed_executable", lambda path, allowed: path) + + call_kwargs: dict[str, object] = {} + + def fake_run( + executable: str, + args: list[str], + *, + allowed_names: tuple[str, ...], + **kwargs: object, + ) -> subprocess.CompletedProcess[str]: + _ = (executable, args, allowed_names) + call_kwargs.update(kwargs) + return subprocess.CompletedProcess( + [executable, *args], 0, stdout="host: bounded\n" + ) + + harness.monkeypatch.setattr(runtime_module, "run_validated", fake_run) + + assert runtime_module.detect_host_target() == "bounded" + assert call_kwargs.get("timeout") == 10 From ae01e9fc25e5985ca65212699c5fdc72cc80de27 Mon Sep 17 00:00:00 2001 From: Leynos Date: Sat, 20 Sep 2025 00:05:04 +0100 Subject: [PATCH 3/4] Unify runtime probe timeout configuration --- .github/actions/rust-build-release/src/runtime.py | 8 +++++--- .../actions/rust-build-release/tests/test_runtime.py | 10 ++++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/.github/actions/rust-build-release/src/runtime.py b/.github/actions/rust-build-release/src/runtime.py index 24dd3143..801874ac 100644 --- a/.github/actions/rust-build-release/src/runtime.py +++ b/.github/actions/rust-build-release/src/runtime.py @@ -3,6 +3,7 @@ from __future__ import annotations import json +import os import shutil import subprocess import typing as typ @@ -15,6 +16,7 @@ CROSS_CONTAINER_ERROR_CODES = {125, 126, 127} DEFAULT_HOST_TARGET = "x86_64-unknown-linux-gnu" +PROBE_TIMEOUT = int(os.environ.get("RUNTIME_PROBE_TIMEOUT", "10")) def runtime_available(name: str, *, cwd: str | Path | None = None) -> bool: @@ -33,7 +35,7 @@ def runtime_available(name: str, *, cwd: str | Path | None = None) -> bool: allowed_names=(name, f"{name}.exe"), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, - timeout=10, + timeout=PROBE_TIMEOUT, cwd=cwd, ) except (OSError, subprocess.TimeoutExpired): @@ -51,7 +53,7 @@ def runtime_available(name: str, *, cwd: str | Path | None = None) -> bool: capture_output=True, text=True, check=True, - timeout=10, + timeout=PROBE_TIMEOUT, cwd=cwd, ) except (OSError, subprocess.CalledProcessError, subprocess.TimeoutExpired): @@ -102,7 +104,7 @@ def detect_host_target( capture_output=True, text=True, check=True, - timeout=10, + timeout=PROBE_TIMEOUT, ) except ( subprocess.CalledProcessError, diff --git a/.github/actions/rust-build-release/tests/test_runtime.py b/.github/actions/rust-build-release/tests/test_runtime.py index accbf0b6..1f736d1c 100644 --- a/.github/actions/rust-build-release/tests/test_runtime.py +++ b/.github/actions/rust-build-release/tests/test_runtime.py @@ -52,7 +52,7 @@ def fake_run( ) -> subprocess.CompletedProcess[str]: _ = allowed_names cmd = [executable, *args] - raise subprocess.TimeoutExpired(cmd, 10) + raise subprocess.TimeoutExpired(cmd, runtime_module.PROBE_TIMEOUT) harness.monkeypatch.setattr(runtime_module, "run_validated", fake_run) @@ -146,7 +146,7 @@ def fake_run( _ = (allowed_names, capture_output, check, text) cmd = [executable, *args] if "--format" in args: - raise subprocess.TimeoutExpired(cmd, 10) + raise subprocess.TimeoutExpired(cmd, runtime_module.PROBE_TIMEOUT) return subprocess.CompletedProcess(cmd, 0, stdout="") harness.monkeypatch.setattr(runtime_module, "run_validated", fake_run) @@ -209,7 +209,9 @@ def fake_run( **_: object, ) -> subprocess.CompletedProcess[str]: _ = (executable, args, allowed_names) - raise subprocess.TimeoutExpired([executable, *args], 10) + raise subprocess.TimeoutExpired( + [executable, *args], runtime_module.PROBE_TIMEOUT + ) harness.monkeypatch.setattr(runtime_module, "run_validated", fake_run) @@ -247,4 +249,4 @@ def fake_run( harness.monkeypatch.setattr(runtime_module, "run_validated", fake_run) assert runtime_module.detect_host_target() == "bounded" - assert call_kwargs.get("timeout") == 10 + assert call_kwargs.get("timeout") == runtime_module.PROBE_TIMEOUT From 6b86737e911cf8983ec4e4e89295bd12e7f5926c Mon Sep 17 00:00:00 2001 From: Leynos Date: Sat, 20 Sep 2025 00:23:26 +0100 Subject: [PATCH 4/4] test: capture rustc probe kwargs in timeout test --- .github/actions/rust-build-release/tests/test_runtime.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/actions/rust-build-release/tests/test_runtime.py b/.github/actions/rust-build-release/tests/test_runtime.py index 1f736d1c..965cd929 100644 --- a/.github/actions/rust-build-release/tests/test_runtime.py +++ b/.github/actions/rust-build-release/tests/test_runtime.py @@ -240,8 +240,9 @@ def fake_run( allowed_names: tuple[str, ...], **kwargs: object, ) -> subprocess.CompletedProcess[str]: - _ = (executable, args, allowed_names) + _ = (executable, args) call_kwargs.update(kwargs) + call_kwargs["allowed_names"] = allowed_names return subprocess.CompletedProcess( [executable, *args], 0, stdout="host: bounded\n" ) @@ -250,3 +251,7 @@ def fake_run( assert runtime_module.detect_host_target() == "bounded" assert call_kwargs.get("timeout") == runtime_module.PROBE_TIMEOUT + assert call_kwargs.get("capture_output") is True + assert call_kwargs.get("text") is True + assert call_kwargs.get("check") is True + assert call_kwargs.get("allowed_names") == ("rustc", "rustc.exe")