heroku
Version:
CLI to interact with Heroku
128 lines (127 loc) • 6.13 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
const color_1 = require("@heroku-cli/color");
const command_1 = require("@heroku-cli/command");
const core_1 = require("@oclif/core");
const heroku_cli_util_1 = require("@heroku/heroku-cli-util");
const lodash_1 = require("lodash");
const statusHelper = require("../../lib/releases/status_helper");
const time = require("../../lib/time");
const stripAnsi = require("strip-ansi");
const getDescriptionTruncation = function (releases, columns, optimizeKey) {
// width management here is quite opaque.
// This entire function is to determine how much of Formation.description should be truncated to accommodate for Formation.status. They both go in the same column.
// Nothing else is truncated and the table is passed `'no-truncate': true` in options.
let optimizationWidth = 0;
const optimizationWidthMap = {};
for (const key of Object.keys(columns)) {
optimizationWidthMap[key] = 0;
}
for (const row of releases) {
for (const colKey in row) {
if (colKey === optimizeKey) {
continue;
}
for (const [key, col] of Object.entries(columns)) {
const parts = key.split('.');
const matchKey = parts[0];
if (matchKey !== colKey) {
continue;
}
let colValue = row;
for (const part of parts) {
colValue = colValue[part];
}
let formattedValue;
if (col.get) {
formattedValue = col.get(row);
}
else {
formattedValue = colValue.toString();
}
if (key !== optimizeKey) {
optimizationWidthMap[key] = Math.max(optimizationWidthMap[key], stripAnsi(formattedValue).length);
}
}
}
}
for (const key of Object.keys(columns)) {
if (key !== optimizeKey) {
optimizationWidth += optimizationWidthMap[key] + 2;
}
}
return optimizationWidth;
};
class Index extends command_1.Command {
async run() {
const { flags } = await this.parse(Index);
const { app, num, json, extended } = flags;
const url = `/apps/${app}/releases${extended ? '?extended=true' : ''}`;
const { body: releases } = await this.heroku.request(url, {
partial: true,
headers: {
Range: `version ..; max=${num || 15}, order=desc`,
Accept: 'application/vnd.heroku+json; version=3.sdk',
},
});
let optimizationWidth = 0;
const descriptionWithStatus = function (release) {
const { description } = release;
const width = () => { var _a; return ((_a = process.stdout) === null || _a === void 0 ? void 0 : _a.columns) && process.stdout.columns > 80 ? process.stdout.columns : 80; };
const trunc = (l, s) => {
if (process.stdout.isTTY) {
return (0, lodash_1.truncate)(s, { length: width() - (optimizationWidth + l), omission: '…' });
}
return s;
};
const status = statusHelper.description(release);
if (status) {
const sc = color_1.default[statusHelper.color(release.status)](status);
return trunc(status.length + 1, description) + ' ' + sc;
}
return trunc(0, description);
};
const columns = {
// column name "v" as ux.table will make it's width at least "version" even though 'no-header': true
v: { get: release => color_1.default[statusHelper.color(release.status)]('v' + release.version) },
description: { get: descriptionWithStatus },
user: { get: ({ user }) => color_1.default.magenta((user === null || user === void 0 ? void 0 : user.email) || '') },
created_at: { get: ({ created_at }) => time.ago(new Date(created_at || '')) },
slug_id: { extended: true, get: ({ extended }) => extended === null || extended === void 0 ? void 0 : extended.slug_id },
slug_uuid: { extended: true, get: ({ extended }) => extended === null || extended === void 0 ? void 0 : extended.slug_uuid },
};
// `getDescriptionTruncation` is dependent on `columns` being defined and thus `descriptionWithStatus`.
// `descriptionWithStatus` requires `optimizationWidth` to be defined. Redefine here before `descriptionWithStatus` is actually called.
optimizationWidth = getDescriptionTruncation(releases, columns, 'description');
if (json) {
core_1.ux.log(JSON.stringify(releases, null, 2));
}
else if (releases.length === 0) {
core_1.ux.log(`${app} has no releases.`);
}
else {
let header = `${app} Releases`;
const currentRelease = releases.find(r => r.current === true);
if (currentRelease) {
header += ' - ' + color_1.default.cyan(`Current: v${currentRelease.version}`);
}
heroku_cli_util_1.hux.styledHeader(header);
heroku_cli_util_1.hux.table(releases, columns, { 'no-header': true, 'no-truncate': true, extended });
}
}
}
exports.default = Index;
Index.topic = 'releases';
Index.description = 'display the releases for an app';
Index.examples = [
'v1 Config add FOO_BAR email@example.com 2015/11/17 17:37:41 (~ 1h ago)',
'v2 Config add BAR_BAZ email@example.com 2015/11/17 17:37:41 (~ 1h ago)',
'v3 Config add BAZ_QUX email@example.com 2015/11/17 17:37:41 (~ 1h ago)',
];
Index.flags = {
num: command_1.flags.string({ char: 'n', description: 'number of releases to show' }),
json: command_1.flags.boolean({ description: 'output releases in json format' }),
extended: command_1.flags.boolean({ char: 'x', hidden: true }),
remote: command_1.flags.remote(),
app: command_1.flags.app({ required: true }),
};
;