Make and upload the release.

def make_release(ctx, bump=None, skip_local=False, skip_test=False,
                 skip_pypi=False, skip_isort=False):
    """Make and upload the release."""
    colorama.init()
    text.title("Minchin 'Make Release' for Python Projects v{}".format(__version__))
    print()

    text.subtitle("Configuration")
    extra_keys = ['here',
                  'source',
                  'test',
                  'docs',
                  'version',
                  'module_name',
                 ]
    check_configuration(ctx, 'releaser', extra_keys)

    check_existence(ctx.releaser.here, "base dir", "releaser.here")

    here = Path(ctx.releaser.here).resolve()
    try:
        check_existence(ctx.releaser.source, "source", "releaser.source", here)
        check_existence(ctx.releaser.test, "test dir", "releaser.test", here, True)
        check_existence(ctx.releaser.docs, "doc dir", "releaser.docs", here, True)
        check_existence(ctx.releaser.version, "version file", "releaser.version", here)
    except FileNotFoundError as e:
        print(e)
        sys.exit(1)
    print()

    text.subtitle("Git -- Clean directory?")
    try:
        repo = git.Repo(str(here))
    except git.exc.InvalidGitRepositoryError:
        repo = None
        print(textwrap.fill('[{}WARN{}] base directory does not appear to be '
                            'a valid git repo.'.format(WARNING_COLOR,
                                                       RESET_COLOR),
                            width=text.get_terminal_size().columns - 1,
                            subsequent_indent=' '*7))

    if repo is not None:
        if repo.is_dirty():
            print(textwrap.fill('[{}WARN{}] git repo is dirty. You should '
                                'probably commit your changes before '
                                'continuing.'.format(WARNING_COLOR,
                                                     RESET_COLOR),
                                width=text.get_terminal_size().columns - 1,
                                subsequent_indent=' '*7))
            # True = yes, False = Quit
            ans = text.query_yes_quit(' '*7 + 'Continue anyway or quit?',
                                      default="quit")
            if ans == text.Answers.QUIT:
                sys.exit(1)
        else:
            print("[{}GOOD{}] Clean Git repo.".format(GOOD_COLOR, RESET_COLOR))
    print()

    text.subtitle("Sort Import Statements")
    if not skip_isort:
        for f in Path(ctx.releaser.source).resolve().glob('**/*.py'):
            isort.SortImports(f)
            print('.', end="")
        if ctx.releaser.test is not None:
            for f in Path(ctx.releaser.test).resolve().glob('**/*.py'):
                isort.SortImports(f)
                print('.', end="")
        print(' Done!')
    else:
        print("[{}WARN{}] Skipped!".format(WARNING_COLOR, RESET_COLOR))
    print()

    if "vendor_packages" in ctx.releaser.keys():
        text.subtitle("Vendorize!")
        vendorize(ctx, internal_call=True)

    text.subtitle("Run Tests")
    # check setup.py
    # python setup.py -r -s
    # https://stackoverflow.com/questions/30328259/what-does-python-setup-py-check-actually-do
    try:
        cmd_none = (ctx.releaser.test_command).lower()
    except AttributeError:
        print("[{}WARN{}] test command not configured. Use key "
              "'releaser.test_command'.".format(WARNING_COLOR, RESET_COLOR))
        cmd_none = 'none'
    if cmd_none != 'none':
        result = invoke.run(ctx.releaser.test_command, warn=True)
        if not result.ok:
            print("[{}WARN{}] the test suite reported errors."
                  .format(WARNING_COLOR, RESET_COLOR))
            ans = text.query_yes_quit(' '*7 + 'Continue anyway or quit?',
                                      default="quit")
            if ans == text.Answers.QUIT:
                sys.exit(1)
    else:
        print('[{}WARN{}] No test command given.'.format(WARNING_COLOR,
                                                         RESET_COLOR))
    print()

    text.subtitle("Update Version Number")
    old_version, new_version = update_version_number(ctx, bump)
    print()

    text.subtitle("Add Release to Changelog")
    if old_version == new_version:
        print("[{}WARN{}] Version hasn't changed. Not updating Changelog."
              .format(WARNING_COLOR, RESET_COLOR))
    else:
        print("[{}WARN{}] I can't do this yet, but you probably should.\n"
              "           Not updating Changelog."
              .format(WARNING_COLOR, RESET_COLOR))
        # https://github.com/bitprophet/releases/blob/master/releases/util.py#L21
        # https://github.com/pyinvoke/invocations/blob/master/invocations/packaging/release.py#L362
    print()

    text.subtitle("Build Documentation")
    try:
        cmd_none = (ctx.releaser.doc_command).lower()
    except (AttributeError, KeyError):
        print("[{}WARN{}] documentation generation command not configured.\n"
              "           Use key 'releaser.doc_command'."
              .format(WARNING_COLOR, RESET_COLOR))
        cmd_none = 'none'
    if cmd_none != 'none':
        result = invoke.run(ctx.releaser.doc_command, warn=True)
        if not result.ok:
            print("[{}WARN{}] the documentation generation reported errors."
                  .format(WARNING_COLOR, RESET_COLOR))
            ans = text.query_yes_quit(' '*7 + 'Continue anyway or quit?',
                                      default="quit")
            if ans == text.Answers.QUIT:
                sys.exit(1)
    else:
        print('[{}WARN{}] No docmentation generation command given.'
              .format(WARNING_COLOR, RESET_COLOR))
    print()

    ans = text.query_yes_quit('All good and ready to go?')
    if ans == text.Answers.QUIT:
        sys.exit(1)
    print()

    text.subtitle("Build Distributions")
    build_distribution()
    print()

    text.subtitle("Check Readme Rendering")
    result = invoke.run('twine check dist/*', warn=True)
    if not result.ok:
        print("[{}WARN{}] Readme reported ReST rendering errors."
                .format(WARNING_COLOR, RESET_COLOR))
        ans = text.query_yes_quit(' '*7 + 'Continue anyway or quit?',
                                    default="quit")
        if ans == text.Answers.QUIT:
            sys.exit(1)
    else:
        print("[{}GOOD{}] Readme renders.".format(GOOD_COLOR, RESET_COLOR))
    print()

    server_list = []
    if not skip_local:
        server_list.append('local')
    if not skip_test:
        server_list.append('testpypi')
    if not skip_pypi:
        server_list.append('pypi')

    success_list = []
    for server in server_list:
        for file_format in ["tar.gz", "whl"]:
            text.subtitle("Test {} Build {}".format(file_format, server))
            s = check_local_install(ctx, new_version, file_format, server)
            success_list.append(s)
            print()

    text.subtitle("Install Test Summary")
    for line in success_list:
        print(line)
    print()

    # git commit

    if repo is not None:
        text.subtitle("Create Git Tag")
        _create_tag = True

        # don't duplicate existing tag
        tags = repo.tags
        for tag in tags:
            if tag.name == new_version:
                print("[{}WARN{}] Git tag for version {} already exists. "
                      "Skipping.".format(WARNING_COLOR, RESET_COLOR, tag.name))
                _create_tag = False
                break

        # warn on pre-release versions
        if new_version.prerelease:
            print("[{}WARN{}] Currently a pre-release version."
                  .format(WARNING_COLOR, RESET_COLOR))
            # True = yes, False = Quit
            ans = text.query_yes_no(' '*7 + 'Create Git tag anyway?',
                                    default="no")
            if ans == text.Answers.NO:
                _create_tag = False
        else:
            ans = text.query_yes_no('Create Git tag for version {}?'.format(new_version),
                                    default="no")
            if ans == text.Answers.NO:
                _create_tag = False

        if _create_tag:
            print("Creating Git tag for version {}".format(new_version))
            repo.create_tag(new_version)
        print()

    text.subtitle("Bump Version to Pre-release?")
    ans = text.query_yes_no("Bump version to pre-release now?")
    if ans == text.Answers.YES:
        old_version, new_version = update_version_number(ctx, 'prerelease', True)