heroku 11.1.1 → 11.2.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/bin/run.js +3 -6
- package/dist/commands/access/index.js +9 -8
- package/dist/commands/addons/index.d.ts +1 -1
- package/dist/commands/addons/index.js +59 -59
- package/dist/commands/apps/create.js +2 -1
- package/dist/commands/apps/index.js +4 -3
- package/dist/commands/apps/info.js +83 -83
- package/dist/commands/apps/transfer.d.ts +1 -4
- package/dist/commands/apps/transfer.js +4 -3
- package/dist/commands/auth/token.js +2 -1
- package/dist/commands/certs/add.d.ts +3 -12
- package/dist/commands/certs/add.js +8 -7
- package/dist/commands/certs/auto/index.js +2 -1
- package/dist/commands/certs/generate.d.ts +1 -1
- package/dist/commands/certs/generate.js +4 -3
- package/dist/commands/config/edit.d.ts +1 -1
- package/dist/commands/config/edit.js +52 -51
- package/dist/commands/dashboard.js +31 -30
- package/dist/commands/data/maintenances/info.d.ts +1 -1
- package/dist/commands/data/maintenances/info.js +4 -3
- package/dist/commands/data/maintenances/run.js +2 -2
- package/dist/commands/data/maintenances/schedule.d.ts +1 -1
- package/dist/commands/data/maintenances/schedule.js +4 -3
- package/dist/commands/data/pg/create.js +9 -9
- package/dist/commands/data/pg/credentials/create.js +2 -2
- package/dist/commands/data/pg/fork.d.ts +1 -1
- package/dist/commands/data/pg/fork.js +4 -3
- package/dist/commands/data/pg/info.js +40 -40
- package/dist/commands/data/pg/settings.js +3 -3
- package/dist/commands/data/pg/update.js +8 -8
- package/dist/commands/domains/add.d.ts +2 -2
- package/dist/commands/domains/add.js +6 -5
- package/dist/commands/domains/index.d.ts +1 -1
- package/dist/commands/domains/index.js +10 -10
- package/dist/commands/keys/add.js +13 -12
- package/dist/commands/local/index.d.ts +2 -0
- package/dist/commands/local/index.js +46 -11
- package/dist/commands/local/run.js +10 -2
- package/dist/commands/local/version.js +10 -2
- package/dist/commands/notifications/index.js +14 -14
- package/dist/commands/pg/backups/download.js +12 -12
- package/dist/commands/pg/backups/restore.js +15 -15
- package/dist/commands/pg/backups/schedule.d.ts +6 -6
- package/dist/commands/pg/backups/schedule.js +18 -18
- package/dist/commands/pg/backups/url.js +2 -2
- package/dist/commands/pg/credentials/create.js +5 -5
- package/dist/commands/pg/credentials/destroy.js +2 -2
- package/dist/commands/pg/credentials/rotate.js +2 -2
- package/dist/commands/pipelines/add.js +2 -1
- package/dist/commands/pipelines/create.js +2 -1
- package/dist/commands/ps/copy.js +3 -3
- package/dist/commands/ps/exec.js +2 -2
- package/dist/commands/ps/forward.js +1 -1
- package/dist/commands/ps/index.js +115 -115
- package/dist/commands/ps/scale.js +2 -1
- package/dist/commands/ps/type.js +6 -5
- package/dist/commands/releases/index.js +2 -1
- package/dist/commands/releases/rollback.js +2 -2
- package/dist/commands/spaces/destroy.js +7 -7
- package/dist/commands/spaces/drains/set.js +2 -2
- package/dist/commands/spaces/peerings/accept.js +1 -1
- package/dist/commands/spaces/ps.js +2 -2
- package/dist/commands/spaces/trusted-ips/add.js +2 -2
- package/dist/commands/spaces/trusted-ips/remove.js +1 -1
- package/dist/commands/status.js +2 -1
- package/dist/commands/version/info.js +2 -6
- package/dist/hooks/command_not_found/{performance_analytics.js → setup-otel-telemetry.js} +3 -2
- package/dist/hooks/init/{performance_analytics.js → setup-otel-telemetry.js} +3 -2
- package/dist/hooks/init/terms-of-service.js +1 -1
- package/dist/hooks/init/version.js +9 -6
- package/dist/hooks/postrun/{performance_analytics.js → send-otel-telemetry.js} +3 -2
- package/dist/hooks/prerun/collect-and-send-herokulytics.js +34 -0
- package/dist/{analytics.d.ts → lib/analytics-telemetry/backboard-herokulytics-client.d.ts} +20 -19
- package/dist/{analytics.js → lib/analytics-telemetry/backboard-herokulytics-client.js} +72 -54
- package/dist/lib/analytics-telemetry/backboard-otel-client.d.ts +16 -0
- package/dist/lib/analytics-telemetry/backboard-otel-client.js +121 -0
- package/dist/{user-config.d.ts → lib/analytics-telemetry/herokulytics-config.d.ts} +9 -9
- package/dist/{user-config.js → lib/analytics-telemetry/herokulytics-config.js} +40 -40
- package/dist/lib/analytics-telemetry/sentry-client.d.ts +14 -8
- package/dist/lib/analytics-telemetry/sentry-client.js +51 -50
- package/dist/lib/analytics-telemetry/telemetry-manager.d.ts +43 -0
- package/dist/lib/analytics-telemetry/telemetry-manager.js +133 -0
- package/dist/lib/analytics-telemetry/telemetry-utils.d.ts +18 -4
- package/dist/lib/analytics-telemetry/telemetry-utils.js +13 -9
- package/dist/lib/analytics-telemetry/telemetry-worker.js +48 -8
- package/dist/lib/analytics-telemetry/worker-client.d.ts +3 -2
- package/dist/lib/analytics-telemetry/worker-client.js +5 -16
- package/dist/lib/data/poolConfig.js +2 -2
- package/dist/lib/data/utils.d.ts +1 -1
- package/dist/lib/data/utils.js +1 -1
- package/dist/lib/lazy-module-loader.d.ts +58 -0
- package/dist/lib/lazy-module-loader.js +100 -0
- package/dist/lib/local/fork-foreman.d.ts +5 -0
- package/dist/lib/local/fork-foreman.js +18 -5
- package/dist/lib/local/load-foreman-procfile.js +3 -0
- package/dist/lib/ps-exec/exec.js +2 -2
- package/dist/lib/releases/status_helper.d.ts +1 -1
- package/dist/lib/run/colorize.js +10 -10
- package/npm-shrinkwrap.json +101 -14
- package/oclif.manifest.json +253 -246
- package/package.json +8 -8
- package/dist/deps.d.ts +0 -9
- package/dist/deps.js +0 -14
- package/dist/hooks/prerun/analytics.js +0 -23
- package/dist/lib/analytics-telemetry/global-telemetry.d.ts +0 -30
- package/dist/lib/analytics-telemetry/global-telemetry.js +0 -103
- package/dist/lib/analytics-telemetry/honeycomb-client.d.ts +0 -15
- package/dist/lib/analytics-telemetry/honeycomb-client.js +0 -135
- /package/dist/hooks/command_not_found/{performance_analytics.d.ts → setup-otel-telemetry.d.ts} +0 -0
- /package/dist/hooks/finally/{sentry.d.ts → send-otel-and-sentry-errors.d.ts} +0 -0
- /package/dist/hooks/finally/{sentry.js → send-otel-and-sentry-errors.js} +0 -0
- /package/dist/hooks/init/{performance_analytics.d.ts → setup-otel-telemetry.d.ts} +0 -0
- /package/dist/hooks/postrun/{performance_analytics.d.ts → send-otel-telemetry.d.ts} +0 -0
- /package/dist/hooks/prerun/{analytics.d.ts → collect-and-send-herokulytics.d.ts} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,29 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
6
|
|
|
7
|
+
## [11.2.0-beta.0](https://github.com/heroku/cli/compare/v11.1.1...v11.2.0-beta.0) (2026-04-06)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* adding --start-cmd flag to heroku local when no Procfile is present ([#3638](https://github.com/heroku/cli/issues/3638)) ([b9b8fed](https://github.com/heroku/cli/commit/b9b8fed8f44ad88946edc4141ec1fe41f157baac))
|
|
13
|
+
* update color usage, telemetry activation logic, and command/util dependencies ([#3646](https://github.com/heroku/cli/issues/3646)) ([ede6655](https://github.com/heroku/cli/commit/ede665557ca3d387e3e57f5a438cd7ab7c27b1b3))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Bug Fixes
|
|
17
|
+
|
|
18
|
+
* procfile comment parsing ([#3641](https://github.com/heroku/cli/issues/3641)) ([aae3751](https://github.com/heroku/cli/commit/aae37512b54bdcf2b832429254f0d2c0fe1f42d3))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
### Code Refactoring
|
|
22
|
+
|
|
23
|
+
* unify telemetry architecture and use background workers for all telemetry ([#3642](https://github.com/heroku/cli/issues/3642)) ([c13074d](https://github.com/heroku/cli/commit/c13074da5f0892fa412ce6bc7c4b1d998eae3366))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
### Tests
|
|
27
|
+
|
|
28
|
+
* this fixes the mocking difference causing flappy tests and adds chore to PR release title ([#3640](https://github.com/heroku/cli/issues/3640)) ([32920f9](https://github.com/heroku/cli/commit/32920f9600cc6b06815aa7db532dd8bb352d90db))
|
|
29
|
+
|
|
7
30
|
## [11.1.1](https://github.com/heroku/cli/compare/v11.1.0...v11.1.1) (2026-04-01)
|
|
8
31
|
|
|
9
32
|
|
package/bin/run.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env -S node --no-deprecation
|
|
2
2
|
|
|
3
|
-
/* eslint-disable n/no-unpublished-bin */
|
|
4
|
-
|
|
5
3
|
import {execute, settings} from '@oclif/core'
|
|
6
4
|
|
|
7
5
|
// Enable performance tracking when oclif:perf is specified in DEBUG
|
|
@@ -14,10 +12,9 @@ process.env.HEROKU_UPDATE_INSTRUCTIONS = process.env.HEROKU_UPDATE_INSTRUCTIONS
|
|
|
14
12
|
const now = new Date()
|
|
15
13
|
const cliStartTime = now.getTime()
|
|
16
14
|
|
|
17
|
-
|
|
18
|
-
const enableTelemetry = process.platform !== 'win32' || process.env.ENABLE_WINDOWS_TELEMETRY === 'true'
|
|
15
|
+
const {isTelemetryEnabled} = await import('../dist/lib/analytics-telemetry/telemetry-utils.js')
|
|
19
16
|
|
|
20
|
-
if (
|
|
17
|
+
if (isTelemetryEnabled()) {
|
|
21
18
|
// Dynamically import telemetry modules
|
|
22
19
|
const {setupTelemetryHandlers} = await import('../dist/lib/analytics-telemetry/worker-client.js')
|
|
23
20
|
const {computeDuration} = await import('../dist/lib/analytics-telemetry/telemetry-utils.js')
|
|
@@ -26,7 +23,7 @@ if (enableTelemetry) {
|
|
|
26
23
|
setupTelemetryHandlers({
|
|
27
24
|
cliStartTime,
|
|
28
25
|
computeDuration,
|
|
29
|
-
enableTelemetry,
|
|
26
|
+
enableTelemetry: isTelemetryEnabled(),
|
|
30
27
|
})
|
|
31
28
|
}
|
|
32
29
|
|
|
@@ -2,7 +2,7 @@ import { Command, flags } from '@heroku-cli/command';
|
|
|
2
2
|
import { HerokuAPIError } from '@heroku-cli/command/lib/api-client.js';
|
|
3
3
|
import { color, hux } from '@heroku/heroku-cli-util';
|
|
4
4
|
import { ux } from '@oclif/core/ux';
|
|
5
|
-
import
|
|
5
|
+
import { lazyModuleLoader } from '../../lib/lazy-module-loader.js';
|
|
6
6
|
import { getOwner, isTeamApp } from '../../lib/teamUtils.js';
|
|
7
7
|
export default class AccessIndex extends Command {
|
|
8
8
|
static description = 'list who has access to an app';
|
|
@@ -13,6 +13,7 @@ export default class AccessIndex extends Command {
|
|
|
13
13
|
};
|
|
14
14
|
static topic = 'access';
|
|
15
15
|
async run() {
|
|
16
|
+
const _ = await lazyModuleLoader.loadLodash();
|
|
16
17
|
const { flags } = await this.parse(AccessIndex);
|
|
17
18
|
const { app: appName, json } = flags;
|
|
18
19
|
const { body: app } = await this.heroku.get(`/apps/${appName}`);
|
|
@@ -28,7 +29,7 @@ export default class AccessIndex extends Command {
|
|
|
28
29
|
admin.permissions = adminPermissions;
|
|
29
30
|
return admin;
|
|
30
31
|
});
|
|
31
|
-
collaborators = buildCollaboratorsArray(collaborators, admins);
|
|
32
|
+
collaborators = buildCollaboratorsArray(collaborators, admins, _);
|
|
32
33
|
}
|
|
33
34
|
catch (error) {
|
|
34
35
|
if (!(error instanceof HerokuAPIError && error.http.statusCode === 403))
|
|
@@ -38,10 +39,10 @@ export default class AccessIndex extends Command {
|
|
|
38
39
|
if (json)
|
|
39
40
|
printJSON(collaborators);
|
|
40
41
|
else
|
|
41
|
-
printAccess(app, collaborators);
|
|
42
|
+
printAccess(app, collaborators, _);
|
|
42
43
|
}
|
|
43
44
|
}
|
|
44
|
-
function buildCollaboratorsArray(collaboratorsRaw, admins) {
|
|
45
|
+
function buildCollaboratorsArray(collaboratorsRaw, admins, _) {
|
|
45
46
|
const collaboratorsNoAdmins = _.reject(collaboratorsRaw, { role: 'admin' });
|
|
46
47
|
return _.union(collaboratorsNoAdmins, admins);
|
|
47
48
|
}
|
|
@@ -62,12 +63,12 @@ function buildTableColumns(showPermissions) {
|
|
|
62
63
|
}
|
|
63
64
|
return baseColumns;
|
|
64
65
|
}
|
|
65
|
-
function printAccess(app, collaborators) {
|
|
66
|
+
function printAccess(app, collaborators, _) {
|
|
66
67
|
const showPermissions = isTeamApp(app.owner?.email);
|
|
67
68
|
collaborators = _.chain(collaborators)
|
|
68
|
-
.sortBy(c => c.email || c.user.email)
|
|
69
|
-
.reject(c => /herokumanager\.com$/.test(c.user.email))
|
|
70
|
-
.map(collab => {
|
|
69
|
+
.sortBy((c) => c.email || c.user.email)
|
|
70
|
+
.reject((c) => /herokumanager\.com$/.test(c.user.email))
|
|
71
|
+
.map((collab) => {
|
|
71
72
|
const { email } = collab.user;
|
|
72
73
|
const { permissions, role } = collab;
|
|
73
74
|
const data = { email, role: role || 'collaborator' };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Command } from '@heroku-cli/command';
|
|
2
2
|
import * as Heroku from '@heroku-cli/schema';
|
|
3
|
-
export declare function renderAttachment(attachment: Heroku.AddOnAttachment, app: string, isLast?: boolean): string;
|
|
4
3
|
export default class Addons extends Command {
|
|
5
4
|
static description: string;
|
|
6
5
|
static examples: string[];
|
|
@@ -14,3 +13,4 @@ export default class Addons extends Command {
|
|
|
14
13
|
static usage: string;
|
|
15
14
|
run(): Promise<void>;
|
|
16
15
|
}
|
|
16
|
+
export declare function renderAttachment(attachment: Heroku.AddOnAttachment, app: string, isLast?: boolean): string;
|
|
@@ -1,9 +1,53 @@
|
|
|
1
|
-
import { color, hux } from '@heroku/heroku-cli-util';
|
|
2
1
|
import { Command, flags } from '@heroku-cli/command';
|
|
2
|
+
import { color, hux } from '@heroku/heroku-cli-util';
|
|
3
3
|
import { ux } from '@oclif/core/ux';
|
|
4
4
|
import _ from 'lodash';
|
|
5
5
|
import { formatPrice, formatState, grandfatheredPrice } from '../../lib/addons/util.js';
|
|
6
6
|
const topic = 'addons';
|
|
7
|
+
export default class Addons extends Command {
|
|
8
|
+
static description = `Lists your add-ons and attachments.
|
|
9
|
+
|
|
10
|
+
The default filter applied depends on whether you are in a Heroku app
|
|
11
|
+
directory. If so, the --app flag is implied. If not, the default of --all
|
|
12
|
+
is implied. Explicitly providing either flag overrides the default
|
|
13
|
+
behavior.
|
|
14
|
+
`;
|
|
15
|
+
static examples = [
|
|
16
|
+
`${color.command(`heroku ${topic} --all`)}`,
|
|
17
|
+
`${color.command(`heroku ${topic} --app acme-inc-www`)}`,
|
|
18
|
+
];
|
|
19
|
+
static flags = {
|
|
20
|
+
all: flags.boolean({ char: 'A', description: 'show add-ons and attachments for all accessible apps' }),
|
|
21
|
+
app: flags.app(),
|
|
22
|
+
json: flags.boolean({ description: 'return add-ons in json format' }),
|
|
23
|
+
remote: flags.remote(),
|
|
24
|
+
};
|
|
25
|
+
static topic = topic;
|
|
26
|
+
static usage = 'addons [--all|--app APP]';
|
|
27
|
+
async run() {
|
|
28
|
+
const { flags } = await this.parse(Addons);
|
|
29
|
+
const { all, app, json } = flags;
|
|
30
|
+
if (!all && app) {
|
|
31
|
+
const addons = await addonGetter(this.heroku, app);
|
|
32
|
+
if (json)
|
|
33
|
+
displayJSON(addons);
|
|
34
|
+
else
|
|
35
|
+
displayForApp(app, addons);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
const addons = await addonGetter(this.heroku);
|
|
39
|
+
if (json)
|
|
40
|
+
displayJSON(addons);
|
|
41
|
+
else
|
|
42
|
+
displayAll(addons);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export function renderAttachment(attachment, app, isLast = false) {
|
|
47
|
+
const line = isLast ? '\u2514\u2500' : '\u251C\u2500';
|
|
48
|
+
const attName = formatAttachment(attachment, attachment.app?.name !== app);
|
|
49
|
+
return ` ${color.gray(line)} ${attName}`;
|
|
50
|
+
}
|
|
7
51
|
async function addonGetter(api, app) {
|
|
8
52
|
let attachmentsResponse = null;
|
|
9
53
|
let addonsResponse;
|
|
@@ -84,21 +128,21 @@ function displayAll(addons) {
|
|
|
84
128
|
Plan: {
|
|
85
129
|
get({ plan }) {
|
|
86
130
|
if (plan === undefined)
|
|
87
|
-
return color.
|
|
131
|
+
return color.inactive('?');
|
|
88
132
|
return plan.name;
|
|
89
133
|
},
|
|
90
134
|
},
|
|
91
135
|
Price: {
|
|
92
136
|
get({ plan }) {
|
|
93
137
|
if (plan?.price === undefined)
|
|
94
|
-
return color.
|
|
138
|
+
return color.inactive('?');
|
|
95
139
|
return formatPrice({ hourly: true, price: plan?.price });
|
|
96
140
|
},
|
|
97
141
|
},
|
|
98
142
|
'Max Price': {
|
|
99
143
|
get({ plan }) {
|
|
100
144
|
if (plan?.price === undefined)
|
|
101
|
-
return color.
|
|
145
|
+
return color.inactive('?');
|
|
102
146
|
return formatPrice({ hourly: false, price: plan?.price });
|
|
103
147
|
},
|
|
104
148
|
},
|
|
@@ -126,20 +170,6 @@ function displayAll(addons) {
|
|
|
126
170
|
});
|
|
127
171
|
/* eslint-enable perfectionist/sort-objects */
|
|
128
172
|
}
|
|
129
|
-
function formatAttachment(attachment, showApp = true) {
|
|
130
|
-
const attName = color.attachment(attachment.name || '');
|
|
131
|
-
const output = [color.dim('as'), attName];
|
|
132
|
-
if (showApp) {
|
|
133
|
-
const appInfo = `on ${color.app(attachment.app?.name || '')} app`;
|
|
134
|
-
output.push(color.dim(appInfo));
|
|
135
|
-
}
|
|
136
|
-
return output.join(' ');
|
|
137
|
-
}
|
|
138
|
-
export function renderAttachment(attachment, app, isLast = false) {
|
|
139
|
-
const line = isLast ? '\u2514\u2500' : '\u251C\u2500';
|
|
140
|
-
const attName = formatAttachment(attachment, attachment.app?.name !== app);
|
|
141
|
-
return ` ${color.dim(line)} ${attName}`;
|
|
142
|
-
}
|
|
143
173
|
function displayForApp(app, addons) {
|
|
144
174
|
if (addons.length === 0) {
|
|
145
175
|
ux.stdout(`No add-ons for app ${app}.`);
|
|
@@ -150,7 +180,7 @@ function displayForApp(app, addons) {
|
|
|
150
180
|
const name = color.addon(addon.name || '');
|
|
151
181
|
let service = addon.addon_service?.name;
|
|
152
182
|
if (service === undefined) {
|
|
153
|
-
service = color.
|
|
183
|
+
service = color.gray('?');
|
|
154
184
|
}
|
|
155
185
|
const addonLine = `${service} (${name})`;
|
|
156
186
|
const atts = _.sortBy(addon.attachments, isForeignApp, 'app.name', 'name');
|
|
@@ -169,14 +199,14 @@ function displayForApp(app, addons) {
|
|
|
169
199
|
Plan: {
|
|
170
200
|
get: ({ plan }) => plan && plan.name !== undefined
|
|
171
201
|
? plan.name.replace(/^[^:]+:/, '')
|
|
172
|
-
: color.
|
|
202
|
+
: color.inactive('?'),
|
|
173
203
|
},
|
|
174
204
|
Price: {
|
|
175
205
|
get(addon) {
|
|
176
206
|
if (addon.app?.name === app) {
|
|
177
207
|
return formatPrice({ hourly: true, price: addon.plan?.price });
|
|
178
208
|
}
|
|
179
|
-
return color.
|
|
209
|
+
return color.gray(`(billed to ${color.app(addon.app?.name || '')} app)`);
|
|
180
210
|
},
|
|
181
211
|
},
|
|
182
212
|
// eslint-disable-next-line perfectionist/sort-objects
|
|
@@ -185,7 +215,7 @@ function displayForApp(app, addons) {
|
|
|
185
215
|
if (addon.app?.name === app) {
|
|
186
216
|
return formatPrice({ hourly: false, price: addon.plan?.price });
|
|
187
217
|
}
|
|
188
|
-
return color.
|
|
218
|
+
return color.gray(`(billed to ${color.app(addon.app?.name || '')} app)`);
|
|
189
219
|
},
|
|
190
220
|
},
|
|
191
221
|
State: {
|
|
@@ -199,42 +229,12 @@ function displayForApp(app, addons) {
|
|
|
199
229
|
function displayJSON(addons) {
|
|
200
230
|
ux.stdout(JSON.stringify(addons, null, 2));
|
|
201
231
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
behavior.
|
|
209
|
-
`;
|
|
210
|
-
static examples = [
|
|
211
|
-
`${color.command(`heroku ${topic} --all`)}`,
|
|
212
|
-
`${color.command(`heroku ${topic} --app acme-inc-www`)}`,
|
|
213
|
-
];
|
|
214
|
-
static flags = {
|
|
215
|
-
all: flags.boolean({ char: 'A', description: 'show add-ons and attachments for all accessible apps' }),
|
|
216
|
-
app: flags.app(),
|
|
217
|
-
json: flags.boolean({ description: 'return add-ons in json format' }),
|
|
218
|
-
remote: flags.remote(),
|
|
219
|
-
};
|
|
220
|
-
static topic = topic;
|
|
221
|
-
static usage = 'addons [--all|--app APP]';
|
|
222
|
-
async run() {
|
|
223
|
-
const { flags } = await this.parse(Addons);
|
|
224
|
-
const { all, app, json } = flags;
|
|
225
|
-
if (!all && app) {
|
|
226
|
-
const addons = await addonGetter(this.heroku, app);
|
|
227
|
-
if (json)
|
|
228
|
-
displayJSON(addons);
|
|
229
|
-
else
|
|
230
|
-
displayForApp(app, addons);
|
|
231
|
-
}
|
|
232
|
-
else {
|
|
233
|
-
const addons = await addonGetter(this.heroku);
|
|
234
|
-
if (json)
|
|
235
|
-
displayJSON(addons);
|
|
236
|
-
else
|
|
237
|
-
displayAll(addons);
|
|
238
|
-
}
|
|
232
|
+
function formatAttachment(attachment, showApp = true) {
|
|
233
|
+
const attName = color.attachment(attachment.name || '');
|
|
234
|
+
const output = [color.gray('as'), attName];
|
|
235
|
+
if (showApp) {
|
|
236
|
+
const appInfo = `on ${color.app(attachment.app?.name || '')} app`;
|
|
237
|
+
output.push(color.gray(appInfo));
|
|
239
238
|
}
|
|
239
|
+
return output.join(' ');
|
|
240
240
|
}
|
|
@@ -3,8 +3,8 @@ import { Command, flags } from '@heroku-cli/command';
|
|
|
3
3
|
import { BuildpackCompletion, RegionCompletion, SpaceCompletion, StackCompletion, } from '@heroku-cli/command/lib/completions.js';
|
|
4
4
|
import { Args, ux } from '@oclif/core';
|
|
5
5
|
import fs from 'fs-extra';
|
|
6
|
-
import { parse } from 'yaml';
|
|
7
6
|
import Git from '../../lib/git/git.js';
|
|
7
|
+
import { lazyModuleLoader } from '../../lib/lazy-module-loader.js';
|
|
8
8
|
const git = new Git();
|
|
9
9
|
function createText(name, space) {
|
|
10
10
|
let text = `Creating ${name ? color.app(name) : 'app'}`;
|
|
@@ -157,6 +157,7 @@ ${color.command('heroku apps:create --region eu')}`];
|
|
|
157
157
|
};
|
|
158
158
|
static hiddenAliases = ['create'];
|
|
159
159
|
async readManifest() {
|
|
160
|
+
const { parse } = await lazyModuleLoader.loadYaml();
|
|
160
161
|
const buffer = await fs.readFile('heroku.yml');
|
|
161
162
|
return parse(buffer.toString());
|
|
162
163
|
}
|
|
@@ -2,7 +2,7 @@ import { Command, flags } from '@heroku-cli/command';
|
|
|
2
2
|
import { SpaceCompletion } from '@heroku-cli/command/lib/completions.js';
|
|
3
3
|
import { color, hux } from '@heroku/heroku-cli-util';
|
|
4
4
|
import { ux } from '@oclif/core/ux';
|
|
5
|
-
import
|
|
5
|
+
import { lazyModuleLoader } from '../../lib/lazy-module-loader.js';
|
|
6
6
|
export default class AppsIndex extends Command {
|
|
7
7
|
static description = 'list your apps';
|
|
8
8
|
static examples = [
|
|
@@ -23,6 +23,7 @@ export default class AppsIndex extends Command {
|
|
|
23
23
|
static hiddenAliases = ['list', 'apps:list'];
|
|
24
24
|
static topic = 'apps';
|
|
25
25
|
async run() {
|
|
26
|
+
const _ = await lazyModuleLoader.loadLodash();
|
|
26
27
|
const { flags } = await this.parse(AppsIndex);
|
|
27
28
|
const teamIdentifier = flags.team;
|
|
28
29
|
let team = (!flags.personal && teamIdentifier) ? teamIdentifier : null;
|
|
@@ -54,7 +55,7 @@ export default class AppsIndex extends Command {
|
|
|
54
55
|
hux.styledJSON(apps);
|
|
55
56
|
}
|
|
56
57
|
else {
|
|
57
|
-
print(apps, user, space, team);
|
|
58
|
+
print(apps, user, space, team, _);
|
|
58
59
|
}
|
|
59
60
|
}
|
|
60
61
|
}
|
|
@@ -74,7 +75,7 @@ function annotateAppName(app) {
|
|
|
74
75
|
function listApps(apps) {
|
|
75
76
|
apps.forEach((app) => ux.stdout(regionizeAppName(app)));
|
|
76
77
|
}
|
|
77
|
-
function print(apps, user, space, team) {
|
|
78
|
+
function print(apps, user, space, team, _) {
|
|
78
79
|
if (apps.length === 0) {
|
|
79
80
|
if (space)
|
|
80
81
|
ux.stdout(`There are no apps in space ${color.space(space)}.`);
|
|
@@ -1,88 +1,10 @@
|
|
|
1
|
-
import { color, hux } from '@heroku/heroku-cli-util';
|
|
2
1
|
import { Command, flags } from '@heroku-cli/command';
|
|
2
|
+
import { color, hux } from '@heroku/heroku-cli-util';
|
|
3
3
|
import { Args, ux } from '@oclif/core';
|
|
4
4
|
import { filesize } from 'filesize';
|
|
5
|
-
import _ from 'lodash';
|
|
6
5
|
import * as util from 'util';
|
|
7
6
|
import { getGeneration } from '../../lib/apps/generation.js';
|
|
8
|
-
|
|
9
|
-
function formatDate(date) {
|
|
10
|
-
return date.toISOString();
|
|
11
|
-
}
|
|
12
|
-
async function getInfo(app, client, extended) {
|
|
13
|
-
const promises = [
|
|
14
|
-
client.heroku.get(`/apps/${app}/addons`),
|
|
15
|
-
client.heroku.request(`/apps/${app}`),
|
|
16
|
-
client.heroku.get(`/apps/${app}/dynos`).catch(() => ({ body: [] })),
|
|
17
|
-
client.heroku.get(`/apps/${app}/collaborators`).catch(() => ({ body: [] })),
|
|
18
|
-
client.heroku.get(`/apps/${app}/pipeline-couplings`).catch(() => ({ body: null })),
|
|
19
|
-
];
|
|
20
|
-
if (extended) {
|
|
21
|
-
promises.push(client.heroku.get(`/apps/${app}?extended=true`));
|
|
22
|
-
}
|
|
23
|
-
const [{ body: addons }, { body: appWithMoreInfo }, { body: dynos }, { body: collaborators }, { body: pipelineCouplings }, appExtendedResponse,] = await Promise.all(promises);
|
|
24
|
-
const data = {
|
|
25
|
-
addons,
|
|
26
|
-
app: appWithMoreInfo,
|
|
27
|
-
collaborators,
|
|
28
|
-
dynos,
|
|
29
|
-
pipeline_coupling: pipelineCouplings,
|
|
30
|
-
};
|
|
31
|
-
if (appExtendedResponse) {
|
|
32
|
-
data.appExtended = appExtendedResponse.body;
|
|
33
|
-
}
|
|
34
|
-
if (extended) {
|
|
35
|
-
data.appExtended.acm = data.app.acm;
|
|
36
|
-
data.app = data.appExtended;
|
|
37
|
-
delete data.appExtended;
|
|
38
|
-
}
|
|
39
|
-
return data;
|
|
40
|
-
}
|
|
41
|
-
function print(info, addons, collaborators, extended) {
|
|
42
|
-
const data = {};
|
|
43
|
-
data.Addons = addons;
|
|
44
|
-
data.Collaborators = collaborators;
|
|
45
|
-
if (info.app.archived_at)
|
|
46
|
-
data['Archived At'] = formatDate(new Date(info.app.archived_at));
|
|
47
|
-
if (info.app.cron_finished_at)
|
|
48
|
-
data['Cron Finished At'] = formatDate(new Date(info.app.cron_finished_at));
|
|
49
|
-
if (info.app.cron_next_run)
|
|
50
|
-
data['Cron Next Run'] = formatDate(new Date(info.app.cron_next_run));
|
|
51
|
-
if (info.app.database_size)
|
|
52
|
-
data['Database Size'] = filesize(info.app.database_size, { round: 0, standard: 'jedec' });
|
|
53
|
-
if (info.app.create_status !== 'complete')
|
|
54
|
-
data['Create Status'] = info.app.create_status;
|
|
55
|
-
if (info.app.space)
|
|
56
|
-
data.Space = color.space(info.app.space.name);
|
|
57
|
-
if (info.app.space && info.app.internal_routing)
|
|
58
|
-
data['Internal Routing'] = info.app.internal_routing;
|
|
59
|
-
if (info.pipeline_coupling)
|
|
60
|
-
data.Pipeline = `${color.pipeline(info.pipeline_coupling.pipeline.name)} - ${info.pipeline_coupling.stage}`;
|
|
61
|
-
data['Auto Cert Mgmt'] = info.app.acm;
|
|
62
|
-
data['Git URL'] = info.app.git_url;
|
|
63
|
-
data['Web URL'] = color.info(info.app.web_url);
|
|
64
|
-
data['Repo Size'] = filesize(info.app.repo_size, { round: 0, standard: 'jedec' });
|
|
65
|
-
if (getGeneration(info.app) !== 'fir')
|
|
66
|
-
data['Slug Size'] = filesize(info.app.slug_size, { round: 0, standard: 'jedec' });
|
|
67
|
-
data.Owner = color.user(info.app.owner.email);
|
|
68
|
-
data.Region = info.app.region.name;
|
|
69
|
-
data.Dynos = countBy(info.dynos, 'type');
|
|
70
|
-
data.Stack = (function (app) {
|
|
71
|
-
let stack = info.app.stack.name;
|
|
72
|
-
if (app.stack.name !== app.build_stack.name) {
|
|
73
|
-
stack += ` (next build will use ${app.build_stack.name})`;
|
|
74
|
-
}
|
|
75
|
-
return stack;
|
|
76
|
-
})(info.app);
|
|
77
|
-
hux.styledHeader(color.app(info.app.name));
|
|
78
|
-
hux.styledObject(data);
|
|
79
|
-
if (extended) {
|
|
80
|
-
ux.stdout('\n\n--- Extended Information ---\n\n');
|
|
81
|
-
if (info.app.extended) {
|
|
82
|
-
ux.stdout(util.inspect(info.app.extended));
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
7
|
+
import { lazyModuleLoader } from '../../lib/lazy-module-loader.js';
|
|
86
8
|
export default class AppsInfo extends Command {
|
|
87
9
|
static args = {
|
|
88
10
|
app: Args.string({ hidden: true }),
|
|
@@ -112,6 +34,7 @@ repo_size=5000000
|
|
|
112
34
|
static hiddenAliases = ['info'];
|
|
113
35
|
static topic = 'apps';
|
|
114
36
|
async run() {
|
|
37
|
+
const _ = await lazyModuleLoader.loadLodash();
|
|
115
38
|
const { args, flags } = await this.parse(AppsInfo);
|
|
116
39
|
const app = args.app || flags.app;
|
|
117
40
|
if (!app)
|
|
@@ -123,7 +46,7 @@ repo_size=5000000
|
|
|
123
46
|
.sort();
|
|
124
47
|
function shell() {
|
|
125
48
|
function print(k, v) {
|
|
126
|
-
ux.stdout(`${snakeCase(k)}=${v}`);
|
|
49
|
+
ux.stdout(`${_.snakeCase(k)}=${v}`);
|
|
127
50
|
}
|
|
128
51
|
print('auto_cert_mgmt', info.app.acm);
|
|
129
52
|
print('addons', addons);
|
|
@@ -147,7 +70,7 @@ repo_size=5000000
|
|
|
147
70
|
print('slug_size', filesize(info.app.slug_size, { round: 0, standard: 'jedec' }));
|
|
148
71
|
print('owner', info.app.owner.email);
|
|
149
72
|
print('region', info.app.region.name);
|
|
150
|
-
print('dynos', util.inspect(countBy(info.dynos, 'type')));
|
|
73
|
+
print('dynos', util.inspect(_.countBy(info.dynos, 'type')));
|
|
151
74
|
print('stack', info.app.stack.name);
|
|
152
75
|
}
|
|
153
76
|
if (flags.shell) {
|
|
@@ -157,7 +80,84 @@ repo_size=5000000
|
|
|
157
80
|
hux.styledJSON(info);
|
|
158
81
|
}
|
|
159
82
|
else {
|
|
160
|
-
print(info, addons, collaborators, flags.extended);
|
|
83
|
+
print(info, addons, collaborators, flags.extended, _);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function formatDate(date) {
|
|
88
|
+
return date.toISOString();
|
|
89
|
+
}
|
|
90
|
+
async function getInfo(app, client, extended) {
|
|
91
|
+
const promises = [
|
|
92
|
+
client.heroku.get(`/apps/${app}/addons`),
|
|
93
|
+
client.heroku.request(`/apps/${app}`),
|
|
94
|
+
client.heroku.get(`/apps/${app}/dynos`).catch(() => ({ body: [] })),
|
|
95
|
+
client.heroku.get(`/apps/${app}/collaborators`).catch(() => ({ body: [] })),
|
|
96
|
+
client.heroku.get(`/apps/${app}/pipeline-couplings`).catch(() => ({ body: null })),
|
|
97
|
+
];
|
|
98
|
+
if (extended) {
|
|
99
|
+
promises.push(client.heroku.get(`/apps/${app}?extended=true`));
|
|
100
|
+
}
|
|
101
|
+
const [{ body: addons }, { body: appWithMoreInfo }, { body: dynos }, { body: collaborators }, { body: pipelineCouplings }, appExtendedResponse,] = await Promise.all(promises);
|
|
102
|
+
const data = {
|
|
103
|
+
addons,
|
|
104
|
+
app: appWithMoreInfo,
|
|
105
|
+
collaborators,
|
|
106
|
+
dynos,
|
|
107
|
+
pipeline_coupling: pipelineCouplings,
|
|
108
|
+
};
|
|
109
|
+
if (appExtendedResponse) {
|
|
110
|
+
data.appExtended = appExtendedResponse.body;
|
|
111
|
+
}
|
|
112
|
+
if (extended) {
|
|
113
|
+
data.appExtended.acm = data.app.acm;
|
|
114
|
+
data.app = data.appExtended;
|
|
115
|
+
delete data.appExtended;
|
|
116
|
+
}
|
|
117
|
+
return data;
|
|
118
|
+
}
|
|
119
|
+
function print(info, addons, collaborators, extended, _) {
|
|
120
|
+
const data = {};
|
|
121
|
+
data.Addons = addons;
|
|
122
|
+
data.Collaborators = collaborators;
|
|
123
|
+
if (info.app.archived_at)
|
|
124
|
+
data['Archived At'] = formatDate(new Date(info.app.archived_at));
|
|
125
|
+
if (info.app.cron_finished_at)
|
|
126
|
+
data['Cron Finished At'] = formatDate(new Date(info.app.cron_finished_at));
|
|
127
|
+
if (info.app.cron_next_run)
|
|
128
|
+
data['Cron Next Run'] = formatDate(new Date(info.app.cron_next_run));
|
|
129
|
+
if (info.app.database_size)
|
|
130
|
+
data['Database Size'] = filesize(info.app.database_size, { round: 0, standard: 'jedec' });
|
|
131
|
+
if (info.app.create_status !== 'complete')
|
|
132
|
+
data['Create Status'] = info.app.create_status;
|
|
133
|
+
if (info.app.space)
|
|
134
|
+
data.Space = color.space(info.app.space.name);
|
|
135
|
+
if (info.app.space && info.app.internal_routing)
|
|
136
|
+
data['Internal Routing'] = info.app.internal_routing;
|
|
137
|
+
if (info.pipeline_coupling)
|
|
138
|
+
data.Pipeline = `${color.pipeline(info.pipeline_coupling.pipeline.name)} - ${info.pipeline_coupling.stage}`;
|
|
139
|
+
data['Auto Cert Mgmt'] = info.app.acm;
|
|
140
|
+
data['Git URL'] = info.app.git_url;
|
|
141
|
+
data['Web URL'] = color.info(info.app.web_url);
|
|
142
|
+
data['Repo Size'] = filesize(info.app.repo_size, { round: 0, standard: 'jedec' });
|
|
143
|
+
if (getGeneration(info.app) !== 'fir')
|
|
144
|
+
data['Slug Size'] = filesize(info.app.slug_size, { round: 0, standard: 'jedec' });
|
|
145
|
+
data.Owner = color.user(info.app.owner.email);
|
|
146
|
+
data.Region = info.app.region.name;
|
|
147
|
+
data.Dynos = _.countBy(info.dynos, 'type');
|
|
148
|
+
data.Stack = (function (app) {
|
|
149
|
+
let stack = info.app.stack.name;
|
|
150
|
+
if (app.stack.name !== app.build_stack.name) {
|
|
151
|
+
stack += ` (next build will use ${app.build_stack.name})`;
|
|
152
|
+
}
|
|
153
|
+
return stack;
|
|
154
|
+
})(info.app);
|
|
155
|
+
hux.styledHeader(color.app(info.app.name));
|
|
156
|
+
hux.styledObject(data);
|
|
157
|
+
if (extended) {
|
|
158
|
+
ux.stdout('\n\n--- Extended Information ---\n\n');
|
|
159
|
+
if (info.app.extended) {
|
|
160
|
+
ux.stdout(util.inspect(info.app.extended));
|
|
161
161
|
}
|
|
162
162
|
}
|
|
163
163
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Command } from '@heroku-cli/command';
|
|
2
2
|
import * as Heroku from '@heroku-cli/schema';
|
|
3
|
-
import inquirer from 'inquirer';
|
|
4
3
|
export default class AppsTransfer extends Command {
|
|
5
4
|
static args: {
|
|
6
5
|
recipient: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
@@ -15,8 +14,6 @@ export default class AppsTransfer extends Command {
|
|
|
15
14
|
remote: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
15
|
};
|
|
17
16
|
static topic: string;
|
|
18
|
-
getAppsToTransfer(apps: Heroku.App[]):
|
|
19
|
-
ui: inquirer.ui.Prompt<any>;
|
|
20
|
-
};
|
|
17
|
+
getAppsToTransfer(apps: Heroku.App[], inquirer: any): any;
|
|
21
18
|
run(): Promise<void>;
|
|
22
19
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Command, flags } from '@heroku-cli/command';
|
|
2
2
|
import * as color from '@heroku/heroku-cli-util/color';
|
|
3
3
|
import { Args, ux } from '@oclif/core';
|
|
4
|
-
import inquirer from 'inquirer';
|
|
5
4
|
import tsheredoc from 'tsheredoc';
|
|
6
5
|
import { appTransfer } from '../../lib/apps/app-transfer.js';
|
|
6
|
+
import { lazyModuleLoader } from '../../lib/lazy-module-loader.js';
|
|
7
7
|
import ConfirmCommand from '../../lib/confirmCommand.js';
|
|
8
8
|
import { getOwner, isTeamApp, isValidEmail } from '../../lib/teamUtils.js';
|
|
9
9
|
import AppsLock from './lock.js';
|
|
@@ -28,7 +28,7 @@ export default class AppsTransfer extends Command {
|
|
|
28
28
|
remote: flags.remote({ char: 'r' }),
|
|
29
29
|
};
|
|
30
30
|
static topic = 'apps';
|
|
31
|
-
getAppsToTransfer(apps) {
|
|
31
|
+
getAppsToTransfer(apps, inquirer) {
|
|
32
32
|
return inquirer.prompt([{
|
|
33
33
|
choices: apps.map(app => ({
|
|
34
34
|
name: `${color.app(app.name ?? '')} (${getOwner(app.owner?.email ?? '')})`, value: { name: app.name, owner: app.owner?.email },
|
|
@@ -40,12 +40,13 @@ export default class AppsTransfer extends Command {
|
|
|
40
40
|
}]);
|
|
41
41
|
}
|
|
42
42
|
async run() {
|
|
43
|
+
const inquirer = await lazyModuleLoader.loadInquirer();
|
|
43
44
|
const { args, flags } = await this.parse(AppsTransfer);
|
|
44
45
|
const { app, bulk, confirm, locked } = flags;
|
|
45
46
|
const { recipient } = args;
|
|
46
47
|
if (bulk) {
|
|
47
48
|
const { body: allApps } = await this.heroku.get('/apps');
|
|
48
|
-
const selectedApps = await this.getAppsToTransfer(allApps.sort((a, b) => (a.name ?? '').localeCompare(b.name ?? '')));
|
|
49
|
+
const selectedApps = await this.getAppsToTransfer(allApps.sort((a, b) => (a.name ?? '').localeCompare(b.name ?? '')), inquirer);
|
|
49
50
|
ux.warn(`Transferring applications to ${color.name(recipient)}...\n`);
|
|
50
51
|
for (const app of selectedApps.choices) {
|
|
51
52
|
try {
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { Command } from '@heroku-cli/command';
|
|
2
2
|
import * as color from '@heroku/heroku-cli-util/color';
|
|
3
3
|
import { ux } from '@oclif/core/ux';
|
|
4
|
-
import {
|
|
4
|
+
import { lazyModuleLoader } from '../../lib/lazy-module-loader.js';
|
|
5
5
|
export default class AuthToken extends Command {
|
|
6
6
|
static baseFlags = Command.baseFlagsWithoutPrompt();
|
|
7
7
|
static description = `outputs current CLI authentication token.
|
|
8
8
|
By default, the CLI auth token is only valid for 1 year. To generate a long-lived token, use heroku authorizations:create`;
|
|
9
9
|
static promptFlagActive = false;
|
|
10
10
|
async run() {
|
|
11
|
+
const { formatRelative } = await lazyModuleLoader.loadDateFns();
|
|
11
12
|
this.parse(AuthToken);
|
|
12
13
|
if (!this.heroku.auth)
|
|
13
14
|
this.error('not logged in');
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { APIClient, Command } from '@heroku-cli/command';
|
|
2
|
-
import inquirer from 'inquirer';
|
|
3
2
|
import { SniEndpoint } from '../../lib/types/sni_endpoint.js';
|
|
4
3
|
export default class Add extends Command {
|
|
5
4
|
static args: {
|
|
@@ -14,16 +13,8 @@ export default class Add extends Command {
|
|
|
14
13
|
};
|
|
15
14
|
static strict: boolean;
|
|
16
15
|
static topic: string;
|
|
17
|
-
configureDomains(app: string, heroku: APIClient, cert: SniEndpoint): Promise<void>;
|
|
18
|
-
getDomainsToAssociate(sniEndpoint: SniEndpoint):
|
|
19
|
-
domains: string[];
|
|
20
|
-
}> & {
|
|
21
|
-
ui: inquirer.ui.Prompt<{
|
|
22
|
-
domains: string[];
|
|
23
|
-
}>;
|
|
24
|
-
};
|
|
16
|
+
configureDomains(app: string, heroku: APIClient, cert: SniEndpoint, inquirer: any): Promise<void>;
|
|
17
|
+
getDomainsToAssociate(sniEndpoint: SniEndpoint, inquirer: any): any;
|
|
25
18
|
run(): Promise<void>;
|
|
26
|
-
selectDomains(domainOptions: string[]): Promise<
|
|
27
|
-
domains: string[];
|
|
28
|
-
}>;
|
|
19
|
+
selectDomains(domainOptions: string[], inquirer: any): Promise<any>;
|
|
29
20
|
}
|