import os
import subprocess
import sys
import time
import argparse
import io

from textwrap import dedent

import conda
from distutils.version import LooseVersion
from conda_build.metadata import MetaData

from . import configure_feedstock
from . import feedstock_io
from . import lint_recipe
from . import azure_ci_utils
from . import __version__


if sys.version_info[0] == 2:
    raise Exception("Conda-smithy does not support python 2!")


def generate_feedstock_content(target_directory, source_recipe_dir):
    target_directory = os.path.abspath(target_directory)
    recipe_dir = "recipe"
    target_recipe_dir = os.path.join(target_directory, recipe_dir)

    if not os.path.exists(target_recipe_dir):
        os.makedirs(target_recipe_dir)
    # If there is a source recipe, copy it now to the right dir
    if source_recipe_dir:
        try:
            configure_feedstock.copytree(source_recipe_dir, target_recipe_dir)
        except Exception as e:
            import sys

            raise type(e)(
                str(e) + " while copying file %s" % source_recipe_dir
            ).with_traceback(sys.exc_info()[2])

    forge_yml = os.path.join(target_directory, "conda-forge.yml")
    if not os.path.exists(forge_yml):
        with feedstock_io.write_file(forge_yml) as fh:
            fh.write(u"[]")


class Subcommand(object):
    #: The name of the subcommand
    subcommand = None
    aliases = []

    def __init__(self, parser, help=None):
        subcommand_parser = parser.add_parser(
            self.subcommand, help=help, aliases=self.aliases
        )
        subcommand_parser.set_defaults(subcommand_func=self)
        self.subcommand_parser = subcommand_parser

    def __call__(self, args):
        pass


class Init(Subcommand):
    subcommand = "init"

    def __init__(self, parser):
        # conda-smithy init /path/to/udunits-recipe ./

        super(Init, self).__init__(
            parser,
            "Create a feedstock git repository, which can contain "
            "one conda recipes.",
        )
        scp = self.subcommand_parser
        scp.add_argument(
            "recipe_directory", help="The path to the source recipe directory."
        )
        scp.add_argument(
            "--feedstock-directory",
            default="./{package.name}-feedstock",
            help="Target directory, where the new feedstock git repository should be "
            "created. (Default: './<packagename>-feedstock')",
        )

    def __call__(self, args):
        # check some error conditions
        if args.recipe_directory and not os.path.isdir(args.recipe_directory):
            raise IOError(
                "The source recipe directory should be the directory of the "
                "conda-recipe you want to build a feedstock for. Got {}".format(
                    args.recipe_directory
                )
            )

        # Get some information about the source recipe.
        if args.recipe_directory:
            meta = MetaData(args.recipe_directory)
        else:
            meta = None

        feedstock_directory = args.feedstock_directory.format(
            package=argparse.Namespace(name=meta.name())
        )
        msg = "Initial feedstock commit with conda-smithy {}.".format(
            __version__
        )

        os.makedirs(feedstock_directory)
        subprocess.check_call(["git", "init"], cwd=feedstock_directory)
        generate_feedstock_content(feedstock_directory, args.recipe_directory)
        subprocess.check_call(
            ["git", "commit", "-m", msg], cwd=feedstock_directory
        )

        print(
            "\nRepository created, please edit conda-forge.yml to configure the upload channels\n"
            "and afterwards call 'conda smithy register-github'"
        )


class RegisterGithub(Subcommand):
    subcommand = "register-github"

    def __init__(self, parser):
        #  conda-smithy register-github ./ --organization=conda-forge
        super(RegisterGithub, self).__init__(
            parser, "Register a repo for a feedstock at github."
        )
        scp = self.subcommand_parser
        scp.add_argument(
            "--add-teams",
            action="store_true",
            default=False,
            help="Create teams and register maintainers to them.",
        )
        scp.add_argument(
            "feedstock_directory",
            help="The directory of the feedstock git repository.",
        )
        group = scp.add_mutually_exclusive_group()
        group.add_argument(
            "--user", help="github username under which to register this repo"
        )
        group.add_argument(
            "--organization",
            default="conda-forge",
            help="github organisation under which to register this repo",
        )
        scp.add_argument(
            "--remote-name",
            default="upstream",
            help="The name of the remote to add to the local repo (default: upstream). "
            "An empty string will disable adding of a remote.",
        )
        scp.add_argument(
            "--extra-admin-users",
            nargs="*",
            help="Extra users to be added as admins",
        )

    def __call__(self, args):
        from . import github

        github.create_github_repo(args)
        print(
            "\nRepository registered at github, now call 'conda smithy register-ci'"
        )


class RegisterCI(Subcommand):
    subcommand = "register-ci"

    def __init__(self, parser):
        # conda-smithy register-ci ./
        super(RegisterCI, self).__init__(
            parser,
            "Register a feedstock at the CI " "services which do the builds.",
        )
        scp = self.subcommand_parser
        scp.add_argument(
            "--feedstock_directory",
            default=os.getcwd(),
            help="The directory of the feedstock git repository.",
        )
        group = scp.add_mutually_exclusive_group()
        group.add_argument(
            "--user", help="github username under which to register this repo"
        )
        group.add_argument(
            "--organization",
            default="conda-forge",
            help="github organisation under which to register this repo",
        )
        for ci in [
            "Azure",
            "Travis",
            "Circle",
            "Appveyor",
            "Drone",
            "Webservice",
        ]:
            scp.add_argument(
                "--without-{}".format(ci.lower()),
                dest=ci.lower(),
                action="store_false",
                help="If set, {} will be not registered".format(ci),
            )
            default = {ci.lower(): True}
            scp.set_defaults(**default)

    def __call__(self, args):
        from conda_smithy import ci_register

        owner = args.user or args.organization
        repo = os.path.basename(os.path.abspath(args.feedstock_directory))

        print("CI Summary for {}/{} (can take ~30s):".format(owner, repo))
        if args.travis:
            # Assume that the user has enabled travis-ci.com service
            # user-wide or org-wide for all repos
            # ci_register.add_project_to_travis(owner, repo)
            time.sleep(1)
            ci_register.travis_configure(owner, repo)
            ci_register.add_token_to_travis(owner, repo)
            # Assume that the user has enabled travis-ci.com service
            # user-wide or org-wide for all repos
            # ci_register.travis_cleanup(owner, repo)
        else:
            print("Travis registration disabled.")
        if args.circle:
            ci_register.add_project_to_circle(owner, repo)
            ci_register.add_token_to_circle(owner, repo)
        else:
            print("Circle registration disabled.")
        if args.azure:
            if azure_ci_utils.default_config.token is None:
                print(
                    "No azure token. Create a token at https://dev.azure.com/"
                    "conda-forge/_usersSettings/tokens and\n"
                    "put it in ~/.conda-smithy/azure.token"
                )
            ci_register.add_project_to_azure(owner, repo)
        else:
            print("Azure registration disabled.")
        if args.appveyor:
            ci_register.add_project_to_appveyor(owner, repo)
            ci_register.appveyor_encrypt_binstar_token(
                args.feedstock_directory, owner, repo
            )
            ci_register.appveyor_configure(owner, repo)
        else:
            print("Appveyor registration disabled.")

        if args.drone:
            ci_register.add_project_to_drone(owner, repo)
            ci_register.add_token_to_drone(owner, repo)
        else:
            print("Drone registration disabled.")

        if args.webservice:
            ci_register.add_conda_forge_webservice_hooks(owner, repo)
        else:
            print("Heroku webservice registration disabled.")
        print(
            "\nCI services have been enabled. You may wish to regenerate the feedstock.\n"
            "Any changes will need commiting to the repo."
        )


