Repository: conda-forge/conda-smithy

Issue:
Get rid of rattler branding
We should get rid of rattler branding here for future conda recipe v2 support in conda-build itself.

For eg: Rename `rattler_linter.py` to `conda_recipe_v2_linter.py` and similar.

cc @nichmor, @wolfv, @beckermr 

Discussion:

Patch:
diff --git a/conda_smithy/cli.py b/conda_smithy/cli.py
index 48cf8536..bbc0a1a3 100644
--- a/conda_smithy/cli.py
+++ b/conda_smithy/cli.py
@@ -11,7 +11,7 @@ from typing import Optional, Union
 import conda  # noqa
 from conda_build.metadata import MetaData
 from rattler_build_conda_compat.render import MetaData as RattlerMetaData
-from rattler_build_conda_compat.utils import has_recipe as has_rattler_recipe
+from rattler_build_conda_compat.utils import has_recipe as has_recipe_v2
 from ruamel.yaml import YAML
 
 import conda_smithy.cirun_utils
@@ -132,7 +132,7 @@ class Init(Subcommand):
         build_tool = CONDA_BUILD
 
         # detect what recipe ( meta.yaml or recipe.yaml ) we should render
-        if has_rattler_recipe(args.recipe_directory):
+        if has_recipe_v2(args.recipe_directory):
             build_tool = RATTLER_BUILD
 
         if build_tool == CONDA_BUILD:
diff --git a/conda_smithy/lint_recipe.py b/conda_smithy/lint_recipe.py
index a9772478..ffdbfca5 100644
--- a/conda_smithy/lint_recipe.py
+++ b/conda_smithy/lint_recipe.py
@@ -12,7 +12,7 @@ import github
 import jsonschema
 import requests
 
