class Heroku::Command::Apps

manage apps (create, destroy)

Public Instance Methods

create() click to toggle source
apps:create [NAME]

create a new app

    --addons ADDONS        # a comma-delimited list of addons to install
-b, --buildpack BUILDPACK  # a buildpack url to use for this app
-n, --no-remote            # don't create a git remote
-r, --remote REMOTE        # the git remote to create, default "heroku"
    --space SPACE          # HIDDEN: the space in which to create the app
-s, --stack STACK          # the stack on which to create the app
    --region REGION        # specify region for this app to run in
-l, --locked               # lock the app
    --ssh-git              # Use SSH git protocol
-t, --tier TIER            # HIDDEN: the tier for this app
    --http-git             # HIDDEN: Use HTTP git protocol

Examples:

$ heroku apps:create
Creating floating-dragon-42... done, stack is cedar
http://floating-dragon-42.heroku.com/ | https://git.heroku.com/floating-dragon-42.git

# specify a stack
$ heroku create -s cedar
Creating stormy-garden-5052... done, stack is cedar
https://stormy-garden-5052.herokuapp.com/ | https://git.heroku.com/stormy-garden-5052.git

# specify a name
$ heroku apps:create example
Creating example... done, stack is cedar
http://example.heroku.com/ | https://git.heroku.com/example.git

# create a staging app
$ heroku apps:create example-staging --remote staging

# create an app in the eu region
$ heroku apps:create --region eu
# File lib/heroku/command/apps.rb, line 245
def create
  name    = shift_argument || options[:app] || ENV['HEROKU_APP']
  validate_arguments!
  options[:ignore_no_org] = true
  validate_space_xor_org!

  params = {
    "name" => name,
    "region" => options[:region],
    "space" => options[:space],
    "stack" => Heroku::Command::Stack::Codex.in(options[:stack]),
    "locked" => options[:locked]
  }

  info = if options[:space]
    api.post_organizations_app_v3_dogwood(params).body
  elsif org
    org_api.post_app(params, org).body
  else
    api.post_app(params).body
  end

  begin
    display_org = !!org
    if info['space']
      space_name = info['space']['name']
      display_org = false
    end
    action("Creating #{info['name']}", :space => space_name, :org => display_org) do
      if info['create_status'] == 'creating'
        Timeout::timeout(options[:timeout].to_i) do
          loop do
            break if api.get_app(info['name']).body['create_status'] == 'complete'
            sleep 1
          end
        end
      end
      if options[:region]
        status("region is #{region_from_app(info)}")
      else
        stack = (info['stack'].is_a?(Hash) ? info['stack']["name"] : info['stack'])
        status("stack is #{Heroku::Command::Stack::Codex.out(stack)}")
      end
    end

    (options[:addons] || "").split(",").each do |addon|
      addon.strip!
      action("Adding #{addon} to #{info["name"]}") do
        api.post_addon(info["name"], addon)
      end
    end

    if buildpack = options[:buildpack]
      api.put_app_buildpacks_v3(info['name'], {:updates => [{:buildpack => buildpack}]})
      display "Buildpack set. Next release on #{info['name']} will use #{buildpack}."
    end

    hputs([ info["web_url"], git_url(info['name']) ].join(" | "))
  rescue Timeout::Error
    hputs("Timed Out! Run `heroku status` to check for known platform issues.")
  end

  unless options[:no_remote].is_a? FalseClass
    create_git_remote(options[:remote] || "heroku", git_url(info['name']))
  end
end
destroy() click to toggle source
apps:destroy --app APP

permanently destroy an app

Example:

$ heroku apps:destroy -a example --confirm example
Destroying example (including all add-ons)... done
# File lib/heroku/command/apps.rb, line 385
def destroy
  @app = shift_argument || options[:app] || options[:confirm]
  validate_arguments!

  unless @app
    error("Usage: heroku apps:destroy --app APP\nMust specify APP to destroy.")
  end

  api.get_app(@app) # fail fast if no access or doesn't exist

  message = "WARNING: Potentially Destructive Action\nThis command will destroy #{@app} (including all add-ons)."
  if confirm_command(@app, message)
    action("Destroying #{@app} (including all add-ons)") do
      api.delete_app(@app)
      if remotes = git_remotes(Dir.pwd)
        remotes.each do |remote_name, remote_app|
          next if @app != remote_app
          git "remote rm #{remote_name}"
        end
      end
    end
  end
end
downgrade() click to toggle source

apps:downgrade TIER –app APP

HIDDEN: downgrade an app's pricing tier

# File lib/heroku/command/apps.rb, line 502
def downgrade
  tier = shift_argument
  error("Usage: heroku apps:downgrade TIER\nMust specify TIER to downgrade.") if tier.nil? || tier.empty?
  validate_arguments!

  action("Upgrading #{app} to #{tier}") do
    api.put_app(app, "tier" => tier)
  end
end
index() click to toggle source
apps

list your apps

-o, --org ORG     # the org to list the apps for
    --space SPACE # HIDDEN: list apps in a given space