class AddAzureBuildId(Subcommand):
    subcommand = "azure-buildid"

    def __init__(self, parser):
        # conda-smithy azure-buildid ./
        super(AddAzureBuildId, self).__init__(
            parser,
            dedent(
                "Update the azure configuration stored in the config file."
            ),
        )
        scp = self.subcommand_parser
        scp.add_argument(
            "--feedstock_directory",
            default=os.getcwd(),
            help="The directory of the feedstock git repository.",
        )
        group = scp.add_mutually_exclusive_group()
        group.add_argument(
            "--user",
            help="azure username for which this repo is enabled already",
        )
        group.add_argument(
            "--organization",
            default=azure_ci_utils.AzureConfig._default_org,
            help="azure organisation for which this repo is enabled already",
        )
        scp.add_argument(
            "--project_name",
            default=azure_ci_utils.AzureConfig._default_project_name,
            help="project name that feedstocks are registered under",
        )

    def __call__(self, args):
        owner = args.user or args.organization
        repo = os.path.basename(os.path.abspath(args.feedstock_directory))

        config = azure_ci_utils.AzureConfig(
            org_or_user=owner, project_name=args.project_name
        )

        build_info = azure_ci_utils.get_build_id(repo, config)

        from .utils import update_conda_forge_config

        with update_conda_forge_config(args.feedstock_directory) as config:
            config.setdefault("azure", {})
            config["azure"]["build_id"] = build_info["build_id"]
            config["azure"]["user_or_org"] = build_info["user_or_org"]
            config["azure"]["project_name"] = build_info["project_name"]
            config["azure"]["project_id"] = build_info["project_id"]


class Regenerate(Subcommand):
    subcommand = "regenerate"
    aliases = ["rerender"]

    def __init__(self, parser):
        super(Regenerate, self).__init__(
            parser,
            "Regenerate / update the CI support files of the " "feedstock.",
        )
        scp = self.subcommand_parser
        scp.add_argument(
            "--feedstock_directory",
            default=os.getcwd(),
            help="The directory of the feedstock git repository.",
        )
        scp.add_argument(
            "-c",
            "--commit",
            nargs="?",
            choices=["edit", "auto"],
            const="edit",
            help="Whether to setup a commit or not.",
        )
        scp.add_argument(
            "--no-check-uptodate",
            action="store_true",
            help="Don't check that conda-smithy and conda-forge-pinning are uptodate",
        )
        scp.add_argument(
            "-e",
            "--exclusive-config-file",
            default=None,
            help="Exclusive conda-build config file to replace conda-forge-pinning. "
            + "For advanced usage only",
        )
        scp.add_argument(
            "--check",
            action="store_true",
            default=False,
            help="Check if regenerate can be performed",
        )

    def __call__(self, args):
        configure_feedstock.main(
            args.feedstock_directory,
            no_check_uptodate=args.no_check_uptodate,
            commit=args.commit,
            exclusive_config_file=args.exclusive_config_file,
            check=args.check,
        )


class RecipeLint(Subcommand):
    subcommand = "recipe-lint"

    def __init__(self, parser):
        super(RecipeLint, self).__init__(parser, "Lint a single conda recipe.")
        scp = self.subcommand_parser
        scp.add_argument("--conda-forge", action="store_true")
        scp.add_argument("recipe_directory", default=[os.getcwd()], nargs="*")

    def __call__(self, args):
        all_good = True
        for recipe in args.recipe_directory:
            lints, hints = lint_recipe.main(
                os.path.join(recipe),
                conda_forge=args.conda_forge,
                return_hints=True,
            )
            if lints:
                all_good = False
                print(
                    "{} has some lint:\n  {}".format(
                        recipe, "\n  ".join(lints)
                    )
                )
                if hints:
                    print(
                        "{} also has some suggestions:\n  {}".format(
                            recipe, "\n  ".join(hints)
                        )
                    )
            elif hints:
                print(
                    "{} has some suggestions:\n  {}".format(
                        recipe, "\n  ".join(hints)
                    )
                )
            else:
                print("{} is in fine form".format(recipe))
        # Exit code 1 for some lint, 0 for no lint.
        sys.exit(int(not all_good))


POST_SKELETON_MESSAGE = """
A CI skeleton has been generated! Please use the following steps
to complete the CI setup process:

1. Fill out {args.recipe_directory}/meta.yaml with your install and test code
2. Commit all changes to the repo.

        $ git add . && git commit -m "ran conda smithy skeleton"

3. Remember to register your repo with the CI providers.
4. Rerender this repo to generate the CI configurations files.
   This can be done with:

        $ conda smithy rerender -c auto

At any time in the future, you will be able to automatically update your CI configuration by
re-running the rerender command above. Happy testing!
"""


class CISkeleton(Subcommand):
    subcommand = "ci-skeleton"

    def __init__(self, parser):
        super(CISkeleton, self).__init__(
            parser, "Generate skeleton for using CI outside of a feedstock"
        )
        scp = self.subcommand_parser
        scp.add_argument(
            "--feedstock-directory",
            default=os.getcwd(),
            help="The directory of the feedstock git repository.",
            dest="feedstock_directory",
        )
        scp.add_argument(
            "-r",
            "--recipe-directory",
            default="recipe",
            dest="recipe_directory",
        )
        scp.add_argument("package_name", nargs="?")

    def __call__(self, args):
        from conda_smithy.ci_skeleton import generate

        # complete configuration
        if getattr(args, "package_name") is None:
            args.package_name = "package"

        generate(
            package_name=args.package_name,
            feedstock_directory=args.feedstock_directory,
            recipe_directory=args.recipe_directory,
        )
        print(POST_SKELETON_MESSAGE.format(args=args).strip())


class UpdateCB3(Subcommand):
    subcommand = "update-cb3"

    def __init__(self, parser):
        # conda-smithy update-cb3 ./
        super(UpdateCB3, self).__init__(
            parser, "Update a feedstock for conda-build=3"
        )
        scp = self.subcommand_parser
        scp.add_argument(
            "--recipe_directory",
            default=os.path.join(os.getcwd(), "recipe"),
            help="The path to the source recipe directory.",
        )
        scp.add_argument(
            "--output",
            default=None,
            help="Filename for the output. No output edits the recipe inplace",
        )
        scp.add_argument(
            "--cbc",
            default=None,
            help="Path to conda_build_config.yaml. No path will use conda-forge-pinning",
        )

    def __call__(self, args):
        from conda_smithy.update_cb3 import update_cb3
        from conda_smithy.configure_feedstock import get_cfp_file_path

        recipe_file = os.path.join(args.recipe_directory, "meta.yaml")
        output_file = args.output
        if output_file is None:
            output_file = recipe_file
        if args.cbc is None:
            cbc, _ = get_cfp_file_path()
        else:
            cbc = os.path.join(os.getcwd(), args.cbc)
        output_content, messages = update_cb3(recipe_file, cbc)
        with io.open(output_file, "w") as fh:
            fh.write(output_content)
        print("List of changes done to the recipe:")
        print(messages)


def main():

    parser = argparse.ArgumentParser(
        "a tool to help create, administer and manage feedstocks."
    )
    subparser = parser.add_subparsers()
    # TODO: Consider allowing plugins/extensions using entry_points.
    # https://reinout.vanrees.org/weblog/2010/01/06/zest-releaser-entry-points.html
    for subcommand in Subcommand.__subclasses__():
        subcommand(subparser)
    # And the alias for rerender
    parser.add_argument(
        "--version",
        action="version",
        version=__version__,
        help="Show conda-smithy's version, and exit.",
    )

    if not sys.argv[1:]:
        args = parser.parse_args(["--help"])
    else:
        args = parser.parse_args()

    # Check conda version for compatibility
    CONDA_VERSION_MAX = "5.0"
    if LooseVersion(conda.__version__) >= LooseVersion(CONDA_VERSION_MAX):
        print(
            "You appear to be using conda {}, but conda-smithy {}\n"
            "is currently only compatible with conda versions < {}.\n".format(
                conda.__version__, __version__, CONDA_VERSION_MAX
            )
        )
        sys.exit(2)

    args.subcommand_func(args)


class GenerateFeedstockToken(Subcommand):
    subcommand = "generate-feedstock-token"

    def __init__(self, parser):
        super(GenerateFeedstockToken, self).__init__(
            parser,
            "Generate a feedstock token at ~/.conda-smithy/{user or org}_{project}_feedstock.token",
        )
        scp = self.subcommand_parser
        scp.add_argument(
            "--feedstock_directory",
            default=os.getcwd(),
            help="The directory of the feedstock git repository.",
        )
        group = scp.add_mutually_exclusive_group()
        group.add_argument(
            "--user", help="github username under which to register this repo"
        )
        group.add_argument(
            "--organization",
            default="conda-forge",
            help="github organisation under which to register this repo",
        )

    def __call__(self, args):
        from conda_smithy.feedstock_tokens import (
            generate_and_write_feedstock_token,
        )

        owner = args.user or args.organization
        repo = os.path.basename(os.path.abspath(args.feedstock_directory))

        generate_and_write_feedstock_token(owner, repo)
        print(
            "Your feedstock token has been generated at ~/.conda-smithy/%s_%s_feedstock.token\n"
            "This token is stored in plaintext so be careful!" % (owner, repo)
        )


class RegisterFeedstockToken(Subcommand):
    subcommand = "register-feedstock-token"

    def __init__(self, parser):
        # conda-smithy register-ci ./
        super(RegisterFeedstockToken, self).__init__(
            parser,
            "Register the feedstock token w/ the CI services for builds and "
            "with the token registry.",
        )
        scp = self.subcommand_parser
        scp.add_argument(
            "--feedstock_directory",
            default=os.getcwd(),
            help="The directory of the feedstock git repository.",
        )
        scp.add_argument(
            "--token_repo",
            default=None,
            help=(
                "The GitHub repo that stores feedstock tokens. The default is "
                "{user or org}/feedstock-tokens on GitHub."
            ),
        )
        group = scp.add_mutually_exclusive_group()
        group.add_argument(
            "--user", help="github username under which to register this repo"
        )
        group.add_argument(
            "--organization",
            default="conda-forge",
            help="github organisation under which to register this repo",
        )
        for ci in [
            "Azure",
            "Travis",
            "Circle",
            "Drone",
        ]:
            scp.add_argument(
                "--without-{}".format(ci.lower()),
                dest=ci.lower(),
                action="store_false",
                help="If set, {} will be not registered".format(ci),
            )
            default = {ci.lower(): True}
            scp.set_defaults(**default)

    def __call__(self, args):
        from conda_smithy.feedstock_tokens import (
            register_feedstock_token_with_proviers,
            register_feedstock_token,
            feedstock_token_exists,
        )

        owner = args.user or args.organization
        repo = os.path.basename(os.path.abspath(args.feedstock_directory))

        if args.token_repo is None:
            token_repo = (
                "https://${GITHUB_TOKEN}@github.com/%s/feedstock-tokens"
                % owner
            )
        else:
            token_repo = args.token_repo

        if feedstock_token_exists(owner, repo, token_repo):
            raise RuntimeError(
                "Token for repo %s/%s already exists!" % (owner, repo)
            )

        print("Registering the feedstock tokens. Can take up to ~30 seconds.")

        # do all providers first
        register_feedstock_token_with_proviers(
            owner,
            repo,
            drone=args.drone,
            circle=args.circle,
            travis=args.travis,
            azure=args.azure,
        )

        # then if that works do the github repo
        register_feedstock_token(owner, repo, token_repo)

        print("Successfully registered the feedstock token!")


if __name__ == "__main__":
    main()

conda_smithy/cli.py
import os
import subprocess
import sys
import time
import argparse
import io

from textwrap import dedent

import conda
from distutils.version import LooseVersion
from conda_build.metadata import MetaData

from . import configure_feedstock
from . import feedstock_io
from . import lint_recipe
from . import azure_ci_utils
from . import __version__


if sys.version_info[0] == 2:
    raise Exception("Conda-smithy does not support python 2!")


def generate_feedstock_content(target_directory, source_recipe_dir):
    target_directory = os.path.abspath(target_directory)
    recipe_dir = "recipe"
    target_recipe_dir = os.path.join(target_directory, recipe_dir)

    if not os.path.exists(target_recipe_dir):
        os.makedirs(target_recipe_dir)
    # If there is a source recipe, copy it now to the right dir
    if source_recipe_dir:
        try:
            configure_feedstock.copytree(source_recipe_dir, target_recipe_dir)
        except Exception as e:
            import sys

            raise type(e)(
                str(e) + " while copying file %s" % source_recipe_dir
            ).with_traceback(sys.exc_info()[2])

    forge_yml = os.path.join(target_directory, "conda-forge.yml")
    if not os.path.exists(forge_yml):
        with feedstock_io.write_file(forge_yml) as fh:
            fh.write(u"{}")


class Subcommand(object):
    #: The name of the subcommand
    subcommand = None
    aliases = []

    def __init__(self, parser, help=None):
        subcommand_parser = parser.add_parser(
            self.subcommand, help=help, aliases=self.aliases
        )
        subcommand_parser.set_defaults(subcommand_func=self)
        self.subcommand_parser = subcommand_parser

    def __call__(self, args):
        pass


class Init(Subcommand):
    subcommand = "init"

    def __init__(self, parser):
        # conda-smithy init /path/to/udunits-recipe ./

        super(Init, self).__init__(
            parser,
            "Create a feedstock git repository, which can contain "
            "one conda recipes.",
        )
        scp = self.subcommand_parser
        scp.add_argument(
            "recipe_directory", help="The path to the source recipe directory."
        )
        scp.add_argument(
            "--feedstock-directory",
            default="./{package.name}-feedstock",
            help="Target directory, where the new feedstock git repository should be "
            "created. (Default: './<packagename>-feedstock')",
        )

    def __call__(self, args):
        # check some error conditions
        if args.recipe_directory and not os.path.isdir(args.recipe_directory):
            raise IOError(
                "The source recipe directory should be the directory of the "
                "conda-recipe you want to build a feedstock for. Got {}".format(
                    args.recipe_directory
                )
            )

        # Get some information about the source recipe.
        if args.recipe_directory:
            meta = MetaData(args.recipe_directory)
        else:
            meta = None

        feedstock_directory = args.feedstock_directory.format(
            package=argparse.Namespace(name=meta.name())
        )
        msg = "Initial feedstock commit with conda-smithy {}.".format(
            __version__
        )

        os.makedirs(feedstock_directory)
        subprocess.check_call(["git", "init"], cwd=feedstock_directory)
        generate_feedstock_content(feedstock_directory, args.recipe_directory)
        subprocess.check_call(
            ["git", "commit", "-m", msg], cwd=feedstock_directory
        )

        print(
            "\nRepository created, please edit conda-forge.yml to configure the upload channels\n"
            "and afterwards call 'conda smithy register-github'"
        )


class RegisterGithub(Subcommand):
    subcommand = "register-github"

    def __init__(self, parser):
        #  conda-smithy register-github ./ --organization=conda-forge
        super(RegisterGithub, self).__init__(
            parser, "Register a repo for a feedstock at github."
        )
        scp = self.subcommand_parser
        scp.add_argument(
            "--add-teams",
            action="store_true",
            default=False,
            help="Create teams and register maintainers to them.",
        )
        scp.add_argument(
            "feedstock_directory",
            help="The directory of the feedstock git repository.",
        )
        group = scp.add_mutually_exclusive_group()
        group.add_argument(
            "--user", help="github username under which to register this repo"
        )
        group.add_argument(
            "--organization",
            default="conda-forge",
            help="github organisation under which to register this repo",
        )
        scp.add_argument(
            "--remote-name",
            default="upstream",
            help="The name of the remote to add to the local repo (default: upstream). "
            "An empty string will disable adding of a remote.",
        )
        scp.add_argument(
            "--extra-admin-users",
            nargs="*",
            help="Extra users to be added as admins",
        )

    def __call__(self, args):
        from . import github

        github.create_github_repo(args)
        print(
            "\nRepository registered at github, now call 'conda smithy register-ci'"
        )


class RegisterCI(Subcommand):
    subcommand = "register-ci"

    def __init__(self, parser):
        # conda-smithy register-ci ./
        super(RegisterCI, self).__init__(
            parser,
            "Register a feedstock at the CI " "services which do the builds.",
        )
        scp = self.subcommand_parser
        scp.add_argument(
            "--feedstock_directory",
            default=os.getcwd(),
            help="The directory of the feedstock git repository.",
        )
        group = scp.add_mutually_exclusive_group()
        group.add_argument(
            "--user", help="github username under which to register this repo"
        )
        group.add_argument(
            "--organization",
            default="conda-forge",
            help="github organisation under which to register this repo",
        )
        for ci in [
            "Azure",
            "Travis",
            "Circle",
            "Appveyor",
            "Drone",
            "Webservice",
        ]:
            scp.add_argument(
                "--without-{}".format(ci.lower()),
                dest=ci.lower(),
                action="store_false",
                help="If set, {} will be not registered".format(ci),
            )
            default = {ci.lower(): True}
            scp.set_defaults(**default)

    def __call__(self, args):
        from conda_smithy import ci_register

        owner = args.user or args.organization
        repo = os.path.basename(os.path.abspath(args.feedstock_directory))

        print("CI Summary for {}/{} (can take ~30s):".format(owner, repo))
        if args.travis:
            # Assume that the user has enabled travis-ci.com service
            # user-wide or org-wide for all repos
            # ci_register.add_project_to_travis(owner, repo)
            time.sleep(1)
            ci_register.travis_configure(owner, repo)
            ci_register.add_token_to_travis(owner, repo)
            # Assume that the user has enabled travis-ci.com service
            # user-wide or org-wide for all repos
            # ci_register.travis_cleanup(owner, repo)
        else:
            print("Travis registration disabled.")
        if args.circle:
            ci_register.add_project_to_circle(owner, repo)
            ci_register.add_token_to_circle(owner, repo)
        else:
            print("Circle registration disabled.")
        if args.azure:
            if azure_ci_utils.default_config.token is None:
                print(
                    "No azure token. Create a token at https://dev.azure.com/"
                    "conda-forge/_usersSettings/tokens and\n"
                    "put it in ~/.conda-smithy/azure.token"
                )
            ci_register.add_project_to_azure(owner, repo)
        else:
            print("Azure registration disabled.")
        if args.appveyor:
            ci_register.add_project_to_appveyor(owner, repo)
            ci_register.appveyor_encrypt_binstar_token(
                args.feedstock_directory, owner, repo
            )
            ci_register.appveyor_configure(owner, repo)
        else:
            print("Appveyor registration disabled.")

        if args.drone:
            ci_register.add_project_to_drone(owner, repo)
            ci_register.add_token_to_drone(owner, repo)
        else:
            print("Drone registration disabled.")

        if args.webservice:
            ci_register.add_conda_forge_webservice_hooks(owner, repo)
        else:
            print("Heroku webservice registration disabled.")
        print(
            "\nCI services have been enabled. You may wish to regenerate the feedstock.\n"
            "Any changes will need commiting to the repo."
        )


class AddAzureBuildId(Subcommand):
    subcommand = "azure-buildid"

    def __init__(self, parser):
        # conda-smithy azure-buildid ./
        super(AddAzureBuildId, self).__init__(
            parser,
            dedent(
                "Update the azure configuration stored in the config file."
            ),
        )
        scp = self.subcommand_parser
        scp.add_argument(
            "--feedstock_directory",
            default=os.getcwd(),
            help="The directory of the feedstock git repository.",
        )
        group = scp.add_mutually_exclusive_group()
        group.add_argument(
            "--user",
            help="azure username for which this repo is enabled already",
        )
        group.add_argument(
            "--organization",
            default=azure_ci_utils.AzureConfig._default_org,
            help="azure organisation for which this repo is enabled already",
        )
        scp.add_argument(
            "--project_name",
            default=azure_ci_utils.AzureConfig._default_project_name,
            help="project name that feedstocks are registered under",
        )

    def __call__(self, args):
        owner = args.user or args.organization
        repo = os.path.basename(os.path.abspath(args.feedstock_directory))

        config = azure_ci_utils.AzureConfig(
            org_or_user=owner, project_name=args.project_name
        )

        build_info = azure_ci_utils.get_build_id(repo, config)

        from .utils import update_conda_forge_config

        with update_conda_forge_config(args.feedstock_directory) as config:
            config.setdefault("azure", {})
            config["azure"]["build_id"] = build_info["build_id"]
            config["azure"]["user_or_org"] = build_info["user_or_org"]
            config["azure"]["project_name"] = build_info["project_name"]
            config["azure"]["project_id"] = build_info["project_id"]


class Regenerate(Subcommand):
    subcommand = "regenerate"
    aliases = ["rerender"]

    def __init__(self, parser):
        super(Regenerate, self).__init__(
            parser,
            "Regenerate / update the CI support files of the " "feedstock.",
        )
        scp = self.subcommand_parser
        scp.add_argument(
            "--feedstock_directory",
            default=os.getcwd(),
            help="The directory of the feedstock git repository.",
        )
        scp.add_argument(
            "-c",
            "--commit",
            nargs="?",
            choices=["edit", "auto"],
            const="edit",
            help="Whether to setup a commit or not.",
        )
        scp.add_argument(
            "--no-check-uptodate",
            action="store_true",
            help="Don't check that conda-smithy and conda-forge-pinning are uptodate",
        )
        scp.add_argument(
            "-e",
            "--exclusive-config-file",
            default=None,
            help="Exclusive conda-build config file to replace conda-forge-pinning. "
            + "For advanced usage only",
        )
        scp.add_argument(
            "--check",
            action="store_true",
            default=False,
            help="Check if regenerate can be performed",
        )

    def __call__(self, args):
        configure_feedstock.main(
            args.feedstock_directory,
            no_check_uptodate=args.no_check_uptodate,
            commit=args.commit,
            exclusive_config_file=args.exclusive_config_file,
            check=args.check,
        )


class RecipeLint(Subcommand):
    subcommand = "recipe-lint"

    def __init__(self, parser):
        super(RecipeLint, self).__init__(parser, "Lint a single conda recipe.")
        scp = self.subcommand_parser
        scp.add_argument("--conda-forge", action="store_true")
        scp.add_argument("recipe_directory", default=[os.getcwd()], nargs="*")

    def __call__(self, args):
        all_good = True
        for recipe in args.recipe_directory:
            lints, hints = lint_recipe.main(
                os.path.join(recipe),
                conda_forge=args.conda_forge,
                return_hints=True,
            )
            if lints:
                all_good = False
                print(
                    "{} has some lint:\n  {}".format(
                        recipe, "\n  ".join(lints)
                    )
                )
                if hints:
                    print(
                        "{} also has some suggestions:\n  {}".format(
                            recipe, "\n  ".join(hints)
                        )
                    )
            elif hints:
                print(
                    "{} has some suggestions:\n  {}".format(
                        recipe, "\n  ".join(hints)
                    )
                )
            else:
                print("{} is in fine form".format(recipe))
        # Exit code 1 for some lint, 0 for no lint.
        sys.exit(int(not all_good))


POST_SKELETON_MESSAGE = """
A CI skeleton has been generated! Please use the following steps
to complete the CI setup process:

1. Fill out {args.recipe_directory}/meta.yaml with your install and test code
2. Commit all changes to the repo.

        $ git add . && git commit -m "ran conda smithy skeleton"

3. Remember to register your repo with the CI providers.
4. Rerender this repo to generate the CI configurations files.
   This can be done with:

        $ conda smithy rerender -c auto

At any time in the future, you will be able to automatically update your CI configuration by
re-running the rerender command above. Happy testing!
"""


class CISkeleton(Subcommand):
    subcommand = "ci-skeleton"

    def __init__(self, parser):
        super(CISkeleton, self).__init__(
            parser, "Generate skeleton for using CI outside of a feedstock"
        )
        scp = self.subcommand_parser
        scp.add_argument(
            "--feedstock-directory",
            default=os.getcwd(),
            help="The directory of the feedstock git repository.",
            dest="feedstock_directory",
        )
        scp.add_argument(
            "-r",
            "--recipe-directory",
            default="recipe",
            dest="recipe_directory",
        )
        scp.add_argument("package_name", nargs="?")

    def __call__(self, args):
        from conda_smithy.ci_skeleton import generate

        # complete configuration
        if getattr(args, "package_name") is None:
            args.package_name = "package"

        generate(
            package_name=args.package_name,
            feedstock_directory=args.feedstock_directory,
            recipe_directory=args.recipe_directory,
        )
        print(POST_SKELETON_MESSAGE.format(args=args).strip())


class UpdateCB3(Subcommand):
    subcommand = "update-cb3"

    def __init__(self, parser):
        # conda-smithy update-cb3 ./
        super(UpdateCB3, self).__init__(
            parser, "Update a feedstock for conda-build=3"
        )
        scp = self.subcommand_parser
        scp.add_argument(
            "--recipe_directory",
            default=os.path.join(os.getcwd(), "recipe"),
            help="The path to the source recipe directory.",
        )
        scp.add_argument(
            "--output",
            default=None,
            help="Filename for the output. No output edits the recipe inplace",
        )
        scp.add_argument(
            "--cbc",
            default=None,
            help="Path to conda_build_config.yaml. No path will use conda-forge-pinning",
        )

    def __call__(self, args):
        from conda_smithy.update_cb3 import update_cb3
        from conda_smithy.configure_feedstock import get_cfp_file_path

        recipe_file = os.path.join(args.recipe_directory, "meta.yaml")
        output_file = args.output
        if output_file is None:
            output_file = recipe_file
        if args.cbc is None:
            cbc, _ = get_cfp_file_path()
        else:
            cbc = os.path.join(os.getcwd(), args.cbc)
        output_content, messages = update_cb3(recipe_file, cbc)
        with io.open(output_file, "w") as fh:
            fh.write(output_content)
        print("List of changes done to the recipe:")
        print(messages)


