Repository: conda/conda-build

Issue:
skip-existing fails with basic auth channels
The `--skip-existing` option doesn't work for channels with basic authentication, i.e. those of the form `https://<user>:<pass>@<path>`. 

It fails to identify identical packages because the package index dictionary is indexed with canonical urls (without the `<user>:<pass>` bit), but the search url does contain `<user>:<pass>` section.

The culprit is this line: https://github.com/conda/conda-build/blob/520f858c2ce2d1cb447d4f1ded40db0845780378/conda_build/build.py#L2499

The `url` here contains the auth section, whereas the keys in the `index` dictionary are parsed from [channel urls wrapped in `Channel`](https://github.com/conda/conda/blob/21a8f3946d8cf9ce3803e2826be6e50400557f91/conda/models/channel.py#L493).

A relatively straightforward fix would be to do `str(Channel(url)) + '::' + metadata.dist())`, but `Channel` is not export by conda.




Discussion:
@kalefranz please comment on the suggested use of conda internals here.  Conda-build definitely needs to learn about the channel syntax at some point soon.
If we were to use new apis, I'd probably recommend something like

```python
from conda.api import SubdirData

def is_package_built(metadata, env, include_local=True):
    for d in metadata.config.bldpkgs_dirs:
        if not os.path.isdir(d):
            os.makedirs(d)
        update_index(d, verbose=metadata.config.verbose)
    subdir = getattr(metadata.config, '{}_subdir'.format(env))

    urls = [url_path(metadata.config.output_folder), 'local'] if include_local else []
    urls += get_rc_urls()
    if metadata.config.channel_urls:
        urls.extend(metadata.config.channel_urls)

    spec_str = " ".join((metadata.name(), metadata.version(), metadata.build_id()))
    return bool(next(SubdirData.query_all(spec_str, channels=urls, subdirs=(subdir, "noarch")), None))

```

Notice that I de-dented `update_index(d, verbose=metadata.config.verbose)` by one level.  My guess is that with the new `conda index` code, there won't be any noticeable performance impact by not handling `index` directly.  I think it's at least worth a try in a PR to see if it dramatically impacts CI times.  Honestly, it might even be faster.

If you don't want to go this route, I'd suggest as an alternative:

```python
from .conda_interface import MatchSpec

def is_package_built(metadata, env, include_local=True):
    for d in metadata.config.bldpkgs_dirs:
        if not os.path.isdir(d):
            os.makedirs(d)
            update_index(d, verbose=metadata.config.verbose)
    subdir = getattr(metadata.config, '{}_subdir'.format(env))
    index, index_ts = get_build_index(subdir=subdir,
                                      bldpkgs_dir=metadata.config.bldpkgs_dir,
                                      output_folder=metadata.config.output_folder,
                                      channel_urls=metadata.config.channel_urls,
                                      debug=metadata.config.debug,
                                      verbose=metadata.config.verbose,
                                      locking=metadata.config.locking,
                                      timeout=metadata.config.timeout,
                                      clear_cache=True)

    spec = MatchSpec(name=metadata.name(), version=metadata.version(), build=metadata.build_id())
    return any(spec.match(prec) for prec in index.values())

```

The API option requires a lower-bound of conda 4.5.x.  I don't think we can do that yet, unfortunately.  We still need to support 4.3.x until enterprise people move off of it.  I guess we could do a version check and paste in both of your blocks here.

Resolving patch:
diff --git a/conda_build/build.py b/conda_build/build.py
--- a/conda_build/build.py
+++ b/conda_build/build.py
@@ -30,7 +30,7 @@
 import yaml
 
 # used to get version
-from .conda_interface import env_path_backup_var_exists, conda_46
+from .conda_interface import env_path_backup_var_exists, conda_45, conda_46
 from .conda_interface import PY3
 from .conda_interface import prefix_placeholder
 from .conda_interface import TemporaryDirectory
@@ -43,7 +43,6 @@
 from .conda_interface import url_path
 from .conda_interface import root_dir
 from .conda_interface import conda_private
-from .conda_interface import dist_str_in_index
 from .conda_interface import MatchSpec
 from .conda_interface import reset_context
 from .conda_interface import context
@@ -2477,24 +2476,27 @@ def is_package_built(metadata, env, include_local=True):
     for d in metadata.config.bldpkgs_dirs:
         if not os.path.isdir(d):
             os.makedirs(d)
-            update_index(d, verbose=metadata.config.verbose)
+        update_index(d, verbose=metadata.config.verbose)
     subdir = getattr(metadata.config, '{}_subdir'.format(env))
-    index, index_ts = get_build_index(subdir=subdir,
-                                      bldpkgs_dir=metadata.config.bldpkgs_dir,
-                                      output_folder=metadata.config.output_folder,
-                                      channel_urls=metadata.config.channel_urls,
-                                      debug=metadata.config.debug,
-                                      verbose=metadata.config.verbose,
-                                      locking=metadata.config.locking,
-                                      timeout=metadata.config.timeout,
-                                      clear_cache=True)
 
     urls = [url_path(metadata.config.output_folder), 'local'] if include_local else []
     urls += get_rc_urls()
     if metadata.config.channel_urls:
         urls.extend(metadata.config.channel_urls)
 
-    # will be empty if none found, and evalute to False
-    found_urls = [url for url in urls
-            if dist_str_in_index(index, url + '::' + metadata.dist())]
-    return found_urls[0] if found_urls else None
+    spec = MatchSpec(name=metadata.name(), version=metadata.version(), build=metadata.build_id())
+
+    if conda_45:
+        from conda.api import SubdirData
+        return bool(SubdirData.query_all(spec, channels=urls, subdirs=(subdir, "noarch")))
+    else:
+        index, index_ts = get_build_index(subdir=subdir,
+                                          bldpkgs_dir=metadata.config.bldpkgs_dir,
+                                          output_folder=metadata.config.output_folder,
+                                          channel_urls=urls,
+                                          debug=metadata.config.debug,
+                                          verbose=metadata.config.verbose,
+                                          locking=metadata.config.locking,
+                                          timeout=metadata.config.timeout,
+                                          clear_cache=True)
+        return any(spec.match(prec) for prec in index.values())
diff --git a/conda_build/conda_interface.py b/conda_build/conda_interface.py
--- a/conda_build/conda_interface.py
+++ b/conda_build/conda_interface.py
@@ -29,6 +29,7 @@ def try_exports(module, attr):
 
 conda_43 = parse_version(CONDA_VERSION) >= parse_version("4.3.0a0")
 conda_44 = parse_version(CONDA_VERSION) >= parse_version("4.4.0a0")
+conda_45 = parse_version(CONDA_VERSION) >= parse_version("4.5.0a0")
 conda_46 = parse_version(CONDA_VERSION) >= parse_version("4.6.0a0")
 
 if conda_44:
@@ -61,7 +62,6 @@ def try_exports(module, attr):
 from conda.exports import (PY3,  StringIO, input, iteritems, lchmod, string_types,  # NOQA
                           text_type, TemporaryDirectory)  # NOQA
 from conda.exports import VersionOrder  # NOQA
-from conda.exports import dist_str_in_index  # NOQA
 
 
 TmpDownload = TmpDownload
@@ -77,7 +77,6 @@ def try_exports(module, attr):
 PY3, input, iteritems, lchmod, string_types = PY3, input, iteritems, lchmod, string_types
 text_type, TemporaryDirectory = text_type, TemporaryDirectory
 ArgumentParser, CondaSession, VersionOrder = ArgumentParser, CondaSession, VersionOrder
-dist_str_in_index = dist_str_in_index
 
 
 from conda.core.package_cache import ProgressiveFetchExtract  # NOQA


Test patch: