From 5c590d6299fe280c6450af60a6d9f33fec6addb8 Mon Sep 17 00:00:00 2001 From: mashehu Date: Fri, 3 Jul 2026 14:33:37 +0200 Subject: [PATCH 1/2] add cuda fallback to pixi template --- .../io/seqera/wave/util/TemplateUtils.java | 13 ++-- .../conda-pixi-v1/dockerfile-conda-file.txt | 9 ++- .../singularityfile-conda-file.txt | 10 +++- .../wave/util/ContainerHelperTest.groovy | 18 ++++-- .../seqera/wave/util/TemplateUtilsTest.groovy | 60 +++++++++++++++---- 5 files changed, 82 insertions(+), 28 deletions(-) diff --git a/src/main/java/io/seqera/wave/util/TemplateUtils.java b/src/main/java/io/seqera/wave/util/TemplateUtils.java index 8b90a6336..7a8f135e2 100644 --- a/src/main/java/io/seqera/wave/util/TemplateUtils.java +++ b/src/main/java/io/seqera/wave/util/TemplateUtils.java @@ -158,7 +158,7 @@ static protected String condaFileTemplate1(String template, PixiOpts opts) { final Map binding = new HashMap<>(); binding.put("base_image", opts.baseImage); binding.put("pixi_image", opts.pixiImage); - binding.put("base_packages", pixiAddBasePackage0(opts.basePackages,singularity)); + binding.put("base_packages", pixiAddBasePackage0(opts.basePackages)); final String result = renderTemplate0(template, binding, List.of("wave_context_dir")); return addCommands(result, opts.commands, singularity); @@ -192,13 +192,12 @@ private static String mambaInstallBasePackage0(String basePackages, boolean sing : "&& " + result + " \\"; } - private static String pixiAddBasePackage0(String basePackages, boolean singularity) { - String result = !StringUtils.isEmpty(basePackages) - ? String.format("pixi add %s", basePackages) + private static String pixiAddBasePackage0(String basePackages) { + // rendered inline after `pixi add conda-forge::which` so that a single + // `pixi add` command solves the whole environment + return !StringUtils.isEmpty(basePackages) + ? " " + basePackages : null; - return result==null || singularity - ? result - : "&& " + result + " \\"; } static private String addCommands(String result, List commands, boolean singularity) { diff --git a/src/main/resources/templates/conda-pixi-v1/dockerfile-conda-file.txt b/src/main/resources/templates/conda-pixi-v1/dockerfile-conda-file.txt index 49fac3371..d70b6171c 100644 --- a/src/main/resources/templates/conda-pixi-v1/dockerfile-conda-file.txt +++ b/src/main/resources/templates/conda-pixi-v1/dockerfile-conda-file.txt @@ -4,8 +4,13 @@ COPY conda.yml /opt/wave/conda.yml WORKDIR /opt/wave RUN pixi init --import /opt/wave/conda.yml \ - && pixi add conda-forge::which \ - {{base_packages}} + && { pixi add conda-forge::which{{base_packages}} > /tmp/pixi.log 2>&1 \ + && cat /tmp/pixi.log \ + || { cat /tmp/pixi.log >&2 && grep -q __cuda /tmp/pixi.log \ + && pixi workspace system-requirements add cuda 99 \ + && export CONDA_OVERRIDE_CUDA="99" \ + && pixi add conda-forge::which{{base_packages}} ; } ; } \ + && pixi install \ && pixi shell-hook > /shell-hook.sh \ && echo 'exec "$@"' >> /shell-hook.sh \ && echo ">> CONDA_LOCK_START" \ diff --git a/src/main/resources/templates/conda-pixi-v1/singularityfile-conda-file.txt b/src/main/resources/templates/conda-pixi-v1/singularityfile-conda-file.txt index 8c5a3d856..b0894803a 100644 --- a/src/main/resources/templates/conda-pixi-v1/singularityfile-conda-file.txt +++ b/src/main/resources/templates/conda-pixi-v1/singularityfile-conda-file.txt @@ -5,8 +5,14 @@ From: {{pixi_image}} %post mkdir /opt/wave && cd /opt/wave pixi init --import /scratch/conda.yml - pixi add conda-forge::which - {{base_packages}} + pixi add conda-forge::which{{base_packages}} > /tmp/pixi.log 2>&1 \ + && cat /tmp/pixi.log \ + || { cat /tmp/pixi.log >&2 && grep -q __cuda /tmp/pixi.log \ + && pixi workspace system-requirements add cuda 99 \ + && export CONDA_OVERRIDE_CUDA="99" \ + && pixi add conda-forge::which{{base_packages}} ; } \ + || exit 1 + pixi install pixi shell-hook > /shell-hook.sh echo ">> CONDA_LOCK_START" cat /opt/wave/pixi.lock diff --git a/src/test/groovy/io/seqera/wave/util/ContainerHelperTest.groovy b/src/test/groovy/io/seqera/wave/util/ContainerHelperTest.groovy index a00bc93d6..b661a45d9 100644 --- a/src/test/groovy/io/seqera/wave/util/ContainerHelperTest.groovy +++ b/src/test/groovy/io/seqera/wave/util/ContainerHelperTest.groovy @@ -634,8 +634,13 @@ class ContainerHelperTest extends Specification { WORKDIR /opt/wave RUN pixi init --import /opt/wave/conda.yml \\ - && pixi add conda-forge::which \\ - && pixi add conda-forge::procps-ng \\ + && { pixi add conda-forge::which conda-forge::procps-ng > /tmp/pixi.log 2>&1 \\ + && cat /tmp/pixi.log \\ + || { cat /tmp/pixi.log >&2 && grep -q __cuda /tmp/pixi.log \\ + && pixi workspace system-requirements add cuda 99 \\ + && export CONDA_OVERRIDE_CUDA="99" \\ + && pixi add conda-forge::which conda-forge::procps-ng ; } ; } \\ + && pixi install \\ && pixi shell-hook > /shell-hook.sh \\ && echo 'exec "$@"' >> /shell-hook.sh \\ && echo ">> CONDA_LOCK_START" \\ @@ -687,8 +692,13 @@ class ContainerHelperTest extends Specification { WORKDIR /opt/wave RUN pixi init --import /opt/wave/conda.yml \\ - && pixi add conda-forge::which \\ - && pixi add foo::one bar::two \\ + && { pixi add conda-forge::which foo::one bar::two > /tmp/pixi.log 2>&1 \\ + && cat /tmp/pixi.log \\ + || { cat /tmp/pixi.log >&2 && grep -q __cuda /tmp/pixi.log \\ + && pixi workspace system-requirements add cuda 99 \\ + && export CONDA_OVERRIDE_CUDA="99" \\ + && pixi add conda-forge::which foo::one bar::two ; } ; } \\ + && pixi install \\ && pixi shell-hook > /shell-hook.sh \\ && echo 'exec "$@"' >> /shell-hook.sh \\ && echo ">> CONDA_LOCK_START" \\ diff --git a/src/test/groovy/io/seqera/wave/util/TemplateUtilsTest.groovy b/src/test/groovy/io/seqera/wave/util/TemplateUtilsTest.groovy index f423a5f90..0e667ed71 100644 --- a/src/test/groovy/io/seqera/wave/util/TemplateUtilsTest.groovy +++ b/src/test/groovy/io/seqera/wave/util/TemplateUtilsTest.groovy @@ -358,8 +358,13 @@ class TemplateUtilsTest extends Specification { WORKDIR /opt/wave RUN pixi init --import /opt/wave/conda.yml \\ - && pixi add conda-forge::which \\ - && pixi add foo::bar \\ + && { pixi add conda-forge::which foo::bar > /tmp/pixi.log 2>&1 \\ + && cat /tmp/pixi.log \\ + || { cat /tmp/pixi.log >&2 && grep -q __cuda /tmp/pixi.log \\ + && pixi workspace system-requirements add cuda 99 \\ + && export CONDA_OVERRIDE_CUDA="99" \\ + && pixi add conda-forge::which foo::bar ; } ; } \\ + && pixi install \\ && pixi shell-hook > /shell-hook.sh \\ && echo 'exec "$@"' >> /shell-hook.sh \\ && echo ">> CONDA_LOCK_START" \\ @@ -394,8 +399,13 @@ class TemplateUtilsTest extends Specification { WORKDIR /opt/wave RUN pixi init --import /opt/wave/conda.yml \\ - && pixi add conda-forge::which \\ - && pixi add conda-forge::procps-ng \\ + && { pixi add conda-forge::which conda-forge::procps-ng > /tmp/pixi.log 2>&1 \\ + && cat /tmp/pixi.log \\ + || { cat /tmp/pixi.log >&2 && grep -q __cuda /tmp/pixi.log \\ + && pixi workspace system-requirements add cuda 99 \\ + && export CONDA_OVERRIDE_CUDA="99" \\ + && pixi add conda-forge::which conda-forge::procps-ng ; } ; } \\ + && pixi install \\ && pixi shell-hook > /shell-hook.sh \\ && echo 'exec "$@"' >> /shell-hook.sh \\ && echo ">> CONDA_LOCK_START" \\ @@ -433,8 +443,13 @@ class TemplateUtilsTest extends Specification { WORKDIR /opt/wave RUN pixi init --import /opt/wave/conda.yml \\ - && pixi add conda-forge::which \\ - && pixi add conda-forge::procps-ng \\ + && { pixi add conda-forge::which conda-forge::procps-ng > /tmp/pixi.log 2>&1 \\ + && cat /tmp/pixi.log \\ + || { cat /tmp/pixi.log >&2 && grep -q __cuda /tmp/pixi.log \\ + && pixi workspace system-requirements add cuda 99 \\ + && export CONDA_OVERRIDE_CUDA="99" \\ + && pixi add conda-forge::which conda-forge::procps-ng ; } ; } \\ + && pixi install \\ && pixi shell-hook > /shell-hook.sh \\ && echo 'exec "$@"' >> /shell-hook.sh \\ && echo ">> CONDA_LOCK_START" \\ @@ -684,8 +699,14 @@ class TemplateUtilsTest extends Specification { %post mkdir /opt/wave && cd /opt/wave pixi init --import /scratch/conda.yml - pixi add conda-forge::which - pixi add conda-forge::procps-ng + pixi add conda-forge::which conda-forge::procps-ng > /tmp/pixi.log 2>&1 \\ + && cat /tmp/pixi.log \\ + || { cat /tmp/pixi.log >&2 && grep -q __cuda /tmp/pixi.log \\ + && pixi workspace system-requirements add cuda 99 \\ + && export CONDA_OVERRIDE_CUDA="99" \\ + && pixi add conda-forge::which conda-forge::procps-ng ; } \\ + || exit 1 + pixi install pixi shell-hook > /shell-hook.sh echo ">> CONDA_LOCK_START" cat /opt/wave/pixi.lock @@ -705,8 +726,14 @@ class TemplateUtilsTest extends Specification { %post mkdir /opt/wave && cd /opt/wave pixi init --import /scratch/conda.yml - pixi add conda-forge::which - pixi add conda-forge::procps-ng + pixi add conda-forge::which conda-forge::procps-ng > /tmp/pixi.log 2>&1 \\ + && cat /tmp/pixi.log \\ + || { cat /tmp/pixi.log >&2 && grep -q __cuda /tmp/pixi.log \\ + && pixi workspace system-requirements add cuda 99 \\ + && export CONDA_OVERRIDE_CUDA="99" \\ + && pixi add conda-forge::which conda-forge::procps-ng ; } \\ + || exit 1 + pixi install pixi shell-hook > /shell-hook.sh echo ">> CONDA_LOCK_START" cat /opt/wave/pixi.lock @@ -733,7 +760,14 @@ class TemplateUtilsTest extends Specification { %post mkdir /opt/wave && cd /opt/wave pixi init --import /scratch/conda.yml - pixi add conda-forge::which + pixi add conda-forge::which > /tmp/pixi.log 2>&1 \\ + && cat /tmp/pixi.log \\ + || { cat /tmp/pixi.log >&2 && grep -q __cuda /tmp/pixi.log \\ + && pixi workspace system-requirements add cuda 99 \\ + && export CONDA_OVERRIDE_CUDA="99" \\ + && pixi add conda-forge::which ; } \\ + || exit 1 + pixi install pixi shell-hook > /shell-hook.sh echo ">> CONDA_LOCK_START" cat /opt/wave/pixi.lock @@ -754,7 +788,7 @@ class TemplateUtilsTest extends Specification { def result = TemplateUtils.condaFileToDockerFileUsingPixi(PIXI_OPTS) then: - result.contains('pixi add conda-forge::procps-ng') + result.contains('pixi add conda-forge::which conda-forge::procps-ng') result.contains('RUN apt-get update') result.contains('RUN apt-get install -y curl') } @@ -770,7 +804,7 @@ class TemplateUtilsTest extends Specification { def result = TemplateUtils.condaFileToSingularityFileUsingPixi(PIXI_OPTS) then: - result.contains('pixi add conda-forge::bash') + result.contains('pixi add conda-forge::which conda-forge::bash') result.contains('%post') result.contains('apt-get update') result.contains('apt-get install -y nano') From e73736b8650da1204e80bce289746a6be243d20b Mon Sep 17 00:00:00 2001 From: mashehu Date: Wed, 15 Jul 2026 12:22:16 +0200 Subject: [PATCH 2/2] update tests --- src/test/groovy/io/seqera/wave/util/PixiHelperTest.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/groovy/io/seqera/wave/util/PixiHelperTest.groovy b/src/test/groovy/io/seqera/wave/util/PixiHelperTest.groovy index 6bddfbd1b..7a8b8f1e9 100644 --- a/src/test/groovy/io/seqera/wave/util/PixiHelperTest.groovy +++ b/src/test/groovy/io/seqera/wave/util/PixiHelperTest.groovy @@ -44,7 +44,7 @@ class PixiHelperTest extends Specification { result.contains('FROM public.cr.seqera.io/wave/pixi:0.61.0-noble AS build') result.contains('COPY conda.yml /opt/wave/conda.yml') result.contains('pixi init --import /opt/wave/conda.yml') - result.contains('pixi add conda-forge::procps-ng') + result.contains('pixi add conda-forge::which conda-forge::procps-ng') result.contains('pixi shell-hook > /shell-hook.sh') result.contains('FROM ubuntu:24.04 AS final') result.contains('COPY --from=build /opt/wave/.pixi/envs/default /opt/wave/.pixi/envs/default') @@ -104,7 +104,7 @@ class PixiHelperTest extends Specification { then: result.contains('FROM ghcr.io/prefix-dev/pixi:0.47.0-jammy-cuda-12.8.1 AS build') - result.contains('pixi add foo::one bar::two') + result.contains('pixi add conda-forge::which foo::one bar::two') result.contains('FROM base/image AS final') }