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.
Files changed (115) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/bin/run.js +3 -6
  3. package/dist/commands/access/index.js +9 -8
  4. package/dist/commands/addons/index.d.ts +1 -1
  5. package/dist/commands/addons/index.js +59 -59
  6. package/dist/commands/apps/create.js +2 -1
  7. package/dist/commands/apps/index.js +4 -3
  8. package/dist/commands/apps/info.js +83 -83
  9. package/dist/commands/apps/transfer.d.ts +1 -4
  10. package/dist/commands/apps/transfer.js +4 -3
  11. package/dist/commands/auth/token.js +2 -1
  12. package/dist/commands/certs/add.d.ts +3 -12
  13. package/dist/commands/certs/add.js +8 -7
  14. package/dist/commands/certs/auto/index.js +2 -1
  15. package/dist/commands/certs/generate.d.ts +1 -1
  16. package/dist/commands/certs/generate.js +4 -3
  17. package/dist/commands/config/edit.d.ts +1 -1
  18. package/dist/commands/config/edit.js +52 -51
  19. package/dist/commands/dashboard.js +31 -30
  20. package/dist/commands/data/maintenances/info.d.ts +1 -1
  21. package/dist/commands/data/maintenances/info.js +4 -3
  22. package/dist/commands/data/maintenances/run.js +2 -2
  23. package/dist/commands/data/maintenances/schedule.d.ts +1 -1
  24. package/dist/commands/data/maintenances/schedule.js +4 -3
  25. package/dist/commands/data/pg/create.js +9 -9
  26. package/dist/commands/data/pg/credentials/create.js +2 -2
  27. package/dist/commands/data/pg/fork.d.ts +1 -1
  28. package/dist/commands/data/pg/fork.js +4 -3
  29. package/dist/commands/data/pg/info.js +40 -40
  30. package/dist/commands/data/pg/settings.js +3 -3
  31. package/dist/commands/data/pg/update.js +8 -8
  32. package/dist/commands/domains/add.d.ts +2 -2
  33. package/dist/commands/domains/add.js +6 -5
  34. package/dist/commands/domains/index.d.ts +1 -1
  35. package/dist/commands/domains/index.js +10 -10
  36. package/dist/commands/keys/add.js +13 -12
  37. package/dist/commands/local/index.d.ts +2 -0
  38. package/dist/commands/local/index.js +46 -11
  39. package/dist/commands/local/run.js +10 -2
  40. package/dist/commands/local/version.js +10 -2
  41. package/dist/commands/notifications/index.js +14 -14
  42. package/dist/commands/pg/backups/download.js +12 -12
  43. package/dist/commands/pg/backups/restore.js +15 -15
  44. package/dist/commands/pg/backups/schedule.d.ts +6 -6
  45. package/dist/commands/pg/backups/schedule.js +18 -18
  46. package/dist/commands/pg/backups/url.js +2 -2
  47. package/dist/commands/pg/credentials/create.js +5 -5
  48. package/dist/commands/pg/credentials/destroy.js +2 -2
  49. package/dist/commands/pg/credentials/rotate.js +2 -2
  50. package/dist/commands/pipelines/add.js +2 -1
  51. package/dist/commands/pipelines/create.js +2 -1
  52. package/dist/commands/ps/copy.js +3 -3
  53. package/dist/commands/ps/exec.js +2 -2
  54. package/dist/commands/ps/forward.js +1 -1
  55. package/dist/commands/ps/index.js +115 -115
  56. package/dist/commands/ps/scale.js +2 -1
  57. package/dist/commands/ps/type.js +6 -5
  58. package/dist/commands/releases/index.js +2 -1
  59. package/dist/commands/releases/rollback.js +2 -2
  60. package/dist/commands/spaces/destroy.js +7 -7
  61. package/dist/commands/spaces/drains/set.js +2 -2
  62. package/dist/commands/spaces/peerings/accept.js +1 -1
  63. package/dist/commands/spaces/ps.js +2 -2
  64. package/dist/commands/spaces/trusted-ips/add.js +2 -2
  65. package/dist/commands/spaces/trusted-ips/remove.js +1 -1
  66. package/dist/commands/status.js +2 -1
  67. package/dist/commands/version/info.js +2 -6
  68. package/dist/hooks/command_not_found/{performance_analytics.js → setup-otel-telemetry.js} +3 -2
  69. package/dist/hooks/init/{performance_analytics.js → setup-otel-telemetry.js} +3 -2
  70. package/dist/hooks/init/terms-of-service.js +1 -1
  71. package/dist/hooks/init/version.js +9 -6
  72. package/dist/hooks/postrun/{performance_analytics.js → send-otel-telemetry.js} +3 -2
  73. package/dist/hooks/prerun/collect-and-send-herokulytics.js +34 -0
  74. package/dist/{analytics.d.ts → lib/analytics-telemetry/backboard-herokulytics-client.d.ts} +20 -19
  75. package/dist/{analytics.js → lib/analytics-telemetry/backboard-herokulytics-client.js} +72 -54
  76. package/dist/lib/analytics-telemetry/backboard-otel-client.d.ts +16 -0
  77. package/dist/lib/analytics-telemetry/backboard-otel-client.js +121 -0
  78. package/dist/{user-config.d.ts → lib/analytics-telemetry/herokulytics-config.d.ts} +9 -9
  79. package/dist/{user-config.js → lib/analytics-telemetry/herokulytics-config.js} +40 -40
  80. package/dist/lib/analytics-telemetry/sentry-client.d.ts +14 -8
  81. package/dist/lib/analytics-telemetry/sentry-client.js +51 -50
  82. package/dist/lib/analytics-telemetry/telemetry-manager.d.ts +43 -0
  83. package/dist/lib/analytics-telemetry/telemetry-manager.js +133 -0
  84. package/dist/lib/analytics-telemetry/telemetry-utils.d.ts +18 -4
  85. package/dist/lib/analytics-telemetry/telemetry-utils.js +13 -9
  86. package/dist/lib/analytics-telemetry/telemetry-worker.js +48 -8
  87. package/dist/lib/analytics-telemetry/worker-client.d.ts +3 -2
  88. package/dist/lib/analytics-telemetry/worker-client.js +5 -16
  89. package/dist/lib/data/poolConfig.js +2 -2
  90. package/dist/lib/data/utils.d.ts +1 -1
  91. package/dist/lib/data/utils.js +1 -1
  92. package/dist/lib/lazy-module-loader.d.ts +58 -0
  93. package/dist/lib/lazy-module-loader.js +100 -0
  94. package/dist/lib/local/fork-foreman.d.ts +5 -0
  95. package/dist/lib/local/fork-foreman.js +18 -5
  96. package/dist/lib/local/load-foreman-procfile.js +3 -0
  97. package/dist/lib/ps-exec/exec.js +2 -2
  98. package/dist/lib/releases/status_helper.d.ts +1 -1
  99. package/dist/lib/run/colorize.js +10 -10
  100. package/npm-shrinkwrap.json +101 -14
  101. package/oclif.manifest.json +253 -246
  102. package/package.json +8 -8
  103. package/dist/deps.d.ts +0 -9
  104. package/dist/deps.js +0 -14
  105. package/dist/hooks/prerun/analytics.js +0 -23
  106. package/dist/lib/analytics-telemetry/global-telemetry.d.ts +0 -30
  107. package/dist/lib/analytics-telemetry/global-telemetry.js +0 -103
  108. package/dist/lib/analytics-telemetry/honeycomb-client.d.ts +0 -15
  109. package/dist/lib/analytics-telemetry/honeycomb-client.js +0 -135
  110. /package/dist/hooks/command_not_found/{performance_analytics.d.ts → setup-otel-telemetry.d.ts} +0 -0
  111. /package/dist/hooks/finally/{sentry.d.ts → send-otel-and-sentry-errors.d.ts} +0 -0
  112. /package/dist/hooks/finally/{sentry.js → send-otel-and-sentry-errors.js} +0 -0
  113. /package/dist/hooks/init/{performance_analytics.d.ts → setup-otel-telemetry.d.ts} +0 -0
  114. /package/dist/hooks/postrun/{performance_analytics.d.ts → send-otel-telemetry.d.ts} +0 -0
  115. /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
- // Skip telemetry entirely on Windows for performance (unless explicitly enabled)
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 (enableTelemetry) {
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 _ from 'lodash';
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.dim('?');
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.dim('?');
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.dim('?');
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.dim('?');
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.dim('?'),
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.dim(`(billed to ${color.app(addon.app?.name || '')} app)`);
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.dim(`(billed to ${color.app(addon.app?.name || '')} app)`);
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
- export default class Addons extends Command {
203
- static description = `Lists your add-ons and attachments.
204
-
205
- The default filter applied depends on whether you are in a Heroku app
206
- directory. If so, the --app flag is implied. If not, the default of --all
207
- is implied. Explicitly providing either flag overrides the default
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 _ from 'lodash';
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
- const { countBy, snakeCase } = _;
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[]): Promise<any> & {
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 { formatRelative } from 'date-fns';
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): Promise<{
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
  }