From 30203cd1b74fb7303c6cbb0723f0a32d4d819a50 Mon Sep 17 00:00:00 2001 From: GlassOfWhiskey Date: Tue, 5 May 2026 00:00:40 +0200 Subject: [PATCH] Fix NodeJS retrieval when Docker is unavailable Fix #1053 by adding `_get_container_engine()` to detect the available container runtime by probing `docker`, `singularity`, `podman`, and `udocker` in order, caching the result with `@cached(FIFOCache(1))` from `cachebox`. The detected engine is passed as `container_engine` to `cwl_utils.expression.interpolate()` in `eval_expression()`, fixing CWL JavaScript expression evaluation on systems without Docker. --- CHANGELOG.md | 1 + streamflow/cwl/utils.py | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e99493467..96c980096 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Fix NodeJS retrieval when Docker is unavailable ([#1054](https://github.com/alpha-unito/streamflow/pull/1054) - Fix bind mount inspection in `SingularityConnector` ([#1058](https://github.com/alpha-unito/streamflow/pull/1058)) - Fix `--wait` flag in `Helm4Connector` for Cobra parser ([#1050](https://github.com/alpha-unito/streamflow/pull/1050)) - Fix shell reuse collision across execution locations ([#1045](https://github.com/alpha-unito/streamflow/pull/1045)) diff --git a/streamflow/cwl/utils.py b/streamflow/cwl/utils.py index 401bf4869..e633f4011 100644 --- a/streamflow/cwl/utils.py +++ b/streamflow/cwl/utils.py @@ -8,6 +8,7 @@ from collections.abc import MutableMapping, MutableSequence from enum import Enum from pathlib import PurePath +from shutil import which from types import ModuleType from typing import Any, cast @@ -15,6 +16,7 @@ import cwl_utils.parser import cwl_utils.parser.utils import cwl_utils.types +from cachebox import FIFOCache, cached from cwl_utils.parser.cwl_v1_2_utils import CONTENT_LIMIT from typing_extensions import Self, TypeIs @@ -113,6 +115,14 @@ async def _create_remote_directory( ) +@cached(cache=FIFOCache(1)) +def _get_container_engine() -> str: + for engine in ("docker", "singularity", "podman", "udocker"): + if which(engine) is not None: + return engine + return "docker" + + async def _get_contents( path: StreamFlowPath, size: int, @@ -740,6 +750,7 @@ def eval_expression( fullJS=full_js, strip_whitespace=strip_whitespace, timeout=timeout, + container_engine=_get_container_engine(), ) if is_expression(expression) else expression