From 4e5dc19f3fe8c1b7df31539ca5d958c47825f7bc Mon Sep 17 00:00:00 2001 From: Julian Hofer Date: Wed, 15 Apr 2026 16:25:57 +0200 Subject: [PATCH] flatten v1 staging outputs before rendering rattler-build --render-only drops staging outputs, hiding their build and host requirements from the dependency graph. Flatten the recipe text first so pinning and arch migrations see the full requirement surface. --- conda_forge_tick/utils.py | 8 +++- environment.yml | 2 +- tests/test_recipe_yaml_parsing.py | 63 +++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 2 deletions(-) diff --git a/conda_forge_tick/utils.py b/conda_forge_tick/utils.py index 391b42cf8..804642fdd 100644 --- a/conda_forge_tick/utils.py +++ b/conda_forge_tick/utils.py @@ -35,6 +35,7 @@ run_container_operation, should_use_container, ) +from rattler_build_conda_compat.outputs import flatten_staging_inheritance from . import sensitive_env from .lazy_json_backends import LazyJson @@ -521,7 +522,12 @@ def _render_recipe_yaml( else ["--target-platform", platform_arch] ) - prepared_text = replace_compiler_with_stub(text) + stubbed_text = replace_compiler_with_stub(text) + # rattler-build --render-only drops staging outputs and does not + # propagate their build/host requirements into inheriting outputs, + # so flatten them first to keep the dep graph complete for pinning + # and arch migrations. + prepared_text = flatten_staging_inheritance(stubbed_text) res = subprocess.run( ["rattler-build", "build", "--render-only"] diff --git a/environment.yml b/environment.yml index fd88674f5..63ef6c4e4 100644 --- a/environment.yml +++ b/environment.yml @@ -52,7 +52,7 @@ dependencies: - python-dateutil - python-graphviz - rattler-build >=0.58.3,<0.60 - - rattler-build-conda-compat >=1.4.6,<2,!=1.4.7,!=1.4.8 + - rattler-build-conda-compat >=1.4.13,<2 - requests - ruamel.yaml - ruamel.yaml.jinja2 diff --git a/tests/test_recipe_yaml_parsing.py b/tests/test_recipe_yaml_parsing.py index 3f95e5181..de041188f 100644 --- a/tests/test_recipe_yaml_parsing.py +++ b/tests/test_recipe_yaml_parsing.py @@ -200,3 +200,66 @@ def test_populate_feedstock_attributes(recipe_name): assert isinstance(value, set) for el in value: assert isinstance(el, str) + + +def test_populate_feedstock_attributes_staging_outputs(): + """Build/host deps declared in a staging output must appear in the + parsed feedstock attributes, otherwise pinning migrations won't + know that this feedstock needs to be rebuilt. + """ + recipe_text = """\ +outputs: + - staging: + name: shared-build + source: + url: https://example.com/foo-1.0.tar.gz + sha256: "0000000000000000000000000000000000000000000000000000000000000000" + requirements: + build: + - cxx_compiler_stub + host: + - cudnn 9.* + - python + - package: + name: foo + version: "1.0" + inherit: shared-build + requirements: + host: + - numpy + run: + - python + - numpy +about: + license: MIT + summary: test +""" + + with TemporaryDirectory() as tmpdir: + os.makedirs(Path(tmpdir) / "recipe", exist_ok=True) + os.makedirs(Path(tmpdir) / ".ci_support", exist_ok=True) + (Path(tmpdir) / "recipe" / "recipe.yaml").write_text(recipe_text) + (Path(tmpdir) / ".ci_support" / "linux_64_.yaml").write_text("""\ +target_platform: + - linux-64 +""") + node_attrs = populate_feedstock_attributes( + "foo", + {}, + recipe_yaml=recipe_text, + feedstock_dir=tmpdir, + ) + + build_reqs = node_attrs["total_requirements"]["build"] + assert "cxx_compiler_stub" in build_reqs + + host_reqs = node_attrs["total_requirements"]["host"] + # from staging + assert "cudnn 9.*" in host_reqs + assert "python" in host_reqs + # from the package output itself + assert "numpy" in host_reqs + + run_reqs = node_attrs["total_requirements"]["run"] + assert "python" in run_reqs + assert "numpy" in run_reqs