Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support running conda-smithy lint in feedstock directory #2250

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 38 additions & 10 deletions conda_smithy/lint_recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -722,29 +722,57 @@ def _format_validation_msg(error: jsonschema.ValidationError):
)


def main(
recipe_dir, conda_forge=False, return_hints=False, feedstock_dir=None
):
def find_recipe_directory(
recipe_dir: str,
feedstock_dir: Optional[str],
) -> tuple[str, str]:
"""Find recipe directory and build tool"""

recipe_dir = os.path.abspath(recipe_dir)
build_tool = CONDA_BUILD_TOOL
if feedstock_dir:

# The logic below:
# 1. If `--feedstock-dir` is not specified, try looking for `recipe.yaml`
# or `meta.yaml` in the specified recipe directory.
# 2. If there is none, look for `conda-forge.yml` -- perhaps the user
# passed feedstock directory instead. In that case, obtain
# the recipe directory from `conda-forge.yml`.

if feedstock_dir is None:
if os.path.exists(os.path.join(recipe_dir, "recipe.yaml")):
return (recipe_dir, RATTLER_BUILD_TOOL)
elif os.path.exists(os.path.join(recipe_dir, "meta.yaml")):
return (recipe_dir, CONDA_BUILD_TOOL)
elif os.path.exists(os.path.join(recipe_dir, "conda-forge.yml")):
# passthrough to the feedstock_dir logic below
feedstock_dir = recipe_dir
recipe_dir = None

if feedstock_dir is not None:
feedstock_dir = os.path.abspath(feedstock_dir)
forge_config = _read_forge_config(feedstock_dir)
if forge_config.get("conda_build_tool", "") == RATTLER_BUILD_TOOL:
build_tool = RATTLER_BUILD_TOOL
else:
if os.path.exists(os.path.join(recipe_dir, "recipe.yaml")):
build_tool = RATTLER_BUILD_TOOL
if recipe_dir is None:
recipe_dir = os.path.join(
feedstock_dir, forge_config.get("recipe_dir", "recipe")
)

return (recipe_dir, build_tool)


def main(
recipe_dir, conda_forge=False, return_hints=False, feedstock_dir=None
):
recipe_dir, build_tool = find_recipe_directory(recipe_dir, feedstock_dir)

if build_tool == RATTLER_BUILD_TOOL:
recipe_file = os.path.join(recipe_dir, "recipe.yaml")
else:
recipe_file = os.path.join(recipe_dir, "meta.yaml")

if not os.path.exists(recipe_file):
raise OSError(
f"Feedstock has no recipe/{os.path.basename(recipe_file)}"
)
raise OSError(f"No recipe file found in {recipe_dir}")

if build_tool == CONDA_BUILD_TOOL:
with open(recipe_file, encoding="utf-8") as fh:
Expand Down
24 changes: 24 additions & 0 deletions news/2250-recipe-dir-param.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
**Added:**

* <news item>

**Changed:**