def main():

    parser = argparse.ArgumentParser(
        "a tool to help create, administer and manage feedstocks."
    )
    subparser = parser.add_subparsers()
    # TODO: Consider allowing plugins/extensions using entry_points.
    # https://reinout.vanrees.org/weblog/2010/01/06/zest-releaser-entry-points.html
    for subcommand in Subcommand.__subclasses__():
        subcommand(subparser)
    # And the alias for rerender
    parser.add_argument(
        "--version",
        action="version",
        version=__version__,
        help="Show conda-smithy's version, and exit.",
    )

    if not sys.argv[1:]:
        args = parser.parse_args(["--help"])
    else:
        args = parser.parse_args()

    # Check conda version for compatibility
    CONDA_VERSION_MAX = "5.0"
    if LooseVersion(conda.__version__) >= LooseVersion(CONDA_VERSION_MAX):
        print(
            "You appear to be using conda {}, but conda-smithy {}\n"
            "is currently only compatible with conda versions < {}.\n".format(
                conda.__version__, __version__, CONDA_VERSION_MAX
            )
        )
        sys.exit(2)

    args.subcommand_func(args)


class GenerateFeedstockToken(Subcommand):
    subcommand = "generate-feedstock-token"

    def __init__(self, parser):
        super(GenerateFeedstockToken, self).__init__(
            parser,
            "Generate a feedstock token at ~/.conda-smithy/{user or org}_{project}_feedstock.token",
        )
        scp = self.subcommand_parser
        scp.add_argument(
            "--feedstock_directory",
            default=os.getcwd(),
            help="The directory of the feedstock git repository.",
        )
        group = scp.add_mutually_exclusive_group()
        group.add_argument(
            "--user", help="github username under which to register this repo"
        )
        group.add_argument(
            "--organization",
            default="conda-forge",
            help="github organisation under which to register this repo",
        )

    def __call__(self, args):
        from conda_smithy.feedstock_tokens import (
            generate_and_write_feedstock_token,
        )

        owner = args.user or args.organization
        repo = os.path.basename(os.path.abspath(args.feedstock_directory))

        generate_and_write_feedstock_token(owner, repo)
        print(
            "Your feedstock token has been generated at ~/.conda-smithy/%s_%s_feedstock.token\n"
            "This token is stored in plaintext so be careful!" % (owner, repo)
        )


class RegisterFeedstockToken(Subcommand):
    subcommand = "register-feedstock-token"

    def __init__(self, parser):
        # conda-smithy register-ci ./
        super(RegisterFeedstockToken, self).__init__(
            parser,
            "Register the feedstock token w/ the CI services for builds and "
            "with the token registry.",
        )
        scp = self.subcommand_parser
        scp.add_argument(
            "--feedstock_directory",
            default=os.getcwd(),
            help="The directory of the feedstock git repository.",
        )
        scp.add_argument(
            "--token_repo",
            default=None,
            help=(
                "The GitHub repo that stores feedstock tokens. The default is "
                "{user or org}/feedstock-tokens on GitHub."
            ),
        )
        group = scp.add_mutually_exclusive_group()
        group.add_argument(
            "--user", help="github username under which to register this repo"
        )
        group.add_argument(
            "--organization",
            default="conda-forge",
            help="github organisation under which to register this repo",
        )
        for ci in [
            "Azure",
            "Travis",
            "Circle",
            "Drone",
        ]:
            scp.add_argument(
                "--without-{}".format(ci.lower()),
                dest=ci.lower(),
                action="store_false",
                help="If set, {} will be not registered".format(ci),
            )
            default = {ci.lower(): True}
            scp.set_defaults(**default)

    def __call__(self, args):
        from conda_smithy.feedstock_tokens import (
            register_feedstock_token_with_proviers,
            register_feedstock_token,
            feedstock_token_exists,
        )

        owner = args.user or args.organization
        repo = os.path.basename(os.path.abspath(args.feedstock_directory))

        if args.token_repo is None:
            token_repo = (
                "https://${GITHUB_TOKEN}@github.com/%s/feedstock-tokens"
                % owner
            )
        else:
            token_repo = args.token_repo

        if feedstock_token_exists(owner, repo, token_repo):
            raise RuntimeError(
                "Token for repo %s/%s already exists!" % (owner, repo)
            )

        print("Registering the feedstock tokens. Can take up to ~30 seconds.")

        # do all providers first
        register_feedstock_token_with_proviers(
            owner,
            repo,
            drone=args.drone,
            circle=args.circle,
            travis=args.travis,
            azure=args.azure,
        )

        # then if that works do the github repo
        register_feedstock_token(owner, repo, token_repo)

        print("Successfully registered the feedstock token!")


if __name__ == "__main__":
    main()

conda_smithy/cli.py
diff --git a/conda_smithy/cli.py b/conda_smithy/cli.py
index 37cfc6492..c2b44ee9d 100644
--- a/conda_smithy/cli.py
+++ b/conda_smithy/cli.py
@@ -43,7 +43,7 @@ def generate_feedstock_content(target_directory, source_recipe_dir):
     forge_yml = os.path.join(target_directory, "conda-forge.yml")
     if not os.path.exists(forge_yml):
         with feedstock_io.write_file(forge_yml) as fh:
-            fh.write(u"[]")
+            fh.write(u"{}")
 
 
 class Subcommand(object):

feedstock created with essentially blank conda-forge.yml
<!--
Thanks for reporting your issue.
Please fill out the sections below.
-->
Issue:

