src/commands/apps/info.js
'use strict'
const co = require('co')
const cli = require('heroku-cli-util')
function * run (context, heroku) {
const filesize = require('filesize')
const util = require('util')
const {countBy, snakeCase} = require('lodash')
function * getInfo (app) {
const pipelineCouplings = heroku.get(`/apps/${app}/pipeline-couplings`).catch(() => null)
let promises = {
addons: heroku.get(`/apps/${app}/addons`),
app: heroku.request({
path: `/apps/${app}`,
headers: {'Accept': 'application/vnd.heroku+json; version=3.cedar-acm'}
}),
dynos: heroku.get(`/apps/${app}/dynos`).catch(() => []),
collaborators: heroku.get(`/apps/${app}/collaborators`).catch(() => []),
pipeline_coupling: pipelineCouplings,
pipeline: pipelineCouplings // TODO: Remove this key once we feel comfortable with https://github.com/heroku/heroku-apps/pull/207#issuecomment-335775852.
}
if (context.flags.extended) {
promises.appExtended = heroku.get(`/apps/${app}?extended=true`)
}
let data = yield promises
if (context.flags.extended) {
data.appExtended.acm = data.app.acm
data.app = data.appExtended
delete data['appExtended']
}
return data
}
let app = context.args.app || context.app
if (!app) throw new Error('No app specified.\nUSAGE: heroku info my-app')
context.app = app // make sure context.app is always set for herkou-cli-util
let info = yield getInfo(app)
let addons = info.addons.map(a => a.plan.name).sort()
let collaborators = info.collaborators.map(c => c.user.email).filter(c => c !== info.app.owner.email).sort()
function print () {
let data = {}
data.Addons = addons
data.Collaborators = collaborators
if (info.app.archived_at) data['Archived At'] = cli.formatDate(new Date(info.app.archived_at))
if (info.app.cron_finished_at) data['Cron Finished At'] = cli.formatDate(new Date(info.app.cron_finished_at))
if (info.app.cron_next_run) data['Cron Next Run'] = cli.formatDate(new Date(info.app.cron_next_run))
if (info.app.database_size) data['Database Size'] = filesize(info.app.database_size, {round: 0})
if (info.app.create_status !== 'complete') data['Create Status'] = info.app.create_status
if (info.app.space) data['Space'] = info.app.space.name
if (info.app.space && info.app.internal_routing) data['Internal Routing'] = info.app.internal_routing
if (info.pipeline_coupling) data['Pipeline'] = `${info.pipeline_coupling.pipeline.name} - ${info.pipeline.stage}`
data['Auto Cert Mgmt'] = info.app.acm
data['Git URL'] = info.app.git_url
data['Web URL'] = info.app.web_url
data['Repo Size'] = filesize(info.app.repo_size, {round: 0})
data['Slug Size'] = filesize(info.app.slug_size, {round: 0})
data['Owner'] = info.app.owner.email
data['Region'] = info.app.region.name
data['Dynos'] = countBy(info.dynos, 'type')
data['Stack'] = (function (app) {
let stack = info.app.stack.name
if (app.stack.name !== app.build_stack.name) {
stack += ` (next build will use ${app.build_stack.name})`
}
return stack
})(info.app)
cli.styledHeader(info.app.name)
cli.styledObject(data)
if (context.flags.extended) {
cli.log('\n\n--- Extended Information ---\n\n')
if (info.app.extended) {
cli.log(util.inspect(info.app.extended))
}
}
}
function shell () {
function print (k, v) {
cli.log(`${snakeCase(k)}=${v}`)
}
print('auto_cert_mgmt', info.app.acm)
print('addons', addons)
print('collaborators', collaborators)
if (info.app.archived_at) print('archived_at', cli.formatDate(new Date(info.app.archived_at)))
if (info.app.cron_finished_at) print('cron_finished_at', cli.formatDate(new Date(info.app.cron_finished_at)))
if (info.app.cron_next_run) print('cron_next_run', cli.formatDate(new Date(info.app.cron_next_run)))
if (info.app.database_size) print('database_size', filesize(info.app.database_size, {round: 0}))
if (info.app.create_status !== 'complete') print('create_status', info.app.create_status)
if (info.pipeline_coupling) print('pipeline', `${info.pipeline_coupling.pipeline.name}:${info.pipeline.stage}`)
print('git_url', info.app.git_url)
print('web_url', info.app.web_url)
print('repo_size', filesize(info.app.repo_size, {round: 0}))
print('slug_size', filesize(info.app.slug_size, {round: 0}))
print('owner', info.app.owner.email)
print('region', info.app.region.name)
print('dynos', util.inspect(countBy(info.dynos, 'type')))
print('stack', info.app.stack.name)
}
if (context.flags.shell) {
shell()
} else if (context.flags.json) {
cli.styledJSON(info)
cli.warn('DEPRECATION WARNING: `pipeline` key will be removed in favor of `pipeline_coupling`')
} else {
print()
}
}
let cmd = {
description: 'show detailed app information',
help: `$ 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
...`,
wantsApp: true,
needsAuth: true,
args: [{name: 'app', hidden: true, optional: true}],
flags: [
{name: 'shell', char: 's', description: 'output more shell friendly key/value pairs'},
{name: 'extended', char: 'x', hidden: true},
{name: 'json', char: 'j'}
],
run: cli.command({preauth: true}, co.wrap(run))
}
module.exports = [
Object.assign({topic: 'apps', command: 'info'}, cmd),
Object.assign({topic: 'info', hidden: true}, cmd)
]