code: def publish_subcommand(publish):
      @publish.command()
      @add_common_publish_arguments_and_options
      @click.option(
          "-n",
          "--name",
          default="datasette",
          help="Application name to use when deploying",
      )
      @click.option(
          "--tar",
          help="--tar option to pass to Heroku, e.g. --tar=/usr/local/bin/gtar",
      )
      @click.option(
          "--generate-dir",
          type=click.Path(dir_okay=True, file_okay=False),
          help="Output generated application files and stop without deploying",
      )
      def heroku(
          files,
          metadata,
          extra_options,
          branch,
          template_dir,
          plugins_dir,
          static,
          install,
          plugin_secret,
          version_note,
          secret,
          title,
          license,
          license_url,
          source,
          source_url,
          about,
          about_url,
          name,
          tar,
          generate_dir,
      ):
          "Publish databases to Datasette running on Heroku"
          fail_if_publish_binary_not_installed(
              "heroku", "Heroku", "https://cli.heroku.com"
          )
  
          # Check for heroku-builds plugin
          plugins = [
              line.split()[0]
              for line in check_output(
                  ["heroku", "plugins"]
              ).splitlines()
          ]
          if b"heroku-builds" not in plugins:
              click.echo(
                  "Publishing to Heroku requires the heroku-builds plugin to be installed."
              )
              click.confirm(
                  "Install it? (this will run `heroku plugins:install heroku-builds`)",
                  abort=True,
              )
              call(["heroku", "plugins:install", "heroku-builds"])
  
          extra_metadata = {
              "title": title,
              "license": license,
              "license_url": license_url,
              "source": source,
              "source_url": source_url,
              "about": about,
              "about_url": about_url,
          }
  
          environment_variables = {}
          if plugin_secret:
              extra_metadata["plugins"] = {}
              for (
                  plugin_name,
                  plugin_setting,
                  setting_value,
              ) in plugin_secret:
                  environment_variable = (
                      f"{plugin_name}_{plugin_setting}".upper().replace(
                          "-", "_"
                      )
                  )
                  environment_variables[
                      environment_variable
                  ] = setting_value
                  extra_metadata["plugins"].setdefault(plugin_name, {})[
                      plugin_setting
                  ] = {"$env": environment_variable}
  
          with temporary_heroku_directory(
              files,
              name,
              metadata,
              extra_options,
              branch,
              template_dir,
              plugins_dir,
              static,
              install,
              version_note,
              secret,
              extra_metadata,
          ):
              if generate_dir:
                  # Recursively copy files from current working directory to it
                  if pathlib.Path(generate_dir).exists():
                      raise click.ClickException(
                          "Directory already exists"
                      )
                  shutil.copytree(".", generate_dir)
                  click.echo(
                      f"Generated files written to {generate_dir}, stopping without deploying",
                      err=True,
                  )
                  return
              app_name = None
              if name:
                  # Check to see if this app already exists
                  list_output = check_output(
                      ["heroku", "apps:list", "--json"]
                  ).decode("utf8")
                  apps = json.loads(list_output)
  
                  for app in apps:
                      if app["name"] == name:
                          app_name = name
                          break
  
              if not app_name:
                  # Create a new app
                  cmd = ["heroku", "apps:create"]
                  if name:
                      cmd.append(name)
                  cmd.append("--json")
                  create_output = check_output(cmd).decode("utf8")
                  app_name = json.loads(create_output)["name"]
  
              for key, value in environment_variables.items():
                  call(
                      [
                          "heroku",
                          "config:set",
                          "-a",
                          app_name,
                          f"{key}={value}",
                      ]
                  )
              tar_option = []
              if tar:
                  tar_option = ["--tar", tar]
              call(
                  [
                      "heroku",
                      "builds:create",
                      "-a",
                      app_name,
                      "--include-vcs-ignore",
                  ]
                  + tar_option
              )
  
file_path: datasette/datasette/publish/heroku.py
imports: from contextlib import contextmanager
  from datasette import hookimpl
  import click
  import json
  import os
  import pathlib
  import shlex
  import shutil
  from subprocess import call, check_output
  import tempfile
  from .common import add_common_publish_arguments_and_options, fail_if_publish_binary_not_installed
  from datasette.utils import link_or_copy, link_or_copy_directory, parse_metadata
label_desc: Use image node_id 1 for calling a global function with example usage: publish_subcommand(publish) without return types