Repository: conda-forge/conda-smithy

Issue:
WIP CFEP-13 validation and some various bug fixes
Enables the new CFEP-13 package validation. 

<!--
Thank you for pull request.
Below are a few things we ask you kindly to self-check before getting a review. Remove checks that are not relevant.
-->
Checklist
* [x] Added a ``news`` entry

<!--
Please note any issues this fixes using [closing keywords]( https://help.github.com/articles/closing-issues-using-keywords/ ):
-->

<!--
Please add any other relevant info below:
-->



Discussion:

Resolving patch:
diff --git a/conda_smithy/cli.py b/conda_smithy/cli.py
--- a/conda_smithy/cli.py
+++ b/conda_smithy/cli.py
@@ -566,7 +566,7 @@ class GenerateFeedstockToken(Subcommand):
     def __init__(self, parser):
         super(GenerateFeedstockToken, self).__init__(
             parser,
-            "Generate a feedstock token at ~/.conda-smithy/{user or org}_{project}_feedstock.token",
+            "Generate a feedstock token at ~/.conda-smithy/{user or org}_{project}.token",
         )
         scp = self.subcommand_parser
         scp.add_argument(
@@ -594,7 +594,7 @@ def __call__(self, args):
 
         generate_and_write_feedstock_token(owner, repo)
         print(
-            "Your feedstock token has been generated at ~/.conda-smithy/%s_%s_feedstock.token\n"
+            "Your feedstock token has been generated at ~/.conda-smithy/%s_%s.token\n"
             "This token is stored in plaintext so be careful!" % (owner, repo)
         )
 
diff --git a/conda_smithy/configure_feedstock.py b/conda_smithy/configure_feedstock.py
--- a/conda_smithy/configure_feedstock.py
+++ b/conda_smithy/configure_feedstock.py
@@ -699,6 +699,22 @@ def _render_ci_provider(
         else:
             forge_config["upload_script"] = "upload_or_check_non_existence"
 
+        # if the recipe has its own conda_forge_ci_setup package, then
+        # install that
+        if os.path.exists(
+            os.path.join(
+                forge_dir,
+                forge_config["recipe_dir"],
+                "conda_forge_ci_setup",
+                "__init__.py",
+            )
+        ) and os.path.exists(
+            os.path.join(forge_dir, forge_config["recipe_dir"], "setup.py",)
+        ):
+            forge_config["local_ci_setup"] = True
+        else:
+            forge_config["local_ci_setup"] = False
+
         # hook for extending with whatever platform specific junk we need.
         #     Function passed in as argument
         for platform, enable in zip(platforms, enable_platform):
@@ -1382,7 +1398,8 @@ def _load_forge_config(forge_dir, exclusive_config_file):
         },
         "recipe_dir": "recipe",
         "skip_render": [],
-        "bot": {"automerge": False,},
+        "bot": {"automerge": False},
+        "conda_forge_output_validation": False,
     }
 
     forge_yml = os.path.join(forge_dir, "conda-forge.yml")
@@ -1731,7 +1748,6 @@ def main(
     check_version_uptodate(r, "conda-smithy", __version__, error_on_warn)
 
     forge_dir = os.path.abspath(forge_file_directory)
-
     if exclusive_config_file is not None:
         exclusive_config_file = os.path.join(forge_dir, exclusive_config_file)
         if not os.path.exists(exclusive_config_file):
@@ -1743,6 +1759,7 @@ def main(
         )
 
     config = _load_forge_config(forge_dir, exclusive_config_file)
+    config["feedstock_name"] = os.path.basename(forge_dir)
 
     for each_ci in ["travis", "circle", "appveyor", "drone"]:
         if config[each_ci].pop("enabled", None):
diff --git a/conda_smithy/feedstock_tokens.py b/conda_smithy/feedstock_tokens.py
--- a/conda_smithy/feedstock_tokens.py
+++ b/conda_smithy/feedstock_tokens.py
@@ -12,7 +12,7 @@
 The `generate-feedstock-token` command must be called before the `register-feedstock-token`
 command. It generates a random token and writes it to
 
-    ~/.conda-smithy/{user or org}_{repo w/o '-feedstock'}_feedstock.token
+    ~/.conda-smithy/{user or org}_{repo}.token
 
 Then when you call `register-feedstock-token`, the generated token is placed
 as a secret variable on the CI services. It is also hashed using `scrypt` and
@@ -32,17 +32,10 @@
 import scrypt
 
 
-def _munge_project(project):
-    if project.endswith("-feedstock"):
-        return project[: -len("-feedstock")]
-    else:
-        return project
-
-
 def generate_and_write_feedstock_token(user, project):
     """Generate a feedstock token and write it to
 
-        ~/.conda-smithy/{user or org}_{repo w/o '-feedstock'}_feedstock.token
+        ~/.conda-smithy/{user or org}_{repo}_feedstock.token
 
     This function will fail if the token file already exists.
     """
@@ -54,9 +47,7 @@ def generate_and_write_feedstock_token(user, project):
         try:
             token = secrets.token_hex(32)
             pth = os.path.join(
-                "~",
-                ".conda-smithy",
-                "%s_%s_feedstock.token" % (user, _munge_project(project)),
+                "~", ".conda-smithy", "%s_%s.token" % (user, project),
             )
             pth = os.path.expanduser(pth)
             if os.path.exists(pth):
@@ -95,7 +86,7 @@ def generate_and_write_feedstock_token(user, project):
 def read_feedstock_token(user, project):
     """Read the feedstock token from
 
-        ~/.conda-smithy/{user or org}_{repo w/o '-feedstock'}_feedstock.token
+        ~/.conda-smithy/{user or org}_{repo}.token
 
     In order to not spill any tokens to stdout/stderr, this function
     should be used in a `try...except` block with stdout/stderr redirected
@@ -106,25 +97,22 @@ def read_feedstock_token(user, project):
 
     # read the token
     user_token_pth = os.path.join(
-        "~",
-        ".conda-smithy",
-        "%s_%s_feedstock.token" % (user, _munge_project(project)),
+        "~", ".conda-smithy", "%s_%s.token" % (user, project),
     )
     user_token_pth = os.path.expanduser(user_token_pth)
 
     if not os.path.exists(user_token_pth):
-        err_msg = (
-            "No token found in '~/.conda-smithy/%s_%s_feedstock.token'"
-            % (user, _munge_project(project))
+        err_msg = "No token found in '~/.conda-smithy/%s_%s.token'" % (
+            user,
+            project,
         )
     else:
         with open(user_token_pth, "r") as fp:
             feedstock_token = fp.read().strip()
         if not feedstock_token:
             err_msg = (
-                "Empty token found in '~/.conda-smithy/"
-                "%s_%s_feedstock.token'"
-            ) % (user, _munge_project(project))
+                "Empty token found in '~/.conda-smithy/" "%s_%s.token'"
+            ) % (user, project)
             feedstock_token = None
     return feedstock_token, err_msg
 
@@ -158,9 +146,7 @@ def feedstock_token_exists(user, project, token_repo):
                 .replace("${GH_TOKEN}", github_token)
             )
             git.Repo.clone_from(_token_repo, tmpdir, depth=1)
-            token_file = os.path.join(
-                tmpdir, "tokens", _munge_project(project) + ".json",
-            )
+            token_file = os.path.join(tmpdir, "tokens", project + ".json",)
 
             if os.path.exists(token_file):
                 exists = True
@@ -217,9 +203,7 @@ def is_valid_feedstock_token(user, project, feedstock_token, token_repo):
                 .replace("${GH_TOKEN}", github_token)
             )
             git.Repo.clone_from(_token_repo, tmpdir, depth=1)
-            token_file = os.path.join(
-                tmpdir, "tokens", _munge_project(project) + ".json",
-            )
+            token_file = os.path.join(tmpdir, "tokens", project + ".json",)
 
             if os.path.exists(token_file):
                 with open(token_file, "r") as fp:
@@ -294,9 +278,7 @@ def register_feedstock_token(user, project, token_repo):
                 .replace("${GH_TOKEN}", github_token)
             )
             repo = git.Repo.clone_from(_token_repo, tmpdir, depth=1)
-            token_file = os.path.join(
-                tmpdir, "tokens", _munge_project(project) + ".json",
-            )
+            token_file = os.path.join(tmpdir, "tokens", project + ".json",)
 
             # don't overwrite existing tokens
             # check again since there might be a race condition


Test patch:
diff --git a/tests/test_feedstock_tokens.py b/tests/test_feedstock_tokens.py
--- a/tests/test_feedstock_tokens.py
+++ b/tests/test_feedstock_tokens.py
@@ -31,8 +31,8 @@ def test_feedstock_tokens_roundtrip(
     )
 
     user = "foo"
-    pth = os.path.expanduser("~/.conda-smithy/foo_bar_feedstock.token")
-    token_json_pth = os.path.join(tmpdir, "tokens", "bar.json")
+    pth = os.path.expanduser("~/.conda-smithy/foo_%s.token" % project)
+    token_json_pth = os.path.join(tmpdir, "tokens", "%s.json" % project)
     os.makedirs(os.path.join(tmpdir, "tokens"), exist_ok=True)
 
     try:
@@ -92,7 +92,7 @@ def test_is_valid_feedstock_token_badtoken(
     user = "conda-forge"
     feedstock_token = "akdjhfl"
 
-    token_pth = os.path.join(tmpdir, "tokens", "bar.json")
+    token_pth = os.path.join(tmpdir, "tokens", "%s.json" % project)
     os.makedirs(os.path.dirname(token_pth), exist_ok=True)
     with open(token_pth, "w") as fp:
         json.dump({"salt": b"adf".hex(), "hashed_token": b"fgh".hex()}, fp)
@@ -105,7 +105,7 @@ def test_generate_and_write_feedstock_token():
     user = "bar"
     repo = "foo"
 
-    pth = os.path.expanduser("~/.conda-smithy/bar_foo_feedstock.token")
+    pth = os.path.expanduser("~/.conda-smithy/bar_foo.token")
 
     try:
         generate_and_write_feedstock_token(user, repo)
@@ -123,7 +123,7 @@ def test_generate_and_write_feedstock_token():
 def test_read_feedstock_token():
     user = "bar"
     repo = "foo"
-    pth = os.path.expanduser("~/.conda-smithy/bar_foo_feedstock.token")
+    pth = os.path.expanduser("~/.conda-smithy/bar_foo.token")
 
     # no token
     token, err = read_feedstock_token(user, repo)
@@ -155,7 +155,7 @@ def test_read_feedstock_token():
 @pytest.mark.parametrize("retval", [True, False])
 @pytest.mark.parametrize("project", ["bar", "bar-feedstock"])
 @pytest.mark.parametrize(
-    "repo", ["GITHUB_TOKEN", "${GITHUB_TOKEN}", "GH_TOKEN", "${GH_TOKEN}"]
+    "repo", ["$GITHUB_TOKEN", "${GITHUB_TOKEN}", "$GH_TOKEN", "${GH_TOKEN}"]
 )
 @mock.patch("conda_smithy.feedstock_tokens.tempfile")
 @mock.patch("conda_smithy.feedstock_tokens.git")
@@ -171,19 +171,21 @@ def test_feedstock_token_exists(
     user = "foo"
     os.makedirs(os.path.join(tmpdir, "tokens"), exist_ok=True)
     if retval:
-        with open(os.path.join(tmpdir, "tokens", "bar.json"), "w") as fp:
+        with open(
+            os.path.join(tmpdir, "tokens", "%s.json" % project), "w"
+        ) as fp:
             fp.write("blarg")
 
     assert feedstock_token_exists(user, project, repo) is retval
 
-    assert git_mock.Repo.clone_from.called_with(
+    git_mock.Repo.clone_from.assert_called_once_with(
         "abc123", str(tmpdir), depth=1,
     )
 
 
 @pytest.mark.parametrize("project", ["bar", "bar-feedstock"])
 @pytest.mark.parametrize(
-    "repo", ["GITHUB_TOKEN", "${GITHUB_TOKEN}", "GH_TOKEN", "${GH_TOKEN}"]
+    "repo", ["$GITHUB_TOKEN", "${GITHUB_TOKEN}", "$GH_TOKEN", "${GH_TOKEN}"]
 )
 @mock.patch("conda_smithy.feedstock_tokens.tempfile")
 @mock.patch("conda_smithy.feedstock_tokens.git")
@@ -199,7 +201,7 @@ def test_feedstock_token_raises(
     git_mock.Repo.clone_from.side_effect = ValueError("blarg")
     user = "foo"
     os.makedirs(os.path.join(tmpdir, "tokens"), exist_ok=True)
-    with open(os.path.join(tmpdir, "tokens", "bar.json"), "w") as fp:
+    with open(os.path.join(tmpdir, "tokens", "%s.json" % project), "w") as fp:
         fp.write("blarg")
 
     with pytest.raises(RuntimeError) as e:
@@ -207,13 +209,13 @@ def test_feedstock_token_raises(
 
     assert "Testing for the feedstock token for" in str(e.value)
 
-    assert git_mock.Repo.clone_from.called_with(
+    git_mock.Repo.clone_from.assert_called_once_with(
         "abc123", str(tmpdir), depth=1,
     )
 
 
 @pytest.mark.parametrize(
-    "repo", ["GITHUB_TOKEN", "${GITHUB_TOKEN}", "GH_TOKEN", "${GH_TOKEN}"]
+    "repo", ["$GITHUB_TOKEN", "${GITHUB_TOKEN}", "$GH_TOKEN", "${GH_TOKEN}"]
 )
 @mock.patch("conda_smithy.feedstock_tokens.secrets")
 @mock.patch("conda_smithy.feedstock_tokens.os.urandom")
@@ -233,8 +235,8 @@ def test_register_feedstock_token_works(
     user = "foo"
     project = "bar"
     os.makedirs(os.path.join(tmpdir, "tokens"), exist_ok=True)
-    pth = os.path.expanduser("~/.conda-smithy/foo_bar_feedstock.token")
-    token_json_pth = os.path.join(tmpdir, "tokens", "bar.json")
+    pth = os.path.expanduser("~/.conda-smithy/foo_%s.token" % project)
+    token_json_pth = os.path.join(tmpdir, "tokens", "%s.json" % project)
 
     try:
         generate_and_write_feedstock_token(user, project)
@@ -245,17 +247,17 @@ def test_register_feedstock_token_works(
         if os.path.exists(pth):
             os.remove(pth)
 
-    assert git_mock.Repo.clone_from.called_with(
+    git_mock.Repo.clone_from.assert_called_once_with(
         "abc123", str(tmpdir), depth=1,
     )
 
     repo = git_mock.Repo.clone_from.return_value
-    assert repo.index.add.called_with(token_json_pth)
-    assert repo.index.commit.called_with(
+    repo.index.add.assert_called_once_with(token_json_pth)
+    repo.index.commit.assert_called_once_with(
         "added token for %s/%s" % (user, project)
     )
-    assert repo.remote.return_value.pull.called_with(rebase=True)
-    assert repo.remote.return_value.push.called_with()
+    repo.remote.return_value.pull.assert_called_once_with(rebase=True)
+    repo.remote.return_value.push.assert_called_once_with()
 
     salted_token = scrypt.hash("fgh", b"\x80SA", buflen=256)
     data = {
@@ -268,7 +270,7 @@ def test_register_feedstock_token_works(
 
 
 @pytest.mark.parametrize(
-    "repo", ["GITHUB_TOKEN", "${GITHUB_TOKEN}", "GH_TOKEN", "${GH_TOKEN}"]
+    "repo", ["$GITHUB_TOKEN", "${GITHUB_TOKEN}", "$GH_TOKEN", "${GH_TOKEN}"]
 )
 @mock.patch("conda_smithy.feedstock_tokens.secrets")
 @mock.patch("conda_smithy.feedstock_tokens.os.urandom")
@@ -288,7 +290,7 @@ def test_register_feedstock_token_notoken(
     user = "foo"
     project = "bar"
     os.makedirs(os.path.join(tmpdir, "tokens"), exist_ok=True)
-    pth = os.path.expanduser("~/.conda-smithy/foo_bar_feedstock.token")
+    pth = os.path.expanduser("~/.conda-smithy/foo_bar.token")
     token_json_pth = os.path.join(tmpdir, "tokens", "bar.json")
 
     try:
@@ -312,7 +314,7 @@ def test_register_feedstock_token_notoken(
 
 
 @pytest.mark.parametrize(
-    "repo", ["GITHUB_TOKEN", "${GITHUB_TOKEN}", "GH_TOKEN", "${GH_TOKEN}"]
+    "repo", ["$GITHUB_TOKEN", "${GITHUB_TOKEN}", "$GH_TOKEN", "${GH_TOKEN}"]
 )
 @mock.patch("conda_smithy.feedstock_tokens.secrets")
 @mock.patch("conda_smithy.feedstock_tokens.os.urandom")
@@ -332,7 +334,7 @@ def test_register_feedstock_token_exists_already(
     user = "foo"
     project = "bar"
     os.makedirs(os.path.join(tmpdir, "tokens"), exist_ok=True)
-    pth = os.path.expanduser("~/.conda-smithy/foo_bar_feedstock.token")
+    pth = os.path.expanduser("~/.conda-smithy/foo_bar.token")
     token_json_pth = os.path.join(tmpdir, "tokens", "bar.json")
     with open(token_json_pth, "w") as fp:
         fp.write("blarg")
@@ -347,7 +349,7 @@ def test_register_feedstock_token_exists_already(
         if os.path.exists(pth):
             os.remove(pth)
 
-    assert git_mock.Repo.clone_from.called_with(
+    git_mock.Repo.clone_from.assert_called_once_with(
         "abc123", str(tmpdir), depth=1,
     )
 
@@ -383,7 +385,7 @@ def test_register_feedstock_token_with_proviers(
     user = "foo"
     project = "bar"
 
-    pth = os.path.expanduser("~/.conda-smithy/foo_bar_feedstock.token")
+    pth = os.path.expanduser("~/.conda-smithy/foo_bar.token")
 
     try:
         generate_and_write_feedstock_token(user, project)
@@ -400,28 +402,28 @@ def test_register_feedstock_token_with_proviers(
         )
 
         if drone:
-            assert drone_mock.called_with(
+            drone_mock.assert_called_once_with(
                 user, project, feedstock_token, clobber
             )
         else:
             drone_mock.assert_not_called()
 
         if circle:
-            assert circle_mock.called_with(
+            circle_mock.assert_called_once_with(
                 user, project, feedstock_token, clobber
             )
         else:
             circle_mock.assert_not_called()
 
         if travis:
-            assert travis_mock.called_with(
+            travis_mock.assert_called_once_with(
                 user, project, feedstock_token, clobber
             )
         else:
             travis_mock.assert_not_called()
 
         if azure:
-            assert azure_mock.called_with(
+            azure_mock.assert_called_once_with(
                 user, project, feedstock_token, clobber
             )
         else:
@@ -484,7 +486,7 @@ def test_register_feedstock_token_with_proviers_error(
     user = "foo"
     project = "bar-feedstock"
 
-    pth = os.path.expanduser("~/.conda-smithy/foo_bar_feedstock.token")
+    pth = os.path.expanduser("~/.conda-smithy/foo_bar-feedstock.token")
 
     if provider == "drone":
         drone_mock.side_effect = ValueError("blah")