-from conda_smithy.linter import rattler_linter
+from conda_smithy.linter import conda_recipe_v2_linter
 from conda_smithy.linter.hints import (
     hint_check_spdx,
     hint_pip_usage,
@@ -36,10 +36,10 @@ from conda_smithy.linter.lints import (
     lint_non_noarch_builds,
     lint_package_version,
     lint_pin_subpackages,
-    lint_rattler_noarch_and_runtime_dependencies,
     lint_recipe_have_tests,
     lint_recipe_maintainers,
     lint_recipe_name,
+    lint_recipe_v2_noarch_and_runtime_dependencies,
     lint_require_lower_bound_on_python_version,
     lint_rust_licenses_are_bundled,
     lint_section_order,
@@ -103,7 +103,7 @@ def lintify_meta_yaml(
     meta,
     recipe_dir=None,
     conda_forge=False,
-    is_rattler_build=False,
+    recipe_version: int = 1,
 ) -> Tuple[List[str], List[str]]:
     lints = []
     hints = []
@@ -111,36 +111,36 @@ def lintify_meta_yaml(
 
     # If the recipe_dir exists (no guarantee within this function) , we can
     # find the meta.yaml within it.
-    recipe_name = "meta.yaml" if not is_rattler_build else "recipe.yaml"
+    recipe_name = "meta.yaml" if recipe_version == 1 else "recipe.yaml"
     recipe_fname = os.path.join(recipe_dir or "", recipe_name)
 
-    sources_section = get_section(meta, "source", lints, is_rattler_build)
-    build_section = get_section(meta, "build", lints, is_rattler_build)
+    sources_section = get_section(meta, "source", lints, recipe_version)
+    build_section = get_section(meta, "build", lints, recipe_version)
     requirements_section = get_section(
-        meta, "requirements", lints, is_rattler_build
+        meta, "requirements", lints, recipe_version
     )
     build_requirements = requirements_section.get("build", [])
     run_reqs = requirements_section.get("run", [])
-    if is_rattler_build:
-        test_section = get_section(meta, "tests", lints, is_rattler_build)
+    if recipe_version == 2:
+        test_section = get_section(meta, "tests", lints, recipe_version)
     else:
-        test_section = get_section(meta, "test", lints, is_rattler_build)
-    about_section = get_section(meta, "about", lints, is_rattler_build)
-    extra_section = get_section(meta, "extra", lints, is_rattler_build)
-    package_section = get_section(meta, "package", lints, is_rattler_build)
-    outputs_section = get_section(meta, "outputs", lints, is_rattler_build)
+        test_section = get_section(meta, "test", lints, recipe_version)
+    about_section = get_section(meta, "about", lints, recipe_version)
+    extra_section = get_section(meta, "extra", lints, recipe_version)
+    package_section = get_section(meta, "package", lints, recipe_version)
+    outputs_section = get_section(meta, "outputs", lints, recipe_version)
 
     recipe_dirname = os.path.basename(recipe_dir) if recipe_dir else "recipe"
     is_staged_recipes = recipe_dirname != "recipe"
 
     # 0: Top level keys should be expected
     unexpected_sections = []
-    if not is_rattler_build:
+    if recipe_version == 1:
         expected_keys = EXPECTED_SECTION_ORDER
     else:
         expected_keys = (
-            rattler_linter.EXPECTED_SINGLE_OUTPUT_SECTION_ORDER
-            + rattler_linter.EXPECTED_MULTIPLE_OUTPUT_SECTION_ORDER
+            conda_recipe_v2_linter.EXPECTED_SINGLE_OUTPUT_SECTION_ORDER
+            + conda_recipe_v2_linter.EXPECTED_MULTIPLE_OUTPUT_SECTION_ORDER
         )
 
     for section in major_sections:
@@ -152,10 +152,10 @@ def lintify_meta_yaml(
         major_sections.remove(section)
 
     # 1: Top level meta.yaml keys should have a specific order.
-    lint_section_order(major_sections, lints, is_rattler_build)
+    lint_section_order(major_sections, lints, recipe_version)
 
     # 2: The about section should have a home, license and summary.
-    lint_about_contents(about_section, lints, is_rattler_build)
+    lint_about_contents(about_section, lints, recipe_version)
 
     # 3a: The recipe should have some maintainers.
     # 3b: Maintainers should be a list
@@ -168,14 +168,14 @@ def lintify_meta_yaml(
         outputs_section,
         lints,
         hints,
-        is_rattler_build,
+        recipe_version,
     )
 
     # 5: License cannot be 'unknown.'
     lint_license_cannot_be_unknown(about_section, lints)
 
     # 6: Selectors should be in a tidy form.
-    if not is_rattler_build:
+    if recipe_version == 1:
         # rattler-build does not have selectors in comments form
         lint_selectors_should_be_in_tidy_form(recipe_fname, lints, hints)
 
@@ -197,7 +197,7 @@ def lintify_meta_yaml(
     # 12: License family must be valid (conda-build checks for that)
     # we skip it for rattler builds as it will validate it
     # See more: https://prefix-dev.github.io/rattler-build/latest/reference/recipe_file/#about-section
-    if not is_rattler_build:
+    if recipe_version == 1:
         try:
             ensure_valid_license_family(meta)
         except RuntimeError as e:
@@ -206,12 +206,12 @@ def lintify_meta_yaml(
     # 12a: License family must be valid (conda-build checks for that)
     license = about_section.get("license", "").lower()
     lint_license_family_should_be_valid(
-        about_section, license, NEEDED_FAMILIES, lints, is_rattler_build
+        about_section, license, NEEDED_FAMILIES, lints, recipe_version
     )
 
     # 13: Check that the recipe name is valid
-    if is_rattler_build:
-        rattler_linter.lint_recipe_name(meta, lints)
+    if recipe_version == 2:
+        conda_recipe_v2_linter.lint_recipe_name(meta, lints)
     else:
         lint_recipe_name(
             package_section,
@@ -221,14 +221,14 @@ def lintify_meta_yaml(
     # 14: Run conda-forge specific lints
     if conda_forge:
         run_conda_forge_specific(
-            meta, recipe_dir, lints, hints, is_rattler_build=is_rattler_build
+            meta, recipe_dir, lints, hints, recipe_version=recipe_version
         )
 
     # 15: Check if we are using legacy patterns
     lint_usage_of_legacy_patterns(requirements_section, lints)
 
     # 16: Subheaders should be in the allowed subheadings
-    if not is_rattler_build:
+    if recipe_version == 1:
         lint_subheaders(major_sections, meta, lints)
 
     # 17: Validate noarch
@@ -238,7 +238,7 @@ def lintify_meta_yaml(
     conda_build_config_filename = None
     if recipe_dir:
         cbc_file = "conda_build_config.yaml"
-        if is_rattler_build:
+        if recipe_version == 2:
             cbc_file = "variants.yaml"
 
         conda_build_config_filename = find_local_config_file(
@@ -266,9 +266,9 @@ def lintify_meta_yaml(
 
     # 18: noarch doesn't work with selectors for runtime dependencies
     noarch_platforms = len(forge_yaml.get("noarch_platforms", [])) > 1
-    if is_rattler_build:
+    if recipe_version == 2:
         raw_requirements_section = meta.get("requirements", {})
-        lint_rattler_noarch_and_runtime_dependencies(
+        lint_recipe_v2_noarch_and_runtime_dependencies(
             noarch_value,
             raw_requirements_section,
             build_section,
@@ -285,8 +285,8 @@ def lintify_meta_yaml(
         )
 
     # 19: check version
-    if is_rattler_build:
-        rattler_linter.lint_package_version(meta, lints)
+    if recipe_version == 2:
+        conda_recipe_v2_linter.lint_package_version(meta, lints)
     else:
         lint_package_version(package_section, lints)
 
@@ -306,7 +306,7 @@ def lintify_meta_yaml(
 
     # 24: jinja2 variable references should be {{<one space>var<one space>}}
     lint_jinja_var_references(
-        recipe_fname, hints, is_rattler_build=is_rattler_build
+        recipe_fname, hints, recipe_version=recipe_version
     )
 
     # 25: require a lower bound on python version
@@ -318,17 +318,25 @@ def lintify_meta_yaml(
     # non-subpackages of the recipe. Contact @carterbox for troubleshooting
     # this lint.
     lint_pin_subpackages(
-        meta, outputs_section, package_section, lints, is_rattler_build
+        meta,
+        outputs_section,
+        package_section,
+        lints,
+        recipe_version=recipe_version,
     )
 
     # 27: Check usage of whl files as a source
     lint_check_usage_of_whls(recipe_fname, noarch_value, lints, hints)
 
     # 28: Check that Rust licenses are bundled.
-    lint_rust_licenses_are_bundled(build_requirements, lints, is_rattler_build)
+    lint_rust_licenses_are_bundled(
+        build_requirements, lints, recipe_version=recipe_version
+    )
 
     # 29: Check that go licenses are bundled.
-    lint_go_licenses_are_bundled(build_requirements, lints, is_rattler_build)
+    lint_go_licenses_are_bundled(
+        build_requirements, lints, recipe_version=recipe_version
+    )
 
     # hints
     # 1: suggest pip
@@ -344,7 +352,7 @@ def lintify_meta_yaml(
         conda_forge,
         recipe_fname,
         hints,
-        is_rattler_build,
+        recipe_version=recipe_version,
     )
 
     # 3: suggest fixing all recipe/*.sh shellcheck findings
@@ -360,40 +368,44 @@ def lintify_meta_yaml(
         conda_build_config_filename,
         lints,
         hints,
-        is_rattler_build,
+        recipe_version=recipe_version,
     )
 
     return lints, hints
 
 
 def run_conda_forge_specific(
-    meta, recipe_dir, lints, hints, is_rattler_build: bool = False
+    meta,
+    recipe_dir,
+    lints,
+    hints,
+    recipe_version: int = 1,
 ):
     gh = github.Github(os.environ["GH_TOKEN"])
 
     # Retrieve sections from meta
     package_section = get_section(
-        meta, "package", lints, is_rattler_build=is_rattler_build
+        meta, "package", lints, recipe_version=recipe_version
     )
     extra_section = get_section(
-        meta, "extra", lints, is_rattler_build=is_rattler_build
+        meta, "extra", lints, recipe_version=recipe_version
     )
     sources_section = get_section(
-        meta, "source", lints, is_rattler_build=is_rattler_build
+        meta, "source", lints, recipe_version=recipe_version
     )
     requirements_section = get_section(
-        meta, "requirements", lints, is_rattler_build=is_rattler_build
+        meta, "requirements", lints, recipe_version=recipe_version
     )
     outputs_section = get_section(
-        meta, "outputs", lints, is_rattler_build=is_rattler_build
+        meta, "outputs", lints, recipe_version=recipe_version
     )
 
     # Fetch list of recipe maintainers
     maintainers = extra_section.get("recipe-maintainers", [])
 
     recipe_dirname = os.path.basename(recipe_dir) if recipe_dir else "recipe"
-    if is_rattler_build:
-        recipe_name = rattler_linter.get_recipe_name(meta)
+    if recipe_version == 2:
+        recipe_name = conda_recipe_v2_linter.get_recipe_name(meta)
     else:
         recipe_name = package_section.get("name", "").strip()
     is_staged_recipes = recipe_dirname != "recipe"
@@ -438,7 +450,7 @@ def run_conda_forge_specific(
             )
 
         url = None
-        if is_rattler_build:
+        if recipe_version == 2:
             for source_url in sources_section:
                 if source_url.startswith("https://pypi.io/packages/source/"):
                     url = source_url
@@ -503,7 +515,7 @@ def run_conda_forge_specific(
     host_reqs = requirements_section.get("host") or []
     run_reqs = requirements_section.get("run") or []
     for out in outputs_section:
-        if is_rattler_build:
+        if recipe_version == 2:
             output_requirements = rattler_loader.load_all_requirements(out)
             build_reqs += output_requirements.get("build") or []
             host_reqs += output_requirements.get("host") or []
@@ -638,11 +650,12 @@ def main(
     else:
         meta = get_yaml().load(Path(recipe_file))
 
+    recipe_version = 2 if build_tool == RATTLER_BUILD_TOOL else 1
     results, hints = lintify_meta_yaml(
         meta,
         recipe_dir,
         conda_forge,
-        is_rattler_build=build_tool == RATTLER_BUILD_TOOL,
+        recipe_version=recipe_version,
     )
     validation_errors, validation_hints = lintify_forge_yaml(
         recipe_dir=recipe_dir
diff --git a/conda_smithy/linter/rattler_linter.py b/conda_smithy/linter/conda_recipe_v2_linter.py
similarity index 100%
rename from conda_smithy/linter/rattler_linter.py
rename to conda_smithy/linter/conda_recipe_v2_linter.py
diff --git a/conda_smithy/linter/hints.py b/conda_smithy/linter/hints.py
index 26d90bf9..12cb1e1c 100644
--- a/conda_smithy/linter/hints.py
+++ b/conda_smithy/linter/hints.py
@@ -5,7 +5,7 @@ import subprocess
 import sys
 from glob import glob
 
-from conda_smithy.linter import rattler_linter
+from conda_smithy.linter import conda_recipe_v2_linter
 from conda_smithy.linter.errors import HINT_NO_ARCH
 from conda_smithy.linter.utils import find_local_config_file, is_selector_line
 from conda_smithy.utils import get_yaml
@@ -32,7 +32,7 @@ def hint_suggest_noarch(
     conda_forge,
     recipe_fname,
     hints,
-    is_rattler_build: bool = False,
+    recipe_version: int = 1,
 ):
     if (
         noarch_value is None
@@ -41,8 +41,8 @@ def hint_suggest_noarch(
         and ("pip" in build_reqs)
         and (is_staged_recipes or not conda_forge)
     ):
-        if is_rattler_build:
-            rattler_linter.hint_noarch_usage(
+        if recipe_version == 2:
+            conda_recipe_v2_linter.hint_noarch_usage(
                 build_reqs, raw_requirements_section, hints
             )
         else:
diff --git a/conda_smithy/linter/lints.py b/conda_smithy/linter/lints.py
index fc37bcca..2038d651 100644
--- a/conda_smithy/linter/lints.py
+++ b/conda_smithy/linter/lints.py
@@ -10,7 +10,7 @@ from rattler_build_conda_compat.jinja.jinja import render_recipe_with_context
 from rattler_build_conda_compat.loader import parse_recipe_config_file
 from ruamel.yaml import CommentedSeq
 
-from conda_smithy.linter import rattler_linter
+from conda_smithy.linter import conda_recipe_v2_linter
 from conda_smithy.linter.utils import (
     EXPECTED_SECTION_ORDER,
     FIELDS,
@@ -28,15 +28,19 @@ from conda_smithy.utils import get_yaml
 
 
 def lint_section_order(
-    major_sections: List[str], lints: List[str], is_rattler_build: bool = False
+    major_sections: List[str],
+    lints: List[str],
+    recipe_version: int = 1,
 ):
-    if not is_rattler_build:
+    if recipe_version == 1:
         order = EXPECTED_SECTION_ORDER
     else:
         if "outputs" in major_sections:
-            order = rattler_linter.EXPECTED_MULTIPLE_OUTPUT_SECTION_ORDER
+            order = (
+                conda_recipe_v2_linter.EXPECTED_MULTIPLE_OUTPUT_SECTION_ORDER
+            )
         else:
-            order = rattler_linter.EXPECTED_SINGLE_OUTPUT_SECTION_ORDER
+            order = conda_recipe_v2_linter.EXPECTED_SINGLE_OUTPUT_SECTION_ORDER
     section_order_sorted = sorted(major_sections, key=order.index)
 
     if major_sections != section_order_sorted:
@@ -51,9 +55,9 @@ def lint_section_order(
         )
 
 
-def lint_about_contents(about_section, lints, is_rattler_build: bool = False):
+def lint_about_contents(about_section, lints, recipe_version: int = 1):
     expected_section = [
-        "homepage" if is_rattler_build else "home",
+        "homepage" if recipe_version == 2 else "home",
         "license",
         "summary",
     ]
@@ -84,10 +88,10 @@ def lint_recipe_have_tests(
     outputs_section,
     lints,
     hints,
-    is_rattler_build: bool = False,
+    recipe_version: int = 1,
 ):
-    if is_rattler_build:
-        rattler_linter.lint_recipe_tests(
+    if recipe_version == 2:
+        conda_recipe_v2_linter.lint_recipe_tests(
             recipe_dir, test_section, outputs_section, lints, hints
         )
         return
@@ -242,12 +246,12 @@ def lint_license_family_should_be_valid(
     license: str,
     needed_families: List[str],
     lints: List[str],
-    is_rattler_build: bool = False,
+    recipe_version: int = 1,
 ) -> None:
     lint_msg = "license_file entry is missing, but is required."
     license_file = about_section.get("license_file", None)
     if not license_file:
-        if is_rattler_build:
+        if recipe_version == 2:
             lints.append(lint_msg)
         else:
             license_family = about_section.get(
@@ -313,7 +317,7 @@ def lint_noarch(noarch_value: Optional[str], lints):
             )
 
 
-def lint_rattler_noarch_and_runtime_dependencies(
+def lint_recipe_v2_noarch_and_runtime_dependencies(
     noarch_value: Optional[Literal["python", "generic"]],
     raw_requirements_section: Dict[str, Any],
     build_section: Dict[str, Any],
@@ -321,7 +325,7 @@ def lint_rattler_noarch_and_runtime_dependencies(
     lints: List[str],
 ) -> None:
     if noarch_value:
-        rattler_linter.lint_usage_of_selectors_for_noarch(
+        conda_recipe_v2_linter.lint_usage_of_selectors_for_noarch(
             noarch_value,
             raw_requirements_section,
             build_section,
@@ -413,11 +417,13 @@ def lint_legacy_usage_of_compilers(build_reqs, lints):
 
 
 def lint_single_space_in_pinned_requirements(
-    requirements_section, lints, is_rattler_build: bool = False
+    requirements_section,
+    lints,
+    recipe_version: int = 1,
 ):
     for section, requirements in requirements_section.items():
         for requirement in requirements or []:
-            if is_rattler_build:
+            if recipe_version == 2:
                 req = requirement
                 symbol_to_check = "${{"
             else:
@@ -506,13 +512,13 @@ def lint_non_noarch_builds(
                         )
 
 
-def lint_jinja_var_references(
-    meta_fname, hints, is_rattler_build: bool = False
-):
+def lint_jinja_var_references(meta_fname, hints, recipe_version: int = 1):
     bad_vars = []
     bad_lines = []
     jinja_pattern = (
-        JINJA_VAR_PAT if not is_rattler_build else rattler_linter.JINJA_VAR_PAT
+        JINJA_VAR_PAT
+        if recipe_version == 1
+        else conda_recipe_v2_linter.JINJA_VAR_PAT
     )
     if os.path.exists(meta_fname):
         with open(meta_fname) as fh:
@@ -526,7 +532,7 @@ def lint_jinja_var_references(
         if bad_vars:
             hint_message = (
                 "``{{<one space><variable name><one space>}}``"
-                if not is_rattler_build
+                if recipe_version == 1
                 else "``${{<one space><variable name><one space>}}``"
             )
             hints.append(
@@ -557,9 +563,9 @@ def lint_pin_subpackages(
     outputs_section,
     package_section,
     lints,
-    is_rattler_build: bool = False,
+    recipe_version: int = 1,
 ):
-    if is_rattler_build:
+    if recipe_version == 2:
         meta = render_recipe_with_context(meta)
         # use the rendered versions here
         package_section = meta.get("package", {})
@@ -567,7 +573,7 @@ def lint_pin_subpackages(
 
     subpackage_names = []
     for out in outputs_section:
-        if is_rattler_build:
+        if recipe_version == 2:
             if out.get("package", {}).get("name"):
                 subpackage_names.append(out["package"]["name"])
         elif "name" in out:
@@ -604,7 +610,7 @@ def lint_pin_subpackages(
                 )
 
     def check_pins_build_and_requirements(top_level):
-        if not is_rattler_build:
+        if recipe_version == 1:
             if "build" in top_level and "run_exports" in top_level["build"]:
                 check_pins(top_level["build"]["run_exports"])
             if (
@@ -679,12 +685,12 @@ def lint_check_usage_of_whls(meta_fname, noarch_value, lints, hints):
 def lint_rust_licenses_are_bundled(
     build_reqs: Optional[List[str]],
     lints: List[str],
-    is_rattler_build: bool = False,
+    recipe_version: int = 1,
 ):
     if not build_reqs:
         return
 
-    if is_rattler_build:
+    if recipe_version == 2:
         has_rust = "${{ compiler('rust') }}" in build_reqs
     else:
         has_rust = "{{ compiler('rust') }}" in build_reqs
@@ -699,12 +705,12 @@ def lint_rust_licenses_are_bundled(
 def lint_go_licenses_are_bundled(
     build_reqs: Optional[List[str]],
     lints: List[str],
-    is_rattler_build: bool = False,
+    recipe_version: int = 1,
 ):
     if not build_reqs:
         return
 
-    if is_rattler_build:
+    if recipe_version == 2:
         has_go = "${{ compiler('go') }}" in build_reqs
     else:
         has_go = "{{ compiler('go') }}" in build_reqs
@@ -723,16 +729,16 @@ def lint_stdlib(
     conda_build_config_filename,
     lints,
     hints,
-    is_rattler_build: bool = False,
+    recipe_version: int = 1,
 ):
     global_build_reqs = requirements_section.get("build") or []
     global_run_reqs = requirements_section.get("run") or []
-    if is_rattler_build:
+    if recipe_version == 2:
         global_constraints = requirements_section.get("run_constraints") or []
     else:
         global_constraints = requirements_section.get("run_constrained") or []
 
-    if is_rattler_build:
+    if recipe_version == 2:
         jinja_stdlib_c = '`${{ stdlib("c") }}`'
     else:
         jinja_stdlib_c = '`{{ stdlib("c") }}`'
@@ -743,18 +749,18 @@ def lint_stdlib(
         "each output of the recipe using a compiler. For further details, please "
         "see https://github.com/conda-forge/conda-forge.github.io/issues/2102."
     )
-    if not is_rattler_build:
+    if recipe_version == 1:
         pat_compiler_stub = re.compile(
             "(m2w64_)?(c|cxx|fortran|rust)_compiler_stub"
         )
     else:
         pat_compiler_stub = re.compile(r"^\${{ compiler\(")
 
-    outputs = get_section(meta, "outputs", lints, is_rattler_build)
+    outputs = get_section(meta, "outputs", lints, recipe_version)
     output_reqs = [x.get("requirements", {}) for x in outputs]
 
     # deal with cb2 recipes (no build/host/run distinction)
-    if not is_rattler_build:
+    if recipe_version == 1:
         output_reqs = [
             {"host": x, "run": x} if isinstance(x, CommentedSeq) else x
             for x in output_reqs
@@ -763,7 +769,7 @@ def lint_stdlib(
     # collect output requirements
     output_build_reqs = [x.get("build", []) or [] for x in output_reqs]
     output_run_reqs = [x.get("run", []) or [] for x in output_reqs]
-    if is_rattler_build:
+    if recipe_version == 2:
         output_contraints = [
             x.get("run_constraints", []) or [] for x in output_reqs
         ]
@@ -788,7 +794,7 @@ def lint_stdlib(
     # this check needs to be done per output --> use separate (unflattened) requirements
     for build_reqs in all_build_reqs:
         has_compiler = any(pat_compiler_stub.match(rq) for rq in build_reqs)
-        stdlib_stub = "c_stdlib_stub" if not is_rattler_build else "${{ stdlib"
+        stdlib_stub = "c_stdlib_stub" if recipe_version == 1 else "${{ stdlib"
         if has_compiler and stdlib_stub not in build_reqs:
             if stdlib_lint not in lints:
                 lints.append(stdlib_lint)
@@ -821,7 +827,7 @@ def lint_stdlib(
     # stdlib issues in CBC ( conda-build-config )
     cbc_osx = {}
 
-    if is_rattler_build:
+    if recipe_version == 2:
         platform_namespace = {
             "unix": True,
             "osx": True,
diff --git a/conda_smithy/linter/utils.py b/conda_smithy/linter/utils.py
index eb0321df..6d7fdd46 100644
--- a/conda_smithy/linter/utils.py
+++ b/conda_smithy/linter/utils.py
@@ -47,11 +47,13 @@ CONDA_BUILD_TOOL = "conda-build"
 RATTLER_BUILD_TOOL = "rattler-build"
 
 
-def get_section(parent, name, lints, is_rattler_build=False):
-    if not is_rattler_build:
+def get_section(parent, name, lints, recipe_version: int = 1):
+    if recipe_version == 1:
         return get_meta_section(parent, name, lints)
+    elif recipe_version == 2:
+        return get_recipe_v2_section(parent, name)
     else:
-        return get_rattler_section(parent, name)
+        raise ValueError(f"Unknown recipe version: {recipe_version}")
 
 
 def get_meta_section(parent, name, lints):
@@ -70,7 +72,7 @@ def get_meta_section(parent, name, lints):
     return section
 
 
-def get_rattler_section(meta, name) -> Union[Dict, List[Dict], List[str]]:
+def get_recipe_v2_section(meta, name) -> Union[Dict, List[Dict]]:
     if name == "requirements":
         return rattler_loader.load_all_requirements(meta)
     elif name == "tests":


Test patch:
diff --git a/tests/test_lint_recipe.py b/tests/test_lint_recipe.py
index ca111fa8..1848c16d 100644
--- a/tests/test_lint_recipe.py
+++ b/tests/test_lint_recipe.py
@@ -188,7 +188,7 @@ def test_osx_noarch_hint(where):
         assert not any(h.startswith(avoid_message) for h in hints)
 
 
-def test_rattler_osx_noarch_hint():
+def test_recipe_v2_osx_noarch_hint():
     # don't warn on packages that are using __osx as a noarch-marker, see
     # https://conda-forge.org/docs/maintainer/knowledge_base/#noarch-packages-with-os-specific-dependencies
     avoid_message = "You're setting a constraint on the `__osx` virtual"
@@ -324,8 +324,8 @@ MACOSX_SDK_VERSION:         # [osx]
             assert any(lint.startswith(exp_lint) for lint in lints)
 
 
-@pytest.mark.parametrize("is_rattler_build,", [(False,), (True,)])
-def test_license_file_required(is_rattler_build):
+@pytest.mark.parametrize("recipe_version", [(1), (2)])
+def test_license_file_required(recipe_version: int):
     meta = {
         "about": {
             "home": "a URL",
@@ -334,14 +334,14 @@ def test_license_file_required(is_rattler_build):
         }
     }
     lints, hints = linter.lintify_meta_yaml(
-        meta, is_rattler_build=is_rattler_build
+        meta, recipe_version=recipe_version
     )
     expected_message = "license_file entry is missing, but is required."
     assert expected_message in lints
 
 
-@pytest.mark.parametrize("is_rattler_build,", [(False,), (True,)])
-def test_license_file_empty(is_rattler_build):
+@pytest.mark.parametrize("recipe_version", [(1), (2)])
+def test_license_file_empty(recipe_version: int):
     meta = {
         "about": {
             "home": "a URL",
@@ -352,7 +352,7 @@ def test_license_file_empty(is_rattler_build):
         }
     }
     lints, hints = linter.lintify_meta_yaml(
-        meta, is_rattler_build=is_rattler_build
+        meta, recipe_version=recipe_version
     )
     expected_message = "license_file entry is missing, but is required."
     assert expected_message in lints
@@ -518,9 +518,9 @@ class TestLinter(unittest.TestCase):
         expected_msg = "The top level meta key sources is unexpected"
         self.assertIn(expected_msg, lints)
 
-    def test_rattler_bad_top_level(self):
+    def test_recipe_v2_bad_top_level(self):
         meta = OrderedDict([["package", {}], ["build", {}], ["sources", {}]])
-        lints, hints = linter.lintify_meta_yaml(meta, is_rattler_build=True)
+        lints, hints = linter.lintify_meta_yaml(meta, recipe_version=2)
         expected_msg = "The top level meta key sources is unexpected"
         self.assertIn(expected_msg, lints)
 
@@ -575,7 +575,7 @@ class TestLinter(unittest.TestCase):
 
     def test_missing_about_homepage_empty(self):
         meta = {"about": {"homepage": "", "summary": "", "license": ""}}
-        lints, hints = linter.lintify_meta_yaml(meta, is_rattler_build=True)
+        lints, hints = linter.lintify_meta_yaml(meta, recipe_version=2)
         expected_message = (
             "The homepage item is expected in the about section."
         )
@@ -691,25 +691,25 @@ class TestLinter(unittest.TestCase):
             "It looks like the 'foobar' output doesn't have any tests.", hints
         )
 
-    def test_rattler_test_section(self):
+    def test_recipe_v2_test_section(self):
         expected_message = "The recipe must have some tests."
 
-        lints, hints = linter.lintify_meta_yaml({}, is_rattler_build=True)
+        lints, hints = linter.lintify_meta_yaml({}, recipe_version=2)
         self.assertIn(expected_message, lints)
 
         lints, hints = linter.lintify_meta_yaml(
-            {"tests": {"script": "sys"}}, is_rattler_build=True
+            {"tests": {"script": "sys"}}, recipe_version=2
         )
         self.assertNotIn(expected_message, lints)
 
         lints, hints = linter.lintify_meta_yaml(
-            {"outputs": [{"name": "foo"}]}, is_rattler_build=True
+            {"outputs": [{"name": "foo"}]}, recipe_version=2
         )
         self.assertIn(expected_message, lints)
 
         lints, hints = linter.lintify_meta_yaml(
             {"outputs": [{"name": "foo", "tests": {"python": "sys"}}]},
-            is_rattler_build=True,
+            recipe_version=2,
         )
         self.assertNotIn(expected_message, lints)
 
@@ -722,7 +722,7 @@ class TestLinter(unittest.TestCase):
                     },
                 ]
             },
-            is_rattler_build=True,
+            recipe_version=2,
         )
         self.assertNotIn(expected_message, lints)
         self.assertIn(
@@ -744,21 +744,21 @@ class TestLinter(unittest.TestCase):
             lints, hints = linter.lintify_meta_yaml({}, recipe_dir)
             self.assertNotIn(expected_message, lints)
 
-    def test_rattler_test_section_with_recipe(self):
+    def test_recipe_v2_test_section_with_recipe(self):
         # rattler-build support legacy run_test.py, so we need to test it
 
         expected_message = "The recipe must have some tests."
 
         with tmp_directory() as recipe_dir:
             lints, hints = linter.lintify_meta_yaml(
-                {}, recipe_dir, is_rattler_build=True
+                {}, recipe_dir, recipe_version=2
             )
             self.assertIn(expected_message, lints)
 
             with open(os.path.join(recipe_dir, "run_test.py"), "w") as fh:
                 fh.write("# foo")
             lints, hints = linter.lintify_meta_yaml(
-                {}, recipe_dir, is_rattler_build=True
+                {}, recipe_dir, recipe_version=2
             )
             self.assertNotIn(expected_message, lints)
 
@@ -791,7 +791,7 @@ class TestLinter(unittest.TestCase):
             _, hints = linter.lintify_meta_yaml({}, recipe_dir)
             self.assertTrue(any(h.startswith(expected_message) for h in hints))
 
-    def test_rattler_jinja2_vars(self):
+    def test_recipe_v2_jinja2_vars(self):
         expected_message = (
             "Jinja2 variable references are suggested to take a ``${{<one space>"
             "<variable name><one space>}}`` form. See lines %s."
@@ -818,7 +818,7 @@ class TestLinter(unittest.TestCase):
                 )
 
             _, hints = linter.lintify_meta_yaml(
-                {}, recipe_dir, is_rattler_build=True
+                {}, recipe_dir, recipe_version=2
             )
             self.assertTrue(any(h.startswith(expected_message) for h in hints))
 
@@ -1121,7 +1121,7 @@ class TestLinter(unittest.TestCase):
                             """
             )
 
-    def test_rattler_noarch_selectors(self):
+    def test_recipe_v2_noarch_selectors(self):
         expected_start = "`noarch` packages can't have"
 
         with tmp_directory() as recipe_dir:
@@ -1688,9 +1688,9 @@ noarch_platforms:
         )
         self.assertIn(expected_message, lints)
 
-    def test_rattler_recipe_name(self):
+    def test_recipe_v2_recipe_name(self):
         meta = {"package": {"name": "mp++"}}
-        lints, _ = linter.lintify_meta_yaml(meta, is_rattler_build=True)
+        lints, _ = linter.lintify_meta_yaml(meta, recipe_version=2)
         expected_message = (
             "Recipe name has invalid characters. only lowercase alpha, "
             "numeric, underscores, hyphens and dots allowed"
@@ -1702,7 +1702,7 @@ noarch_platforms:
             "package": {"name": "${{ blah }}"},
         }  # noqa
         lints, _ = linter.lintify_meta_yaml(
-            meta_with_context, is_rattler_build=True
+            meta_with_context, recipe_version=2
         )
         expected_message = (
             "Recipe name has invalid characters. only lowercase alpha, "
@@ -1712,7 +1712,7 @@ noarch_platforms:
 
         meta_with_context = {"recipe": {"name": "mp++"}, "outputs": []}  # noqa
         lints, _ = linter.lintify_meta_yaml(
-            meta_with_context, is_rattler_build=True
+            meta_with_context, recipe_version=2
         )
         expected_message = (
             "Recipe name has invalid characters. only lowercase alpha, "
@@ -1998,17 +1998,17 @@ noarch_platforms:
         lints, hints = linter.lintify_meta_yaml(meta)
         assert any(lint.startswith(expected_message) for lint in lints)
 
-    def test_rattler_version(self):
+    def test_recipe_v2_version(self):
         meta = {"package": {"name": "python", "version": "3.6.4"}}
         expected_message = "Package version 3.6.4 doesn't match conda spec"
-        lints, hints = linter.lintify_meta_yaml(meta, is_rattler_build=True)
+        lints, hints = linter.lintify_meta_yaml(meta, recipe_version=2)
         self.assertNotIn(expected_message, lints)
 
         meta = {"package": {"name": "python", "version": "2.0.0~alpha0"}}
         expected_message = (
             "Package version 2.0.0~alpha0 doesn't match conda spec"
         )
-        lints, hints = linter.lintify_meta_yaml(meta, is_rattler_build=True)
+        lints, hints = linter.lintify_meta_yaml(meta, recipe_version=2)
         assert any(lint.startswith(expected_message) for lint in lints)
 
         # when having multiple outputs it should use recipe keyword
@@ -2016,16 +2016,16 @@ noarch_platforms:
         expected_message = (
             "Package version 2.0.0~alpha0 doesn't match conda spec"
         )
-        lints, hints = linter.lintify_meta_yaml(meta, is_rattler_build=True)
+        lints, hints = linter.lintify_meta_yaml(meta, recipe_version=2)
         assert any(lint.startswith(expected_message) for lint in lints)
 
-    def test_rattler_version_with_context(self):
+    def test_recipe_v2_version_with_context(self):
         meta = {
             "context": {"foo": "3.6.4"},
             "package": {"name": "python", "version": "${{ foo }}"},
         }
         expected_message = "Package version 3.6.4 doesn't match conda spec"
-        lints, hints = linter.lintify_meta_yaml(meta, is_rattler_build=True)
+        lints, hints = linter.lintify_meta_yaml(meta, recipe_version=2)
         self.assertNotIn(expected_message, lints)
 
         meta = {
@@ -2035,7 +2035,7 @@ noarch_platforms:
         expected_message = (
             "Package version 2.0.0~alpha0 doesn't match conda spec"
         )
-        lints, hints = linter.lintify_meta_yaml(meta, is_rattler_build=True)
+        lints, hints = linter.lintify_meta_yaml(meta, recipe_version=2)
         assert any(lint.startswith(expected_message) for lint in lints)
 
     @unittest.skipUnless(is_gh_token_set(), "GH_TOKEN not set")
@@ -2108,7 +2108,7 @@ noarch_platforms:
         ]
         self.assertEqual(expected_messages, filtered_lints)
 
-    def test_rattler_single_space_pins(self):
+    def test_recipe_v2_single_space_pins(self):
         meta = {
             "requirements": {
                 "build": ["${{ compiler('c') }}", "python >=3", "pip   19"],
@@ -2116,7 +2116,7 @@ noarch_platforms:
                 "run": ["xonsh>1.0", "conda= 4.*", "conda-smithy<=54.*"],
             }
         }
-        lints, hints = linter.lintify_meta_yaml(meta, is_rattler_build=True)
+        lints, hints = linter.lintify_meta_yaml(meta, recipe_version=2)
         filtered_lints = [
             lint for lint in lints if lint.startswith("``requirements: ")
         ]
@@ -2247,12 +2247,12 @@ noarch_platforms:
         self.assertTrue(any(hint.startswith(expected) for hint in hints))
 
 
-@pytest.mark.parametrize("is_rattler_build", [True, False])
-def test_rust_license_bundling(is_rattler_build: bool):
+@pytest.mark.parametrize("recipe_version", [2, 1])
+def test_rust_license_bundling(recipe_version: int):
     # Case where go-licenses is missing
     compiler = (
         "${{ compiler('rust') }}"
-        if is_rattler_build
+        if recipe_version == 2
         else "{{ compiler('rust') }}"
     )
     meta_missing_license = {
@@ -2260,7 +2260,7 @@ def test_rust_license_bundling(is_rattler_build: bool):
     }
 
     lints, hints = linter.lintify_meta_yaml(
-        meta_missing_license, is_rattler_build=is_rattler_build
+        meta_missing_license, recipe_version=recipe_version
     )
     expected_msg = (
         "Rust packages must include the licenses of the Rust dependencies. "
@@ -2274,23 +2274,25 @@ def test_rust_license_bundling(is_rattler_build: bool):
     }
 
     lints, hints = linter.lintify_meta_yaml(
-        meta_with_license, is_rattler_build=is_rattler_build
+        meta_with_license, recipe_version=recipe_version
     )
     assert expected_msg not in lints
 
 
-@pytest.mark.parametrize("is_rattler_build", [True, False])
-def test_go_license_bundling(is_rattler_build: bool):
+@pytest.mark.parametrize("recipe_version", [2, 1])
+def test_go_license_bundling(recipe_version: int):
     # Case where go-licenses is missing
     compiler = (
-        "${{ compiler('go') }}" if is_rattler_build else "{{ compiler('go') }}"
+        "${{ compiler('go') }}"
+        if recipe_version == 2
+        else "{{ compiler('go') }}"
     )
     meta_missing_license = {
         "requirements": {"build": [compiler]},
     }
 
     lints, hints = linter.lintify_meta_yaml(
-        meta_missing_license, is_rattler_build=is_rattler_build
+        meta_missing_license, recipe_version=recipe_version
     )
     expected_msg = (
         "Go packages must include the licenses of the Go dependencies. "
@@ -2304,7 +2306,7 @@ def test_go_license_bundling(is_rattler_build: bool):
     }
 
     lints, hints = linter.lintify_meta_yaml(
-        meta_with_license, is_rattler_build=is_rattler_build
+        meta_with_license, recipe_version=recipe_version
     )
     assert expected_msg not in lints
 
@@ -2590,15 +2592,15 @@ def test_lint_wheels(tmp_path, yaml_block, annotation):
         assert any(expected_message in hint for hint in hints)
 
 
-@pytest.mark.parametrize("is_rattler_build", [False, True])
-def test_pin_compatible_in_run_exports(is_rattler_build: bool):
+@pytest.mark.parametrize("recipe_version", [1, 2])
+def test_pin_compatible_in_run_exports(recipe_version: int):
     meta = {
         "package": {
             "name": "apackage",
         }
     }
 
-    if is_rattler_build:
+    if recipe_version == 2:
         meta["requirements"] = {
             "run_exports": ['${{ pin_compatible("apackage") }}'],
         }
@@ -2608,15 +2610,15 @@ def test_pin_compatible_in_run_exports(is_rattler_build: bool):
         }
 
     lints, hints = linter.lintify_meta_yaml(
-        meta, is_rattler_build=is_rattler_build
+        meta, recipe_version=recipe_version
     )
     expected = "pin_subpackage should be used instead"
     assert any(lint.startswith(expected) for lint in lints)
 
 
-@pytest.mark.parametrize("is_rattler_build", [False, True])
-def test_pin_compatible_in_run_exports_output(is_rattler_build: bool):
-    if is_rattler_build:
+@pytest.mark.parametrize("recipe_version", [1, 2])
+def test_pin_compatible_in_run_exports_output(recipe_version: int):
+    if recipe_version == 2:
         meta = {
             "recipe": {
                 "name": "apackage",
@@ -2648,7 +2650,7 @@ def test_pin_compatible_in_run_exports_output(is_rattler_build: bool):
         }
 
     lints, hints = linter.lintify_meta_yaml(
-        meta, is_rattler_build=is_rattler_build
+        meta, recipe_version=recipe_version
     )
     expected = "pin_compatible should be used instead"
     assert any(lint.startswith(expected) for lint in lints)