The [proj-data-feedstock](https://github.com/conda-forge/proj-data-feedstock/tree/0a121acaff45af14cb42a5b24995d1e7eeaebff9) seems to have been created with an empty [conda-forge.yml](https://github.com/conda-forge/proj-data-feedstock/blob/0a121acaff45af14cb42a5b24995d1e7eeaebff9/conda-forge.yml).  Rerendering the feedstock did not populate the file, so I did so [manully](https://github.com/conda-forge/proj-data-feedstock/pull/3/files).

As a result of this empty `conda-forge.yml`, the linter was also failing:
```
conda smithy recipe-lint .
Traceback (most recent call last):
  File "/home/xylar/miniconda3/bin/conda-smithy", line 10, in <module>
    sys.exit(main())
  File "/home/xylar/miniconda3/lib/python3.7/site-packages/conda_smithy/cli.py", line 554, in main
    args.subcommand_func(args)
  File "/home/xylar/miniconda3/lib/python3.7/site-packages/conda_smithy/cli.py", line 392, in __call__
    return_hints=True,
  File "/home/xylar/miniconda3/lib/python3.7/site-packages/conda_smithy/lint_recipe.py", line 697, in main
    results, hints = lintify(meta, recipe_dir, conda_forge)
  File "/home/xylar/miniconda3/lib/python3.7/site-packages/conda_smithy/lint_recipe.py", line 550, in lintify
    shellcheck_enabled = code.get("shellcheck", {}).get(
AttributeError: 'CommentedSeq' object has no attribute 'get'
```


<br/>
Environment (<code>conda list</code>):
<details>

```
$ conda list
# packages in environment at /home/xylar/miniconda3:
#
# Name                    Version                   Build  Channel
_libgcc_mutex             0.1                 conda_forge    conda-forge
_openmp_mutex             4.5                       0_gnu    conda-forge
alabaster                 0.7.12                     py_0    conda-forge
anaconda-client           1.7.2                      py_0    conda-forge
astroid                   2.3.3                    py37_1    conda-forge
attrs                     19.3.0                     py_0    conda-forge
babel                     2.8.0                      py_0    conda-forge
backcall                  0.1.0                      py_0    conda-forge
bash-completion           2.10                          1    conda-forge
beautifulsoup4            4.8.2                    py37_0    conda-forge
bleach                    3.1.1                      py_0    conda-forge
blinker                   1.4                        py_1    conda-forge
bzip2                     1.0.8                h516909a_2    conda-forge
ca-certificates           2019.11.28           hecc5488_0    conda-forge
certifi                   2019.11.28       py37hc8dfbb8_0    conda-forge
cffi                      1.14.0           py37hd463f26_0    conda-forge
chardet                   3.0.4                 py37_1003    conda-forge
click                     7.0                        py_0    conda-forge
cloudpickle               1.3.0                      py_0    conda-forge
clyent                    1.2.2                      py_1    conda-forge
conda                     4.8.2                    py37_0    conda-forge
conda-bash-completion     1.5                           0    conda-forge
conda-build               3.18.12                  py37_1    conda-forge
conda-env                 2.6.0                         1    conda-forge
conda-forge-build-setup   4.4.23                        0    conda-forge
conda-forge-ci-setup      2.5.3                    py37_0    conda-forge
conda-forge-pinning       2020.03.09                    0    conda-forge
conda-package-handling    1.6.0            py37h516909a_1    conda-forge
conda-smithy              3.6.11                     py_0    conda-forge
conda-verify              3.1.1                 py37_1000    conda-forge
cryptography              2.8              py37h72c5cf5_1    conda-forge
curl                      7.68.0               hf8cf82a_0    conda-forge
dbus                      1.13.6               he372182_0    conda-forge
decorator                 4.4.2                      py_0    conda-forge
defusedxml                0.6.0                      py_0    conda-forge
deprecated                1.2.7                      py_0    conda-forge
docutils                  0.16                     py37_0    conda-forge
entrypoints               0.3                   py37_1000    conda-forge
expat                     2.2.9                he1b5a44_2    conda-forge
filelock                  3.0.10                     py_0    conda-forge
fontconfig                2.13.1            h86ecdb6_1001    conda-forge
freetype                  2.10.0               he983fc9_1    conda-forge
future                    0.18.2                   py37_0    conda-forge
geos                      3.8.0                he1b5a44_0    conda-forge
gettext                   0.19.8.1          hc5be6a0_1002    conda-forge
git                       2.23.0          pl526hce37bd2_2    conda-forge
gitdb                     4.0.2                      py_0    conda-forge
gitpython                 3.1.0                      py_0    conda-forge
glib                      2.58.3          py37h6f030ca_1002    conda-forge
glob2                     0.7                        py_0    conda-forge
gst-plugins-base          1.14.5               h0935bb2_2    conda-forge
gstreamer                 1.14.5               h36ae1b5_2    conda-forge
icu                       64.2                 he1b5a44_1    conda-forge
idna                      2.9                        py_1    conda-forge
imagesize                 1.2.0                      py_0    conda-forge
importlib_metadata        1.5.0                    py37_0    conda-forge
ipykernel                 5.1.4            py37h5ca1d4c_0    conda-forge
ipython                   7.13.0           py37h5ca1d4c_0    conda-forge
ipython_genutils          0.2.0                      py_1    conda-forge
isodate                   0.6.0                      py_1    conda-forge
isort                     4.3.21                   py37_0    conda-forge
jedi                      0.16.0                   py37_0    conda-forge
jeepney                   0.4.3                      py_0    conda-forge
jinja2                    2.11.1                     py_0    conda-forge
jpeg                      9c                h14c3975_1001    conda-forge
jsonschema                3.2.0                    py37_0    conda-forge
jupyter_client            6.0.0                      py_0    conda-forge
jupyter_core              4.6.3                    py37_0    conda-forge
keyring                   21.1.1                   py37_0    conda-forge
krb5                      1.16.4               h2fd8d38_0    conda-forge
lazy-object-proxy         1.4.3            py37h516909a_1    conda-forge
ld_impl_linux-64          2.33.1               h53a641e_8    conda-forge
libarchive                3.4.0                hfd892fa_2    conda-forge
libclang                  9.0.1           default_hde54327_0    conda-forge
libcurl                   7.68.0               hda55be3_0    conda-forge
libedit                   3.1.20170329      hf8c457e_1001    conda-forge
libffi                    3.2.1             he1b5a44_1006    conda-forge
libgcc-ng                 9.2.0                h24d8f2e_2    conda-forge
libgomp                   9.2.0                h24d8f2e_2    conda-forge
libiconv                  1.15              h516909a_1005    conda-forge
liblief                   0.9.0                hf8a498c_1    conda-forge
libllvm9                  9.0.1                hc9558a2_0    conda-forge
libpng                    1.6.37               hed695b0_0    conda-forge
libsodium                 1.0.17               h516909a_0    conda-forge
libssh2                   1.8.2                h22169c7_2    conda-forge
libstdcxx-ng              9.2.0                hdf63c60_2    conda-forge
libuuid                   2.32.1            h14c3975_1000    conda-forge
libxcb                    1.13              h14c3975_1002    conda-forge
libxkbcommon              0.10.0               he1b5a44_0    conda-forge
libxml2                   2.9.10               hee79883_0    conda-forge
lz4-c                     1.8.3             he1b5a44_1001    conda-forge
lzo                       2.10              h14c3975_1000    conda-forge
markupsafe                1.1.1            py37h516909a_0    conda-forge
mccabe                    0.6.1                      py_1    conda-forge
mistune                   0.8.4           py37h516909a_1000    conda-forge
msrest                    0.6.10                     py_0    conda-forge
nbconvert                 5.6.1                    py37_0    conda-forge
nbformat                  5.0.4                      py_0    conda-forge
ncurses                   6.1               hf484d3e_1002    conda-forge
nspr                      4.25                 he1b5a44_0    conda-forge
nss                       3.47                 he751ad9_0    conda-forge
numpydoc                  0.9.2                      py_0    conda-forge
oauthlib                  3.0.1                      py_0    conda-forge
openssl                   1.1.1d               h516909a_0    conda-forge
packaging                 20.1                       py_0    conda-forge
pandoc                    2.9.2                         0    conda-forge
pandocfilters             1.4.2                      py_1    conda-forge
parso                     0.6.2                      py_0    conda-forge
patchelf                  0.10                 he1b5a44_0    conda-forge
pcre                      8.44                 he1b5a44_0    conda-forge
perl                      5.26.2            h516909a_1006    conda-forge
pexpect                   4.8.0                    py37_0    conda-forge
pickleshare               0.7.5                 py37_1000    conda-forge
pip                       20.0.2                     py_2    conda-forge
pkginfo                   1.5.0.1                    py_0    conda-forge
prompt-toolkit            3.0.4                      py_0    conda-forge
prompt_toolkit            3.0.4                         0    conda-forge
psutil                    5.7.0            py37h516909a_0    conda-forge
pthread-stubs             0.4               h14c3975_1001    conda-forge
ptyprocess                0.6.0                   py_1001    conda-forge
py-lief                   0.9.0            py37he1b5a44_1    conda-forge
pycodestyle               2.5.0                      py_0    conda-forge
pycosat                   0.6.3           py37h516909a_1002    conda-forge
pycparser                 2.20                       py_0    conda-forge
pycrypto                  2.6.1           py37h516909a_1003    conda-forge
pyflakes                  2.1.1                      py_0    conda-forge
pygithub                  1.43.6                   py37_0    conda-forge
pygments                  2.6.1                      py_0    conda-forge
pyjwt                     1.7.1                      py_0    conda-forge
pylint                    2.4.4                    py37_0    conda-forge
pyopenssl                 19.1.0                     py_1    conda-forge
pyparsing                 2.4.6                      py_0    conda-forge
pyqt                      5.12.3           py37hcca6a23_1    conda-forge
pyqt5-sip                 4.19.18                  pypi_0    pypi
pyqtwebengine             5.12.1                   pypi_0    pypi
pyrsistent                0.15.7           py37h516909a_0    conda-forge
pysocks                   1.7.1                    py37_0    conda-forge
python                    3.7.6                h357f687_0    conda-forge
python-dateutil           2.8.1                      py_0    conda-forge
python-libarchive-c       2.9                      py37_0    conda-forge
python_abi                3.7                     1_cp37m    conda-forge
pytz                      2019.3                     py_0    conda-forge
pyyaml                    5.3              py37h516909a_0    conda-forge
pyzmq                     19.0.0           py37h1768529_0    conda-forge
qt                        5.12.5               hd8c4c69_1    conda-forge
qtawesome                 0.7.0                      py_0    conda-forge
qtconsole                 4.7.1                      py_0    conda-forge
qtpy                      1.9.0                      py_0    conda-forge
readline                  8.0                  hf8c457e_0    conda-forge
requests                  2.23.0                   py37_0    conda-forge
requests-oauthlib         1.2.0                      py_0    conda-forge
ripgrep                   11.0.2               h516909a_3    conda-forge
rope                      0.16.0                     py_0    conda-forge
ruamel.yaml               0.16.6           py37h516909a_0    conda-forge
ruamel.yaml.clib          0.2.0            py37h516909a_0    conda-forge
ruamel_yaml               0.15.80         py37h516909a_1000    conda-forge
secretstorage             3.1.2                    py37_0    conda-forge
setuptools                46.0.0                   py37_0    conda-forge
six                       1.14.0                   py37_0    conda-forge
smmap                     3.0.1                      py_0    conda-forge
snowballstemmer           2.0.0                      py_0    conda-forge
soupsieve                 1.9.4                    py37_0    conda-forge
sphinx                    2.4.4                      py_0    conda-forge
sphinxcontrib-applehelp   1.0.2                      py_0    conda-forge
sphinxcontrib-devhelp     1.0.2                      py_0    conda-forge
sphinxcontrib-htmlhelp    1.0.3                      py_0    conda-forge
sphinxcontrib-jsmath      1.0.1                      py_0    conda-forge
sphinxcontrib-qthelp      1.0.3                      py_0    conda-forge
sphinxcontrib-serializinghtml 1.1.4                      py_0    conda-forge
spyder                    3.3.6                    py37_1    conda-forge
spyder-kernels            0.5.2                    py37_0    conda-forge
sqlite                    3.30.1               hcee41ef_0    conda-forge
testpath                  0.4.4                      py_0    conda-forge
tk                        8.6.10               hed695b0_0    conda-forge
toolz                     0.10.0                     py_0    conda-forge
tornado                   6.0.4            py37h516909a_0    conda-forge
tqdm                      4.43.0                     py_0    conda-forge
traitlets                 4.3.3                    py37_0    conda-forge
typed-ast                 1.4.1            py37h516909a_0    conda-forge
urllib3                   1.25.7                   py37_0    conda-forge
vsts-python-api           0.1.22                     py_0    conda-forge
wcwidth                   0.1.8                      py_0    conda-forge
webencodings              0.5.1                      py_1    conda-forge
wheel                     0.34.2                     py_1    conda-forge
wrapt                     1.12.1           py37h516909a_0    conda-forge
wurlitzer                 2.0.0                    py37_0    conda-forge
xorg-libxau               1.0.9                h14c3975_0    conda-forge
xorg-libxdmcp             1.1.3                h516909a_0    conda-forge
xz                        5.2.4             h14c3975_1001    conda-forge
yaml                      0.2.2                h516909a_1    conda-forge
zeromq                    4.3.2                he1b5a44_2    conda-forge
zipp                      3.1.0                      py_0    conda-forge
zlib                      1.2.11            h516909a_1006    conda-forge
zstd                      1.4.4                h3b9ef0a_1    conda-forge
```
</details>

<br/>
Details about  <code>conda</code> and system ( <code>conda info</code> ):
<details>

```
$ conda info

     active environment : base
    active env location : /home/xylar/miniconda3
            shell level : 1
       user config file : /home/xylar/.condarc
 populated config files : /home/xylar/.condarc
          conda version : 4.8.2
    conda-build version : 3.18.12
         python version : 3.7.6.final.0
       virtual packages : __glibc=2.27
       base environment : /home/xylar/miniconda3  (writable)
           channel URLs : https://conda.anaconda.org/conda-forge/linux-64
                          https://conda.anaconda.org/conda-forge/noarch
                          https://repo.anaconda.com/pkgs/main/linux-64
                          https://repo.anaconda.com/pkgs/main/noarch
                          https://repo.anaconda.com/pkgs/r/linux-64
                          https://repo.anaconda.com/pkgs/r/noarch
          package cache : /home/xylar/miniconda3/pkgs
                          /home/xylar/.conda/pkgs
       envs directories : /home/xylar/miniconda3/envs
                          /home/xylar/.conda/envs
               platform : linux-64
             user-agent : conda/4.8.2 requests/2.23.0 CPython/3.7.6 Linux/4.15.0-1073-oem ubuntu/18.04.4 glibc/2.27
                UID:GID : 1001:1001
             netrc file : None
           offline mode : False
```
</details>

