Skip to content

Commit

Permalink
Starts work on adding recipe upgrade feature
Browse files Browse the repository at this point in the history
The package that this commit relies on is not currently available in
conda-forge. Viewer discretion is advised.

- First-pass attempt at integrating `conda-recipe-manager` into Grayskull for
  the purposes of generating recipes in the V1 format.
  • Loading branch information
schuylermartin45 committed May 16, 2024
1 parent a34b757 commit 8d4c1a7
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 4 deletions.
1 change: 1 addition & 0 deletions environment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ dependencies:
- libcblas
- beautifulsoup4
- semver >=3.0.0,<4.0.0
# - conda-recipe-manager >=0.2.0
22 changes: 20 additions & 2 deletions grayskull/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,15 @@ def init_parser():
help="If sections are specified, grayskull will populate just the sections "
"informed.",
)
cran_parser.add_argument(
"--use-v1-format",
"-u",
default=False,
action="store_true",
dest="use_v1_format",
help="Returns a recipe file in the V1 format, used by rattler-build."
" NOTE: This is experimental.",
)
# create parser for pypi
pypi_parser = subparsers.add_parser("pypi", help="Options to generate PyPI recipes")
pypi_parser.add_argument(
Expand Down Expand Up @@ -244,6 +253,15 @@ def init_parser():
dest="licence_exclude_folders",
help="Exclude folders when searching for licence.",
)
pypi_parser.add_argument(
"--use-v1-format",
"-u",
default=False,
action="store_true",
dest="use_v1_format",
help="Returns a recipe file in the V1 format, used by rattler-build."
" NOTE: This is experimental.",
)

return parser

Expand Down Expand Up @@ -319,7 +337,7 @@ def generate_recipes_from_list(list_pkgs, args):
if args.sections_populate is None or "extra" in args.sections_populate:
add_extra_section(recipe, args.maintainers)

generate_recipe(recipe, config, args.output)
generate_recipe(recipe, config, args.output, args.use_v1_format)
print_msg(
f"\n{Fore.GREEN}#### Recipe generated on "
f"{os.path.realpath(args.output)} for {pkg_name} ####\n\n"
Expand Down Expand Up @@ -365,7 +383,7 @@ def generate_r_recipes_from_list(list_pkgs, args):
if args.sections_populate is None or "extra" in args.sections_populate:
add_extra_section(recipe, args.maintainers)

generate_recipe(recipe, config, args.output)
generate_recipe(recipe, config, args.output, args.use_v1_format)
print_msg(
f"\n{Fore.GREEN}#### Recipe generated on "
f"{os.path.realpath(args.output)} for {pkg_name} ####\n\n"
Expand Down
30 changes: 28 additions & 2 deletions grayskull/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
from difflib import SequenceMatcher
from functools import lru_cache
from glob import glob
from io import StringIO
from pathlib import Path
from shutil import copyfile
from typing import List, Optional, Union

from conda_recipe_manager.parser.recipe_parser_convert import RecipeParserConvert
from ruamel.yaml import YAML
from ruamel.yaml.comments import CommentedMap
from souschef.recipe import Recipe
Expand Down Expand Up @@ -194,11 +196,13 @@ def generate_recipe(
recipe: Recipe,
config,
folder_path: Union[str, Path] = ".",
use_v1_format: bool = False,
):
"""Write the recipe in a location. It will create a folder with the
package name and the recipe will be there.
:param folder_path: Path to the folder
:param use_v1_format: If set to True, return a recipe in the V1 format
"""
if recipe["package"]["name"].value.startswith("r-{{"):
pkg_name = f"r-{config.name}"
Expand All @@ -215,18 +219,40 @@ def generate_recipe(
logging.debug(f"Generating recipe on: {recipe_dir}")
if not recipe_dir.is_dir():
recipe_dir.mkdir()
recipe_path = recipe_dir / "meta.yaml"
recipe_path = recipe_dir / "recipe.yaml" if use_v1_format else "meta.yaml"
recipe_folder = recipe_dir
add_new_lines_after_section(recipe.yaml)

clean_yaml(recipe)
recipe.save(recipe_path)
if use_v1_format:
# Write the converted recipe straight to disk to avoid having convert-back
# to a data-type that will not be used past this point.
upgrade_v0_recipe_to_v1(recipe, recipe_path)
else:
recipe.save(recipe_path)
for file_to_recipe in config.files_to_copy:
name = file_to_recipe.split(os.path.sep)[-1]
if os.path.isfile(file_to_recipe):
copyfile(file_to_recipe, os.path.join(recipe_folder, name))


def upgrade_v0_recipe_to_v1(recipe: Recipe, recipe_path: Path) -> None:
"""
Takes a V0 (pre CEP-13) recipe and converts it to a V1 (post CEP-13) recipe file.
Upgraded recipes are saved to the provided file path.
:param recipe: Recipe data structure to convert
:param recipe_path: Path to write the converted recipe file
"""
recipe_stream = StringIO()
yaml.dump(recipe.yaml, recipe_stream)
recipe_content = recipe_stream.getvalue()
recipe_stream.close()

recipe_converter = RecipeParserConvert(recipe_content)
v1_content, _, _ = recipe_converter.render_to_v1_recipe_format()
recipe_path.write_text(v1_content, encoding="utf-8")


def add_new_lines_after_section(recipe_yaml: CommentedMap) -> CommentedMap:
for section in recipe_yaml.keys():
if section == "package":
Expand Down

0 comments on commit 8d4c1a7

Please sign in to comment.