* ``conda-smithy lint`` now can be run with the feedstock directory instead of
the recipe subdirectory. (#2250)

**Deprecated:**

* <news item>

**Removed:**

* <news item>

**Fixed:**

* <news item>

**Security:**

* <news item>
107 changes: 106 additions & 1 deletion tests/test_lint_recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@

import conda_smithy.lint_recipe as linter
from conda_smithy.linter import hints
from conda_smithy.linter.utils import VALID_PYTHON_BUILD_BACKENDS
from conda_smithy.linter.utils import (
CONDA_BUILD_TOOL,
RATTLER_BUILD_TOOL,
VALID_PYTHON_BUILD_BACKENDS,
)
from conda_smithy.utils import get_yaml, render_meta_yaml

_thisdir = os.path.abspath(os.path.dirname(__file__))
Expand Down Expand Up @@ -4259,5 +4263,106 @@ def test_bad_specs_report(tmp_path, spec, ok):
assert all("has some malformed specs" not in hint for hint in hints) is ok


@pytest.mark.parametrize(
"create_recipe_file,directory_param,expected_tool",
[
# recipe path passed
(None, "recipe", CONDA_BUILD_TOOL),
("meta.yaml", "recipe", CONDA_BUILD_TOOL),
("recipe.yaml", "recipe", RATTLER_BUILD_TOOL),
# no conda-forge.yml, incorrect path passed
(None, ".", CONDA_BUILD_TOOL),
("meta.yaml", ".", CONDA_BUILD_TOOL),
("recipe.yaml", ".", CONDA_BUILD_TOOL),
],
)
def test_find_recipe_directory(
tmp_path, create_recipe_file, directory_param, expected_tool
):
"""
Test ``find_recipe_directory()`` without ``conda-forge.yml``

When ``find_recipe_directory()`` is passed a directory with
no ``conda-forge.yml``, and no ``--feedstock-directory`` is passed,
it should just return the input directory and guess tool from files
inside it.
"""

tmp_path.joinpath("recipe").mkdir()
if create_recipe_file is not None:
tmp_path.joinpath("recipe", create_recipe_file).touch()

assert linter.find_recipe_directory(
str(tmp_path / directory_param), None
) == (str(tmp_path / directory_param), expected_tool)


@pytest.mark.parametrize(
"build_tool", [None, CONDA_BUILD_TOOL, RATTLER_BUILD_TOOL]
)
@pytest.mark.parametrize("recipe_dir", [None, "foo", "recipe"])
def test_find_recipe_directory_via_conda_forge_yml(
tmp_path, build_tool, recipe_dir
):
"""
Test ``find_recipe_directory()`` with ``conda-forge.yml``

When ``find_recipe_directory()`` is passed a directory with
``conda-forge.yml``, and no ``--feedstock-directory``, it should read
both the recipe directory and the tool type from it.
"""

# create all files to verify that format is taken from conda-forge.yml
tmp_path.joinpath("recipe").mkdir()
tmp_path.joinpath("recipe", "meta.yaml").touch()
tmp_path.joinpath("recipe", "recipe.yaml").touch()

with tmp_path.joinpath("conda-forge.yml").open("w") as f:
if build_tool is not None:
f.write(f"conda_build_tool: {build_tool}\n")
if recipe_dir is not None:
f.write(f"recipe_dir: {recipe_dir}\n")

assert linter.find_recipe_directory(str(tmp_path), None) == (
str(tmp_path / (recipe_dir or "recipe")),
build_tool or CONDA_BUILD_TOOL,
)


@pytest.mark.parametrize(
"build_tool", [None, CONDA_BUILD_TOOL, RATTLER_BUILD_TOOL]
)
@pytest.mark.parametrize("yaml_recipe_dir", [None, "foo", "recipe"])
@pytest.mark.parametrize("directory_param", [".", "recipe"])
def test_find_recipe_directory_with_feedstock_dir(
tmp_path, build_tool, yaml_recipe_dir, directory_param
):
"""
Test ``find_recipe_directory()`` with ``--feedstock-directory``

When ``find_recipe_directory()`` is passed a ``--feedstock-directory``,
it should read the tool type from it, but use the passed recipe
directory.
"""

# create all files to verify that format is taken from conda-forge.yml
tmp_path.joinpath("recipe").mkdir()
tmp_path.joinpath("recipe", "meta.yaml").touch()
tmp_path.joinpath("recipe", "recipe.yaml").touch()

with tmp_path.joinpath("conda-forge.yml").open("w") as f:
if build_tool is not None:
f.write(f"conda_build_tool: {build_tool}\n")
if yaml_recipe_dir is not None:
f.write(f"recipe_dir: {yaml_recipe_dir}\n")

assert linter.find_recipe_directory(
str(tmp_path / directory_param), str(tmp_path)
) == (
str(tmp_path / directory_param),
build_tool or CONDA_BUILD_TOOL,
)


if __name__ == "__main__":
unittest.main()