-A, --all         # list all apps in the org. Not just joined apps
-p, --personal    # list apps in personal account when a default org is set

Example:

$ heroku apps
=== My Apps
example
example2

=== Collaborated Apps
theirapp   other@owner.name
# File lib/heroku/command/apps.rb, line 28
def index
  validate_arguments!
  options[:ignore_no_org] = true
  validate_space_xor_org!

  apps = if options[:space]
    api.get_apps.body.select do |app|
      app["space"] && [app["space"]["name"], app["space"]["id"]].include?(options[:space])
    end
  elsif org
    org_api.get_apps(org).body
  else
    api.get_apps.body.select { |app| options[:all] ? true : !org?(app["owner_email"]) }
  end

  unless apps.empty?
    if options[:space]
      styled_header("Apps in space #{options[:space]}")
      styled_array(apps.map { |app| regionized_app_name(app) })
    elsif org
      joined, unjoined = apps.partition { |app| app['joined'] == true }

      styled_header("Apps joined in organization #{org}")
      unless joined.empty?
        styled_array(joined.map {|app| regionized_app_name(app) + (app['locked'] ? ' (locked)' : '') })
      else
        display("You haven't joined any apps.")
        display("Use --all to see unjoined apps.") unless options[:all]
        display
      end

      if options[:all]
        styled_header("Apps available to join in organization #{org}")
        unless unjoined.empty?
          styled_array(unjoined.map {|app| regionized_app_name(app) + (app['locked'] ? ' (locked)' : '') })
        else
          display("There are no apps to join.")
          display
        end
      end
    else
      my_apps, collaborated_apps = apps.partition { |app| app["owner_email"] == Heroku::Auth.user }

      unless my_apps.empty?
        styled_header("My Apps")
        styled_array(my_apps.map { |app| regionized_app_name(app) })
      end

      unless collaborated_apps.empty?
        styled_header("Collaborated Apps")
        styled_array(collaborated_apps.map { |app| [regionized_app_name(app), app_owner(app["owner_email"])] })
      end
    end
  else
    if options[:space]
      display("There are no apps available in space #{options[:space]}.")
    elsif org
      display("There are no apps in organization #{org}.")
    else
      display("You have no apps.")
    end
  end
end
info() click to toggle source
apps:info

show detailed app information

-s, --shell  # output more shell friendly key/value pairs

Examples:

$ heroku apps:info
=== example
Git URL:   https://git.heroku.com/example.git
Repo Size: 5M
...

$ heroku apps:info --shell
git_url=https://git.heroku.com/example.git
repo_size=5000000
...
# File lib/heroku/command/apps.rb, line 113
def info
  validate_arguments!
  requires_preauth
  app_data = api.get_app(app).body

  unless options[:shell]
    styled_header(app_data["name"])
  end

  addons_data = api.get_addons(app).body.map {|addon| addon['name']}.sort rescue {}
  collaborators_data = api.get_collaborators(app).body.map {|collaborator| collaborator["email"]}.sort
  collaborators_data.reject! {|email| email == app_data["owner_email"]}

  if org? app_data['owner_email']
    app_data['owner'] = app_owner(app_data['owner_email'])
    app_data.delete("owner_email")
  end

  if options[:shell]
    app_data['git_url'] = git_url(app_data['name'])
    if app_data['domain_name']
      app_data['domain_name'] = app_data['domain_name']['domain']
    end
    unless addons_data.empty?
      app_data['addons'] = addons_data.join(',')
    end
    unless collaborators_data.empty?
      app_data['collaborators'] = collaborators_data.join(',')
    end
    app_data.keys.sort_by { |a| a.to_s }.each do |key|
      hputs("#{key}=#{app_data[key]}")
    end
  else
    data = {}

    unless addons_data.empty?
      data["Addons"] = addons_data
    end

    if app_data["archived_at"]
      data["Archived At"] = format_date(app_data["archived_at"])
    end

    data["Collaborators"] = collaborators_data

    if app_data["create_status"] && app_data["create_status"] != "complete"
      data["Create Status"] = app_data["create_status"]
    end

    if app_data["cron_finished_at"]
      data["Cron Finished At"] = format_date(app_data["cron_finished_at"])
    end

    if app_data["cron_next_run"]
      data["Cron Next Run"] = format_date(app_data["cron_next_run"])
    end

    if app_data["database_size"]
      data["Database Size"] = format_bytes(app_data["database_size"])
    end

    data["Git URL"] = git_url(app_data['name'])

    if app_data["database_tables"]
      data["Database Size"].gsub!('(empty)', '0K') + " in #{quantify("table", app_data["database_tables"])}"
    end

    if app_data["dyno_hours"].is_a?(Hash)
      data["Dyno Hours"] = app_data["dyno_hours"].keys.map do |type|
        "%s - %0.2f dyno-hours" % [ type.to_s.capitalize, app_data["dyno_hours"][type] ]
      end
    end

    data["Owner Email"] = app_data["owner_email"] if app_data["owner_email"]
    data["Owner"] = app_data["owner"] if app_data["owner"]
    data["Region"] = app_data["region"] if app_data["region"]
    data["Space"] = app_data["space"]["name"] if app_data["space"] && app_data["space"]["name"]
    data["Repo Size"] = format_bytes(app_data["repo_size"]) if app_data["repo_size"]
    data["Slug Size"] = format_bytes(app_data["slug_size"]) if app_data["slug_size"]
    data["Cache Size"] = format_bytes(app_data["cache_size"]) if app_data["cache_size"]

    data["Stack"] = Heroku::Command::Stack::Codex.out(app_data["stack"])
    if data["Stack"] != "cedar-10"
      data.merge!("Dynos" => app_data["dynos"], "Workers" => app_data["workers"])
    end

    data["Web URL"] = app_data["web_url"]

    styled_hash(data)
  end
