def publish(spec, nb_name, template='full', save_first=True):
    """
    Converts nb_name to an HTML file. Preserves widget functionality.

    Outputs a link to download HTML file after conversion if called in a
    notebook environment.

    Equivalent to running `nbinteract ${spec} ${nb_name}` on the command line.

    Args:
        spec (str): BinderHub spec for Jupyter image. Must be in the format:
            `${username}/${repo}/${branch}`.

        nb_name (str): Complete name of the notebook file to convert. Can be a
            relative path (eg. './foo/test.ipynb').

    Kwargs:
        template (str): Template to use for conversion. Valid templates:

            - 'full': Outputs a complete standalone HTML page with default
              styling. Automatically loads the nbinteract JS library.
            - 'partial': Outputs an HTML partial that can be embedded in
              another page. Automatically loads the nbinteract JS library but
              has no styling.
            - 'plain': Outputs an HTML partial used to embed in an HTML page
              where the nbinteract JS library is already loaded. Does not load
              JS library or styling

        save_first (bool): If True, saves the currently opened notebook before
            converting nb_name. Used to ensure notebook is written to
            filesystem before starting conversion. Does nothing if not in a
            notebook environment.


    Returns:
        None
    """
    if not os.path.isfile(nb_name):
        raise ValueError(
            "{} isn't a path to a file. Double check your "
            "filename and try again.".format(nb_name)
        )

    if save_first:
        _save_nb(nb_name)

    print('Converting notebook...')
    try:
        check_output(
            ['nbinteract', '--template', template, '--spec', spec, nb_name],
            stderr=STDOUT
        )
    except CalledProcessError as err:
        logging.warning(
            ERROR_MESSAGE.format(
                filename=nb_name, error=str(err.output, 'utf-8')
            )
        )
        return

    html_filename = os.path.splitext(nb_name)[0] + '.html'
    display(Markdown(CONVERT_SUCCESS_MD.format(url=html_filename)))