diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1ee391448c..e17fc9f94d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -102,6 +102,7 @@ jobs: CONDA_CHANNEL_LABEL: ${{ matrix.conda-version == 'canary' && 'conda-canary/label/dev::' || '' }} CONDA_VERSION: ${{ contains('canary|release', matrix.conda-version) && 'conda' || format('conda={0}', matrix.conda-version) }} PYTEST_MARKER: ${{ matrix.test-type == 'serial' && 'serial' || 'not serial' }} + REQUIREMENTS_V1_RECIPE_FORMAT: ${{ matrix.python-version != '3.10' && '--file tests/requirements-v1-recipe-format.txt' || ''}} steps: - name: Free Disk Space @@ -165,6 +166,7 @@ jobs: --file tests/requirements.txt --file tests/requirements-${{ runner.os }}.txt --file tests/requirements-ci.txt + ${{ env.REQUIREMENTS_V1_RECIPE_FORMAT }} python=${{ matrix.python-version }} ${{ env.CONDA_CHANNEL_LABEL }}${{ env.CONDA_VERSION }} @@ -296,6 +298,7 @@ jobs: ErrorActionPreference: Stop # powershell exit on first error CONDA_CHANNEL_LABEL: ${{ matrix.conda-version == 'canary' && 'conda-canary/label/dev' || 'defaults' }} PYTEST_MARKER: ${{ matrix.test-type == 'serial' && 'serial' || 'not serial and not slow' }} + REQUIREMENTS_V1_RECIPE_FORMAT: ${{ matrix.python-version != '3.10' && '--file tests/requirements-v1-recipe-format.txt' || ''}} steps: - name: Checkout Source @@ -334,6 +337,7 @@ jobs: --file tests\requirements.txt --file tests\requirements-${{ runner.os }}.txt --file tests\requirements-ci.txt + ${{ env.REQUIREMENTS_V1_RECIPE_FORMAT }} python=${{ matrix.python-version }} ${{ env.CONDA_CHANNEL_LABEL }}::conda @@ -405,6 +409,7 @@ jobs: # Use conda-forge for release tests since conda 25.11+ is not in defaults for osx-64 CONDA_CHANNEL_LABEL: ${{ matrix.conda-version == 'canary' && 'conda-canary/label/dev' || 'conda-forge' }} PYTEST_MARKER: ${{ matrix.test-type == 'serial' && 'serial' || 'not serial' }} + REQUIREMENTS_V1_RECIPE_FORMAT: ${{ matrix.python-version != '3.10' && '--file tests/requirements-v1-recipe-format.txt' || ''}} steps: - name: Checkout Source @@ -454,6 +459,7 @@ jobs: --file tests/requirements.txt --file tests/requirements-${{ runner.os }}.txt --file tests/requirements-ci.txt + ${{ env.REQUIREMENTS_V1_RECIPE_FORMAT }} python=${{ matrix.python-version }} ${{ env.CONDA_CHANNEL_LABEL }}::conda diff --git a/conda_build/cli/main_skeleton.py b/conda_build/cli/main_skeleton.py index 66a93cef63..ffbe24653e 100644 --- a/conda_build/cli/main_skeleton.py +++ b/conda_build/cli/main_skeleton.py @@ -7,12 +7,14 @@ import pkgutil import sys from importlib import import_module +from pathlib import Path from typing import TYPE_CHECKING from conda.base.context import context from .. import api from ..config import Config +from ..exceptions import CondaBuildUserError if TYPE_CHECKING: from argparse import ArgumentParser, Namespace @@ -21,6 +23,8 @@ thisdir = os.path.dirname(os.path.abspath(__file__)) logging.basicConfig(level=logging.INFO) +SUPPORTED_RECIPE_VERSIONS = ("v0", "v1") + def parse_args(args: Sequence[str] | None) -> tuple[ArgumentParser, Namespace]: from conda.cli.conda_argparse import ArgumentParser @@ -37,6 +41,14 @@ def parse_args(args: Sequence[str] | None) -> tuple[ArgumentParser, Namespace]: """, ) + # Flag for using rattler-build + parser.add_argument( + "--output-format", + choices=SUPPORTED_RECIPE_VERSIONS, + default="v0", + help="Output format for recipe generation.", + ) + repos = parser.add_subparsers(dest="repo") skeletons = [ @@ -62,6 +74,16 @@ def execute(args: Sequence[str] | None = None) -> int: parser.print_help() sys.exit() + if parsed.output_format == "v1": + try: + from conda_recipe_manager.parser.recipe_parser_convert import ( + RecipeParserConvert, + ) + except ImportError: + raise CondaBuildUserError( + "Please install conda-recipe-manager to enable v1 recipe generation." + ) + api.skeletonize( parsed.packages, parsed.repo, @@ -71,4 +93,19 @@ def execute(args: Sequence[str] | None = None) -> int: config=config, ) + if parsed.output_format == "v1": + for package in parsed.packages: + v0_recipe_path = Path(os.path.join(parsed.output_dir, package, "meta.yaml")) + v1_recipe_path = Path( + os.path.join(parsed.output_dir, package, "recipe.yaml") + ) + + recipe_content = RecipeParserConvert.pre_process_recipe_text( + v0_recipe_path.read_text() + ) + recipe_converter = RecipeParserConvert(recipe_content) + v1_content, _, _ = recipe_converter.render_to_v1_recipe_format() + v1_recipe_path.write_text(v1_content, encoding="utf-8") + os.remove(v0_recipe_path) + return 0 diff --git a/news/5919-v1-recipe-generation.md b/news/5919-v1-recipe-generation.md new file mode 100644 index 0000000000..864a7623a6 --- /dev/null +++ b/news/5919-v1-recipe-generation.md @@ -0,0 +1,19 @@ +### Enhancements + +* Add support for generating v1 recipes using `conda-recipe-manager` (#5919). + +### Bug fixes + +* + +### Deprecations + +* + +### Docs + +* + +### Other + +* diff --git a/tests/cli/test_main_skeleton.py b/tests/cli/test_main_skeleton.py index c2dd0a65b5..53d7fe69b0 100644 --- a/tests/cli/test_main_skeleton.py +++ b/tests/cli/test_main_skeleton.py @@ -1,6 +1,7 @@ # Copyright (C) 2014 Anaconda, Inc # SPDX-License-Identifier: BSD-3-Clause import os +import sys import pytest @@ -57,3 +58,16 @@ def test_skeleton_pypi_arguments_work(testing_workdir): metadata = api.render("photutils")[0][0] assert "--offline" in metadata.meta["build"]["script"] assert metadata.version() == "1.10.0" + + +@pytest.mark.slow +@pytest.mark.skipif(sys.version_info < (3, 11), reason="requires python3.11 or higher") +def test_v1_recipe_generation(testing_workdir): + """ + Test v1 recipe generation + """ + args = ["--output-format=v1", "pypi", "botocore"] + main_skeleton.execute(args) + assert os.path.isfile("botocore/recipe.yaml") + with open(os.path.join("botocore", "recipe.yaml")) as f: + assert "botocore" in f.read() diff --git a/tests/requirements-v1-recipe-format.txt b/tests/requirements-v1-recipe-format.txt new file mode 100644 index 0000000000..416de646a9 --- /dev/null +++ b/tests/requirements-v1-recipe-format.txt @@ -0,0 +1 @@ +conda-recipe-manager