end
join() click to toggle source

apps:join –app APP

add yourself to an organization app

-a, –app APP # the app

# File lib/heroku/command/apps.rb, line 417
def join
  begin
    action("Joining application #{app}") do
      org_api.join_app(app)
    end
  rescue Heroku::API::Errors::NotFound
    error("Application does not exist or does not belong to an org that you have access to.")
  end
end
leave() click to toggle source

apps:leave –app APP

remove yourself from an organization app

-a, –app APP # the app

# File lib/heroku/command/apps.rb, line 434
def leave
  begin
    action("Leaving application #{app}") do
      if org_from_app = extract_org_from_app
        org_api.leave_app(app)
      else
        api.delete_collaborator(app, Heroku::Auth.user)
      end
    end
  end
end
lock() click to toggle source

apps:lock –app APP

lock an organization app to restrict access

# File lib/heroku/command/apps.rb, line 452
def lock
  begin
    action("Locking #{app}") {
      org_api.lock_app(app)
    }
    display("Organization members must be invited this app.")
  rescue Excon::Errors::NotFound
    error("#{app} was not found")
  end
end
open() click to toggle source
apps:open --app APP

open the app in a web browser

Example:

$ heroku apps:open
Opening example... done
# File lib/heroku/command/apps.rb, line 364
def open
  path = shift_argument
  validate_arguments!

  app_data = api.get_app(app).body

  url = [app_data['web_url'], path].join
  launchy("Opening #{app}", url)
end
rename() click to toggle source
apps:rename NEWNAME --app APP

rename the app

    --ssh-git              # Use SSH git protocol
    --http-git             # HIDDEN: Use HTTP git protocol

Example:

$ heroku apps:rename example-newname
http://example-newname.herokuapp.com/ | https://git.heroku.com/example-newname.git
Git remote heroku updated
# File lib/heroku/command/apps.rb, line 327
def rename
  newname = shift_argument
  if newname.nil? || newname.empty?
    error("Usage: heroku apps:rename NEWNAME\nMust specify NEWNAME to rename.")
  end
  validate_arguments!

  action("Renaming #{app} to #{newname}") do
    api.put_app(app, "name" => newname)
  end

  app_data = api.get_app(newname).body
  hputs([ app_data["web_url"], git_url(newname) ].join(" | "))

  if remotes = git_remotes(Dir.pwd)
    remotes.each do |remote_name, remote_app|
      next if remote_app != app
      git "remote rm #{remote_name}"
      git "remote add #{remote_name} #{git_url(newname)}"
      hputs("Git remote #{remote_name} updated")
    end
  else
    hputs("Don't forget to update your Git remotes on any local checkouts.")
  end
end
unlock() click to toggle source

apps:unlock –app APP

unlock an organization app so that any org member can join it

# File lib/heroku/command/apps.rb, line 469
def unlock
  begin
    action("Unlocking #{app}") {
      org_api.unlock_app(app)
    }
    display("All organization members can join this app.")
  rescue Excon::Errors::NotFound
    error("#{app} was not found")
  end
end
upgrade() click to toggle source

apps:upgrade TIER –app APP

HIDDEN: upgrade an app's pricing tier

# File lib/heroku/command/apps.rb, line 486
def upgrade
  tier = shift_argument
  error("Usage: heroku apps:upgrade TIER\nMust specify TIER to upgrade.") if tier.nil? || tier.empty?
  validate_arguments!

  action("Upgrading #{app} to #{tier}") do
    api.put_app(app, "tier" => tier)
  end
end

Private Instance Methods

region_from_app(app) click to toggle source
# File lib/heroku/command/apps.rb, line 527
def region_from_app app
  region = app["region"].is_a?(Hash) ? app["region"]["name"] : app["region"]
end
regionized_app_name(app) click to toggle source
# File lib/heroku/command/apps.rb, line 516
def regionized_app_name(app)
  region = region_from_app(app)

  # temporary, show region for non-us apps
  if app["region"] && region != 'us'
    "#{app["name"]} (#{region})"
  else
    app["name"]
  end
end
validate_space_xor_org!() click to toggle source
# File lib/heroku/command/apps.rb, line 531
def validate_space_xor_org!
  if options[:space] && options[:org]
    error "Specify option for space or org, but not both."
  end
end