docs: 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)))