Repository: conda-forge/conda-smithy

Issue:
Azure smithy regenerate error for private forge
Following https://github.com/conda-forge/conda-forge.github.io/issues/1069

I am trying to setup a private conda-forge like forge using Azure only. I have generated an azure token with the full scopes but when running `conda smithy regenerate` I have the following error: 

```
Traceback (most recent call last):
  File "/home/hadim/local/conda/bin/conda-smithy", line 10, in <module>
    sys.exit(main())
  File "/home/hadim/local/conda/lib/python3.7/site-packages/conda_smithy/cli.py", line 584, in main
    args.subcommand_func(args)
  File "/home/hadim/local/conda/lib/python3.7/site-packages/conda_smithy/cli.py", line 391, in __call__
    self._call(args, tmpdir)
  File "/home/hadim/local/conda/lib/python3.7/site-packages/conda_smithy/cli.py", line 402, in _call
    temporary_directory=temporary_directory,
  File "/home/hadim/local/conda/lib/python3.7/site-packages/conda_smithy/configure_feedstock.py", line 1878, in main
    render_README(env, config, forge_dir, render_info)
  File "/home/hadim/local/conda/lib/python3.7/site-packages/conda_smithy/configure_feedstock.py", line 1329, in render_README
    build_def = resp.json()["value"][0]
  File "/home/hadim/local/conda/lib/python3.7/site-packages/requests/models.py", line 898, in json
    return complexjson.loads(self.text, **kwargs)
  File "/home/hadim/local/conda/lib/python3.7/json/__init__.py", line 348, in loads
    return _default_decoder.decode(s)
  File "/home/hadim/local/conda/lib/python3.7/json/decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/home/hadim/local/conda/lib/python3.7/json/decoder.py", line 355, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 3 column 1 (char 4)
```


Discussion:
```python
    if forge_config["azure"].get("build_id") is None:
        # Try to retrieve the build_id from the interwebs
        try:
            import requests

            resp = requests.get(
                "https://dev.azure.com/{org}/{project_name}/_apis/build/definitions?name={repo}".format(
                    org=forge_config["azure"]["user_or_org"],
                    project_name=forge_config["azure"]["project_name"],
                    repo=forge_config["github"]["repo_name"],
                )
            )
            resp.raise_for_status()
            print(resp.text)
            build_def = resp.json()["value"][0]
            forge_config["azure"]["build_id"] = build_def["id"]
        except (IndexError, IOError):
            pass
```

I tried to print the response and it's an HTML text asking me to signin.
The response code is 203.
The token I use within Azure to grant GH access has the following permissions: admin:org, admin:org_hook, admin:repo_hook, repo

Resolving patch:
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
@@ -10,7 +10,7 @@
 import copy
 import hashlib
 import requests
-import time
+import json
 
 import conda_build.api
 import conda_build.utils
@@ -1245,6 +1245,38 @@ def render_drone(jinja_env, forge_config, forge_dir, return_metadata=False):
     )
 
 
+def azure_build_id_from_token(forge_config):
+    """Retrieve Azure `build_id` from a `forge_config` using an Azure token.
+    This function allows the `build_id` to be retrieved when the Azure org is private.
+    """
+    # If it fails then we switch to a request using an Azure token.
+    from conda_smithy import azure_ci_utils
+
+    config = azure_ci_utils.AzureConfig(
+        org_or_user=forge_config["azure"]["user_or_org"],
+        project_name=forge_config["azure"]["project_name"],
+    )
+    repo = forge_config["github"]["repo_name"]
+    build_info = azure_ci_utils.get_build_id(repo, config)
+    forge_config["azure"]["build_id"] = build_info["build_id"]
+
+
+def azure_build_id_from_public(forge_config):
+    """Retrieve Azure `build_id` from a `forge_config`. This function only works
+    when the Azure org is public.
+    """
+    resp = requests.get(
+        "https://dev.azure.com/{org}/{project_name}/_apis/build/definitions?name={repo}".format(
+            org=forge_config["azure"]["user_or_org"],
+            project_name=forge_config["azure"]["project_name"],
+            repo=forge_config["github"]["repo_name"],
+        )
+    )
+    resp.raise_for_status()
+    build_def = resp.json()["value"][0]
+    forge_config["azure"]["build_id"] = build_def["id"]
+
+
 def render_README(jinja_env, forge_config, forge_dir, render_info=None):
     if "README.md" in forge_config["skip_render"]:
         logger.info("README.md rendering is skipped")
@@ -1314,20 +1346,20 @@ def render_README(jinja_env, forge_config, forge_dir, render_info=None):
     )
 
     if forge_config["azure"].get("build_id") is None:
-        # Try to retrieve the build_id from the interwebs
+
+        # Try to retrieve the build_id from the interwebs.
+        # Works if the Azure CI is public
         try:
-            resp = requests.get(
-                "https://dev.azure.com/{org}/{project_name}/_apis/build/definitions?name={repo}".format(
-                    org=forge_config["azure"]["user_or_org"],
-                    project_name=forge_config["azure"]["project_name"],
-                    repo=forge_config["github"]["repo_name"],
+            azure_build_id_from_public(forge_config)
+        except (IndexError, IOError) as err:
+            # We don't want to command to fail if requesting the build_id fails.
+            logger.warning(
+                "Azure build_id can't be retrieved using the Azure token. Exception: {}".format(
+                    err
                 )
             )
-            resp.raise_for_status()
-            build_def = resp.json()["value"][0]
-            forge_config["azure"]["build_id"] = build_def["id"]
-        except (IndexError, IOError):
-            pass
+        except json.decoder.JSONDecodeError:
+            azure_build_id_from_token(forge_config)
 
     logger.debug("README")
     logger.debug(yaml.dump(forge_config))


Test patch: