heroku 10.1.0-beta.0 → 10.1.0-beta.2

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.
@@ -25,6 +25,7 @@ export default class AnalyticsCommand {
25
25
  config: Interfaces.Config;
26
26
  userConfig: typeof deps.UserConfig.prototype;
27
27
  http: typeof deps.HTTP;
28
+ initialize: Promise<void>;
28
29
  constructor(config: Interfaces.Config);
29
30
  record(opts: RecordOpts): Promise<any>;
30
31
  get url(): string;
package/lib/analytics.js CHANGED
@@ -11,9 +11,10 @@ class AnalyticsCommand {
11
11
  this.http = deps_1.default.HTTP.create({
12
12
  headers: { 'user-agent': config.userAgent },
13
13
  });
14
+ this.initialize = this.init();
14
15
  }
15
16
  async record(opts) {
16
- await this.init();
17
+ await this.initialize;
17
18
  const plugin = opts.Command.plugin;
18
19
  if (!plugin) {
19
20
  debug('no plugin found for analytics');
@@ -53,7 +53,7 @@ class Open extends command_1.Command {
53
53
  const sso = await this.heroku.request(`/apps/${app}/addons/${args.addon}/sso`, {
54
54
  method: 'GET',
55
55
  headers: {
56
- Accept: 'application/vnd.heroku+json; version=3.add-ons-sso',
56
+ Accept: 'application/vnd.heroku+json; version=3.sdk',
57
57
  },
58
58
  });
59
59
  const { method, action } = sso.body;
@@ -5,6 +5,7 @@ const command_1 = require("@heroku-cli/command");
5
5
  const util = require("util");
6
6
  const _ = require("lodash");
7
7
  const filesize_1 = require("filesize");
8
+ const generation_1 = require("../../lib/apps/generation");
8
9
  const { countBy, snakeCase } = _;
9
10
  function formatDate(date) {
10
11
  return date.toISOString();
@@ -62,7 +63,7 @@ function print(info, addons, collaborators, extended) {
62
63
  data['Git URL'] = info.app.git_url;
63
64
  data['Web URL'] = info.app.web_url;
64
65
  data['Repo Size'] = (0, filesize_1.filesize)(info.app.repo_size, { standard: 'jedec', round: 0 });
65
- if (info.app.generation.name !== 'fir')
66
+ if ((0, generation_1.getGeneration)(info.app) !== 'fir')
66
67
  data['Slug Size'] = (0, filesize_1.filesize)(info.app.slug_size, { standard: 'jedec', round: 0 });
67
68
  data.Owner = info.app.owner.email;
68
69
  data.Region = info.app.region.name;
@@ -116,7 +117,7 @@ class AppsInfo extends command_1.Command {
116
117
  print('git_url', info.app.git_url);
117
118
  print('web_url', info.app.web_url);
118
119
  print('repo_size', (0, filesize_1.filesize)(info.app.repo_size, { standard: 'jedec', round: 0 }));
119
- if (info.app.generation.name !== 'fir')
120
+ if ((0, generation_1.getGeneration)(info.app) !== 'fir')
120
121
  print('slug_size', (0, filesize_1.filesize)(info.app.slug_size, { standard: 'jedec', round: 0 }));
121
122
  print('owner', info.app.owner.email);
122
123
  print('region', info.app.region.name);
@@ -4,6 +4,7 @@ const command_1 = require("@heroku-cli/command");
4
4
  const core_1 = require("@oclif/core");
5
5
  const color_1 = require("@heroku-cli/color");
6
6
  const buildpacks_1 = require("../../lib/buildpacks/buildpacks");
7
+ const generation_1 = require("../../lib/apps/generation");
7
8
  class Index extends command_1.Command {
8
9
  async run() {
9
10
  const { flags } = await this.parse(Index);
@@ -13,7 +14,7 @@ class Index extends command_1.Command {
13
14
  Accept: 'application/vnd.heroku+json; version=3.sdk',
14
15
  },
15
16
  });
16
- const buildpacks = await buildpacksCommand.fetch(flags.app, app.generation === 'fir');
17
+ const buildpacks = await buildpacksCommand.fetch(flags.app, (0, generation_1.getGeneration)(app) === 'fir');
17
18
  if (buildpacks.length === 0) {
18
19
  this.log(`${color_1.default.app(flags.app)} has no Buildpacks.`);
19
20
  }
@@ -34,8 +34,12 @@ class Add extends command_1.Command {
34
34
  await sshKeygen(path.join(sshdir, 'id_rsa'), flags.quiet);
35
35
  };
36
36
  const findKey = async function () {
37
- const defaultKey = path.join(sshdir, 'id_rsa.pub');
38
- if (!(await fs.pathExists(defaultKey))) {
37
+ const defaultKeyPath = path.join(sshdir, 'id_rsa.pub');
38
+ const defaultKeyExists = await fs.pathExists(defaultKeyPath);
39
+ const keys = (await fs.readdir(sshdir))
40
+ .map(k => path.join(sshdir, k))
41
+ .filter(k => path.extname(k) === '.pub');
42
+ if (!defaultKeyExists && keys.length === 0) {
39
43
  core_1.ux.warn('Could not find an existing SSH key at ' + path.join('~', '.ssh', 'id_rsa.pub'));
40
44
  if (!flags.yes) {
41
45
  const resp = await confirmPrompt('Would you like to generate a new one?');
@@ -43,14 +47,11 @@ class Add extends command_1.Command {
43
47
  return;
44
48
  }
45
49
  await generate();
46
- return defaultKey;
50
+ return defaultKeyPath;
47
51
  }
48
- let keys = await fs.readdir(sshdir);
49
- keys = keys.map(k => path.join(sshdir, k));
50
- keys = keys.filter(k => path.extname(k) === '.pub');
51
52
  if (keys.length === 1) {
52
53
  const key = keys[0];
53
- core_1.ux.warn(`Found an SSH public key at ${color_1.default.cyan(key)}`);
54
+ core_1.ux.info(`Found an SSH public key at ${color_1.default.cyan(key)}`);
54
55
  if (!flags.yes) {
55
56
  const resp = await confirmPrompt('Would you like to upload it to Heroku?');
56
57
  if (!resp.yes)
@@ -10,7 +10,7 @@ class LogMinDurationStatement extends setter_1.PGSettingsCommand {
10
10
  this.settingKey = 'log_min_duration_statement';
11
11
  }
12
12
  convertValue(val) {
13
- return val;
13
+ return Number.parseInt(val, 10);
14
14
  }
15
15
  explain(setting) {
16
16
  if (setting.value === -1) {
@@ -24,6 +24,7 @@ class Wait extends command_1.Command {
24
24
  let status;
25
25
  let waiting = false;
26
26
  let retries = 20;
27
+ const notFoundMessage = 'Waiting to provision...';
27
28
  while (true) {
28
29
  try {
29
30
  ({ body: status } = await this.heroku.get(`/client/v11/databases/${db.id}/wait_status`, { hostname: (0, host_1.default)() }));
@@ -34,7 +35,7 @@ class Wait extends command_1.Command {
34
35
  if (!retries || httpError.statusCode !== 404)
35
36
  throw httpError;
36
37
  retries--;
37
- status = { 'waiting?': true };
38
+ status = { 'waiting?': true, message: notFoundMessage };
38
39
  }
39
40
  if (status['error?']) {
40
41
  (0, notify_1.default)('error', `${db.name} ${status.message}`, false);
@@ -1,5 +1,6 @@
1
1
  import { Command } from '@heroku-cli/command';
2
2
  import KolkrabbiAPI from '../../lib/pipelines/kolkrabbi-api';
3
+ import { GenerationKind } from '../../lib/apps/generation';
3
4
  interface AppInfo {
4
5
  name: string;
5
6
  repo?: string;
@@ -13,7 +14,7 @@ export default class PipelinesDiff extends Command {
13
14
  remote: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
14
15
  };
15
16
  kolkrabbi: KolkrabbiAPI;
16
- getAppInfo: (appName: string, appId: string, generation: string) => Promise<AppInfo>;
17
+ getAppInfo: (appName: string, appId: string, generation: GenerationKind) => Promise<AppInfo>;
17
18
  run(): Promise<undefined>;
18
19
  }
19
20
  export {};
@@ -6,6 +6,7 @@ const core_1 = require("@oclif/core");
6
6
  const http_call_1 = require("@heroku/http-call");
7
7
  const api_1 = require("../../lib/api");
8
8
  const kolkrabbi_api_1 = require("../../lib/pipelines/kolkrabbi-api");
9
+ const generation_1 = require("../../lib/apps/generation");
9
10
  const PROMOTION_ORDER = ['development', 'staging', 'production'];
10
11
  async function diff(targetApp, downstreamApp, githubToken, herokuUserAgent) {
11
12
  if (!downstreamApp.repo) {
@@ -23,13 +24,15 @@ async function diff(targetApp, downstreamApp, githubToken, herokuUserAgent) {
23
24
  // Do the actual GitHub diff
24
25
  try {
25
26
  const path = `${targetApp.repo}/compare/${downstreamApp.hash}...${targetApp.hash}`;
26
- const headers = { authorization: 'token ' + githubToken };
27
+ const headers = {
28
+ authorization: 'token ' + githubToken,
29
+ 'Content-Type': 'application/vnd.github+json',
30
+ 'X-GitHub-Api-Version': '2022-11-28',
31
+ };
27
32
  if (herokuUserAgent) {
28
- headers['user-agent'] = herokuUserAgent;
33
+ Reflect.set(headers, 'user-agent', herokuUserAgent);
29
34
  }
30
- const githubDiff = await http_call_1.default.get(`https://api.github.com/repos/${path}`, {
31
- headers,
32
- }).then(res => res.body);
35
+ const { body: githubDiff } = await http_call_1.default.get(`https://api.github.com/repos/${path}`, { headers });
33
36
  core_1.ux.log('');
34
37
  core_1.ux.styledHeader(`${color_1.default.app(targetApp.name)} is ahead of ${color_1.default.app(downstreamApp.name)} by ${githubDiff.ahead_by} commit${githubDiff.ahead_by === 1 ? '' : 's'}`);
35
38
  const mapped = githubDiff.commits.map((commit) => {
@@ -49,7 +52,6 @@ async function diff(targetApp, downstreamApp, githubToken, herokuUserAgent) {
49
52
  message: {},
50
53
  });
51
54
  core_1.ux.log(`\nhttps://github.com/${path}`);
52
- // tslint:disable-next-line: no-unused
53
55
  }
54
56
  catch (_a) {
55
57
  core_1.ux.log(`\n${color_1.default.app(targetApp.name)} was not compared to ${color_1.default.app(downstreamApp.name)} because we were unable to perform a diff`);
@@ -99,15 +101,17 @@ class PipelinesDiff extends command_1.Command {
99
101
  async run() {
100
102
  const { flags } = await this.parse(PipelinesDiff);
101
103
  const targetAppName = flags.app;
102
- const coupling = await (0, api_1.getCoupling)(this.heroku, targetAppName)
103
- .then(res => res.body)
104
- .catch(() => { });
105
- if (!coupling) {
104
+ let coupling;
105
+ try {
106
+ ({ body: coupling } = await (0, api_1.getCoupling)(this.heroku, targetAppName));
107
+ }
108
+ catch (_a) {
106
109
  core_1.ux.error(`This app (${targetAppName}) does not seem to be a part of any pipeline`);
107
110
  return;
108
111
  }
112
+ const { body: pipeline } = await (0, api_1.getPipeline)(this.heroku, coupling.pipeline.id);
109
113
  const targetAppId = coupling.app.id;
110
- const generation = coupling.generation;
114
+ const generation = (0, generation_1.getGeneration)(pipeline);
111
115
  core_1.ux.action.start('Fetching apps from pipeline');
112
116
  const allApps = await (0, api_1.listPipelineApps)(this.heroku, coupling.pipeline.id);
113
117
  core_1.ux.action.stop();
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const command_1 = require("@heroku-cli/command");
4
4
  const core_1 = require("@oclif/core");
5
+ const generation_1 = require("../../../lib/apps/generation");
5
6
  const METRICS_HOST = 'api.metrics.heroku.com';
6
7
  const isPerfOrPrivateTier = (size) => {
7
8
  const applicableTiers = ['performance', 'private', 'shield'];
@@ -26,7 +27,7 @@ class Enable extends command_1.Command {
26
27
  const app = appResponse.body;
27
28
  const formations = formationResponse.body;
28
29
  const webFormation = formations.find((f) => f.type === 'web');
29
- if (app.generation === 'fir') {
30
+ if ((0, generation_1.getGeneration)(app) === 'fir') {
30
31
  throw new Error('Autoscaling is unavailable for apps in this space. See https://devcenter.heroku.com/articles/generations.');
31
32
  }
32
33
  if (!webFormation)
@@ -7,6 +7,7 @@ const tsheredoc_1 = require("tsheredoc");
7
7
  const spaces_1 = require("../../lib/spaces/spaces");
8
8
  const completions_1 = require("../../lib/autocomplete/completions");
9
9
  const parsers_1 = require("../../lib/spaces/parsers");
10
+ const generation_1 = require("../../lib/apps/generation");
10
11
  class Create extends command_1.Command {
11
12
  async run() {
12
13
  const { flags, args } = await this.parse(Create);
@@ -59,7 +60,7 @@ class Create extends command_1.Command {
59
60
  core_1.ux.warn(`Use ${color_1.default.cmd('heroku spaces:wait')} to track allocation.`);
60
61
  core_1.ux.styledHeader(space.name);
61
62
  core_1.ux.styledObject({
62
- ID: space.id, Team: space.team.name, Region: space.region.name, CIDR: space.cidr, 'Data CIDR': space.data_cidr, State: space.state, Shield: (0, spaces_1.displayShieldState)(space), Generation: space.generation, 'Created at': space.created_at,
63
+ ID: space.id, Team: space.team.name, Region: space.region.name, CIDR: space.cidr, 'Data CIDR': space.data_cidr, State: space.state, Shield: (0, spaces_1.displayShieldState)(space), Generation: (0, generation_1.getGeneration)(space), 'Created at': space.created_at,
63
64
  }, ['ID', 'Team', 'Region', 'CIDR', 'Data CIDR', 'State', 'Shield', 'Generation', 'Created at']);
64
65
  }
65
66
  }
@@ -1,13 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const core_1 = require("@oclif/core");
4
- const color_1 = require("@heroku-cli/color");
5
4
  const command_1 = require("@heroku-cli/command");
6
5
  const tsheredoc_1 = require("tsheredoc");
7
6
  const confirmCommand_1 = require("../../lib/confirmCommand");
8
7
  const spaces_1 = require("../../lib/spaces/spaces");
8
+ const color_1 = require("@heroku-cli/color");
9
+ const generation_1 = require("../../lib/apps/generation");
9
10
  class Destroy extends command_1.Command {
10
11
  async run() {
12
+ var _a;
11
13
  const { flags, args } = await this.parse(Destroy);
12
14
  const { confirm } = flags;
13
15
  const spaceName = flags.space || args.space;
@@ -22,7 +24,20 @@ class Destroy extends command_1.Command {
22
24
  if (space.state === 'allocated') {
23
25
  ({ body: space.outbound_ips } = await this.heroku.get(`/spaces/${spaceName}/nat`));
24
26
  if (space.outbound_ips && space.outbound_ips.state === 'enabled') {
25
- natWarning = `The Outbound IPs for this space will be reused!\nEnsure that external services no longer allow these Outbound IPs: ${(0, spaces_1.displayNat)(space.outbound_ips)}\n`;
27
+ const ipv6 = (0, generation_1.getGeneration)(space) === 'fir' ? ' and IPv6' : '';
28
+ natWarning = (0, tsheredoc_1.default) `
29
+ ${color_1.default.dim('===')} ${color_1.default.bold('WARNING: Outbound IPs Will Be Reused')}
30
+ ${color_1.default.yellow(`⚠️ Deleting this space frees up the following outbound IPv4${ipv6} IPs for reuse:`)}
31
+ ${color_1.default.bold((_a = (0, spaces_1.displayNat)(space.outbound_ips)) !== null && _a !== void 0 ? _a : '')}
32
+
33
+ ${color_1.default.dim('Update the following configurations:')}
34
+ ${color_1.default.dim('=')} IP allowlists
35
+ ${color_1.default.dim('=')} Firewall rules
36
+ ${color_1.default.dim('=')} Security group configurations
37
+ ${color_1.default.dim('=')} Network ACLs
38
+
39
+ ${color_1.default.yellow(`Ensure that you remove the listed IPv4${ipv6} addresses from your security configurations.`)}
40
+ `;
26
41
  }
27
42
  }
28
43
  await (0, confirmCommand_1.default)(spaceName, confirm, `Destructive Action\nThis command will destroy the space ${color_1.default.bold.red(spaceName)}\n${natWarning}\n`);
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const color_1 = require("@heroku-cli/color");
4
4
  const command_1 = require("@heroku-cli/command");
5
5
  const core_1 = require("@oclif/core");
6
+ const generation_1 = require("../../lib/apps/generation");
6
7
  class Index extends command_1.Command {
7
8
  async run() {
8
9
  const { flags } = await this.parse(Index);
@@ -43,7 +44,7 @@ class Index extends command_1.Command {
43
44
  Team: { get: space => space.team.name },
44
45
  Region: { get: space => space.region.name },
45
46
  State: { get: space => space.state },
46
- Generation: { get: space => space.generation },
47
+ Generation: { get: space => (0, generation_1.getGeneration)(space) },
47
48
  createdAt: {
48
49
  header: 'Created At',
49
50
  get: space => space.created_at,
@@ -29,7 +29,7 @@ class Add extends command_1.Command {
29
29
  signals: (0, util_1.validateAndFormatSignals)(signals),
30
30
  exporter: {
31
31
  endpoint,
32
- type: `otlp${transport}`,
32
+ type: (transport === 'grpc') ? 'otlp' : 'otlphttp',
33
33
  headers: JSON.parse(exporterHeaders),
34
34
  },
35
35
  };
@@ -44,6 +44,6 @@ Index.topic = 'telemetry';
44
44
  Index.description = 'list telemetry drains';
45
45
  Index.flags = {
46
46
  space: command_1.flags.string({ char: 's', description: 'filter by space name', exactlyOne: ['app', 'space'] }),
47
- app: command_1.flags.app({ description: 'filter by app name' }),
47
+ app: command_1.flags.string({ description: 'filter by app name' }),
48
48
  };
49
49
  Index.example = '$ heroku telemetry';
package/lib/file.d.ts CHANGED
@@ -10,5 +10,5 @@ export declare function ls(dir: string): Promise<{
10
10
  }[]>;
11
11
  export declare function removeEmptyDirs(dir: string): Promise<void>;
12
12
  export declare function readJSON(file: string): Promise<any>;
13
- export declare function outputJSON(file: string, data: any, options?: FS.WriteOptions): Promise<void>;
13
+ export declare function outputJSON(file: string, data: any): Promise<void>;
14
14
  export declare function realpathSync(p: string): string;
package/lib/file.js CHANGED
@@ -53,9 +53,9 @@ async function readJSON(file) {
53
53
  return deps_1.default.fs.readJSON(file);
54
54
  }
55
55
  exports.readJSON = readJSON;
56
- async function outputJSON(file, data, options = {}) {
56
+ async function outputJSON(file, data) {
57
57
  debug('outputJSON', file);
58
- return deps_1.default.fs.outputJSON(file, data, Object.assign({ spaces: 2 }, options));
58
+ return deps_1.default.fs.outputJSON(file, data, { spaces: 2 });
59
59
  }
60
60
  exports.outputJSON = outputJSON;
61
61
  function realpathSync(p) {
@@ -8,5 +8,6 @@ const performance_analytics = async function () {
8
8
  const cmdStartTime = global.cliTelemetry.commandRunDuration;
9
9
  global.cliTelemetry.commandRunDuration = telemetry.computeDuration(cmdStartTime);
10
10
  global.cliTelemetry.lifecycleHookCompletion.postrun = true;
11
+ await Reflect.get(globalThis, 'recordPromise');
11
12
  };
12
13
  exports.default = performance_analytics;
@@ -8,6 +8,6 @@ const analytics = async function (options) {
8
8
  }
9
9
  global.cliTelemetry = telemetry.setupTelemetry(this.config, options);
10
10
  const analytics = new analytics_1.default(this.config);
11
- await analytics.record(options);
11
+ Reflect.set(globalThis, 'recordPromise', analytics.record(options));
12
12
  };
13
13
  exports.default = analytics;
@@ -3,7 +3,7 @@ export declare const trapConfirmationRequired: <T>(app: string, confirm: string
3
3
  export declare const formatPrice: ({ price, hourly }: {
4
4
  price: Heroku.AddOn['price'] | number;
5
5
  hourly?: boolean | undefined;
6
- }) => any;
6
+ }) => string | undefined;
7
7
  export declare const formatPriceText: (price: Heroku.AddOn['price']) => string;
8
8
  export declare const grandfatheredPrice: (addon: Heroku.AddOn) => any;
9
9
  export declare const formatState: (state: string) => string;
@@ -3,17 +3,24 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.formatState = exports.grandfatheredPrice = exports.formatPriceText = exports.formatPrice = exports.trapConfirmationRequired = void 0;
4
4
  const confirmCommand_1 = require("../confirmCommand");
5
5
  const color_1 = require("@heroku-cli/color");
6
- const printf = require('printf');
7
- const trapConfirmationRequired = async function (app, confirm, fn) {
8
- return await fn(confirm)
9
- .catch((error) => {
10
- if (!error.body || error.body.id !== 'confirmation_required')
6
+ const printf = require("printf");
7
+ const trapConfirmationRequired = async (app, confirm, fn) => {
8
+ var _a;
9
+ try {
10
+ return await fn(confirm);
11
+ }
12
+ catch (error) {
13
+ if (!isHttpError(error) || ((_a = error.body) === null || _a === void 0 ? void 0 : _a.id) !== 'confirmation_required') {
11
14
  throw error;
12
- return (0, confirmCommand_1.default)(app, confirm, error.body.message)
13
- .then(() => fn(app));
14
- });
15
+ }
16
+ await (0, confirmCommand_1.default)(app, confirm, error.body.message);
17
+ return fn(app);
18
+ }
15
19
  };
16
20
  exports.trapConfirmationRequired = trapConfirmationRequired;
21
+ function isHttpError(error) {
22
+ return Boolean(error) && error instanceof Error && Reflect.has(error, 'body');
23
+ }
17
24
  // This function assumes that price.cents will reflect price per month.
18
25
  // If the API returns any unit other than month
19
26
  // this function will need to be updated.
package/lib/lib/api.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { APIClient } from '@heroku-cli/command';
2
2
  import * as Heroku from '@heroku-cli/schema';
3
- import { App, PipelineCoupling, Release } from './types/fir';
3
+ import { App, Pipeline, PipelineCoupling } from './types/fir';
4
4
  export declare const V3_HEADER = "application/vnd.heroku+json; version=3";
5
5
  export declare const SDK_HEADER = "application/vnd.heroku+json; version=3.sdk";
6
6
  export declare const FILTERS_HEADER: string;
@@ -14,24 +14,21 @@ export declare function createPipeline(heroku: APIClient, name: any, owner: any)
14
14
  export declare function createPipelineTransfer(heroku: APIClient, pipeline: Heroku.Pipeline): Promise<import("@heroku/http-call").HTTP<unknown>>;
15
15
  export declare function destroyPipeline(heroku: APIClient, name: any, pipelineId: any): Promise<import("@heroku/http-call").HTTP<unknown>>;
16
16
  export declare function findPipelineByName(heroku: APIClient, idOrName: string): Promise<import("@heroku/http-call").HTTP<Heroku.Pipeline[]>>;
17
- export interface PipelineCouplingSdk extends Required<PipelineCoupling> {
18
- generation: 'fir' | 'cedar';
19
- }
20
- export declare function getCoupling(heroku: APIClient, app: string): Promise<import("@heroku/http-call").HTTP<PipelineCouplingSdk>>;
21
- export declare function getPipeline(heroku: APIClient, id: string): Promise<import("@heroku/http-call").HTTP<Heroku.Pipeline>>;
17
+ export declare function getCoupling(heroku: APIClient, app: string): Promise<import("@heroku/http-call").HTTP<PipelineCoupling>>;
18
+ export declare function getPipeline(heroku: APIClient, id: string): Promise<import("@heroku/http-call").HTTP<Pipeline>>;
22
19
  export declare function updatePipeline(heroku: APIClient, id: string, body: Heroku.Pipeline): Promise<import("@heroku/http-call").HTTP<Heroku.Pipeline>>;
23
20
  export declare function getTeam(heroku: APIClient, teamId: any): Promise<import("@heroku/http-call").HTTP<Heroku.Team>>;
24
21
  export declare function getAccountInfo(heroku: APIClient, id?: string): Promise<import("@heroku/http-call").HTTP<Heroku.Account>>;
25
22
  export declare function getAppSetup(heroku: APIClient, buildId: any): Promise<import("@heroku/http-call").HTTP<Heroku.AppSetup>>;
26
23
  export interface AppWithPipelineCoupling extends App {
27
- pipelineCoupling: PipelineCouplingSdk;
24
+ pipelineCoupling: PipelineCoupling;
28
25
  [k: string]: unknown;
29
26
  }
30
27
  export declare function listPipelineApps(heroku: APIClient, pipelineId: string): Promise<Array<AppWithPipelineCoupling>>;
31
28
  export declare function patchCoupling(heroku: APIClient, id: string, stage: string): Promise<import("@heroku/http-call").HTTP<Heroku.PipelineCoupling>>;
32
29
  export declare function removeCoupling(heroku: APIClient, app: string): Promise<import("@heroku/http-call").HTTP<unknown>>;
33
30
  export declare function updateCoupling(heroku: APIClient, app: string, stage: string): Promise<import("@heroku/http-call").HTTP<Heroku.PipelineCoupling>>;
34
- export declare function getReleases(heroku: APIClient, appId: string): Promise<import("@heroku/http-call").HTTP<Release[]>>;
31
+ export declare function getReleases(heroku: APIClient, appId: string): Promise<import("@heroku/http-call").HTTP<Heroku.Release[]>>;
35
32
  export declare function getPipelineConfigVars(heroku: APIClient, pipelineID: string): Promise<import("@heroku/http-call").HTTP<Heroku.ConfigVars>>;
36
33
  export declare function setPipelineConfigVars(heroku: APIClient, pipelineID: string, body: Heroku.ConfigVars | Record<string, null>): Promise<import("@heroku/http-call").HTTP<Heroku.ConfigVars>>;
37
34
  export declare function createTestRun(heroku: APIClient, body: Heroku.TestRun): Promise<import("@heroku/http-call").HTTP<Heroku.TestRun>>;
@@ -1,4 +1,21 @@
1
1
  import { APIClient } from '@heroku-cli/command';
2
- import { App } from '../types/fir';
3
- export declare function isFirApp(appOrName: App | string, herokuApi?: APIClient): Promise<boolean>;
4
- export declare function isCedarApp(appOrName: App | string, herokuApi?: APIClient): Promise<boolean>;
2
+ import { App, Space, DynoSize, TeamApp, Pipeline, Generation, AppGeneration, DynoSizeGeneration, PipelineGeneration } from '../types/fir';
3
+ import Dyno from '../run/dyno';
4
+ export declare type GenerationKind = 'fir' | 'cedar';
5
+ export declare type GenerationLike = Generation | AppGeneration | DynoSizeGeneration | PipelineGeneration | Dyno;
6
+ export declare type GenerationCapable = App | Space | DynoSize | TeamApp | Pipeline;
7
+ /**
8
+ * Get the generation of an object
9
+ *
10
+ * @param source The object to get the generation from
11
+ * @returns The generation of the object
12
+ */
13
+ export declare function getGeneration(source: GenerationLike | GenerationCapable | string): GenerationKind | undefined;
14
+ /**
15
+ * Get the generation of an app by id or name
16
+ *
17
+ * @param appIdOrName The id or name of the app to get the generation for
18
+ * @param herokuApi The Heroku API client to use
19
+ * @returns The generation of the app
20
+ */
21
+ export declare function getGenerationByAppId(appIdOrName: string, herokuApi: APIClient): Promise<GenerationKind | undefined>;
@@ -1,24 +1,52 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isCedarApp = exports.isFirApp = void 0;
4
- async function getApp(appOrName, herokuApi) {
5
- if (typeof appOrName === 'string') {
6
- if (herokuApi === undefined)
7
- throw new Error('herokuApi parameter is required when passing an app name');
8
- const { body: app } = await herokuApi.get(`/apps/${appOrName}`, {
9
- headers: { Accept: 'application/vnd.heroku+json; version=3.sdk' },
10
- });
11
- return app;
3
+ exports.getGenerationByAppId = exports.getGeneration = void 0;
4
+ function getGenerationFromGenerationLike(generation) {
5
+ var _a;
6
+ let maybeGeneration = '';
7
+ if (typeof generation === 'string') {
8
+ maybeGeneration = generation;
12
9
  }
13
- return appOrName;
10
+ else if (generation && 'name' in generation) {
11
+ maybeGeneration = (_a = generation.name) !== null && _a !== void 0 ? _a : '';
12
+ }
13
+ if (/(fir|cedar)/.test(maybeGeneration)) {
14
+ return maybeGeneration;
15
+ }
16
+ // web-1234abcde44-123ab etc. fir
17
+ if (/^web-[0-9a-z]+-[0-9a-z]{5}$/.test(maybeGeneration)) {
18
+ return 'fir';
19
+ }
20
+ // web.n cedar
21
+ if (/^web\.[0-9]+$/.test(maybeGeneration)) {
22
+ return 'cedar';
23
+ }
24
+ return undefined;
14
25
  }
15
- async function isFirApp(appOrName, herokuApi) {
16
- const app = await getApp(appOrName, herokuApi);
17
- return app.generation === 'fir';
26
+ /**
27
+ * Get the generation of an object
28
+ *
29
+ * @param source The object to get the generation from
30
+ * @returns The generation of the object
31
+ */
32
+ function getGeneration(source) {
33
+ if (typeof source === 'object' && 'generation' in source) {
34
+ return getGenerationFromGenerationLike(source.generation);
35
+ }
36
+ return getGenerationFromGenerationLike(source);
18
37
  }
19
- exports.isFirApp = isFirApp;
20
- async function isCedarApp(appOrName, herokuApi) {
21
- const app = await getApp(appOrName, herokuApi);
22
- return app.generation === 'cedar';
38
+ exports.getGeneration = getGeneration;
39
+ /**
40
+ * Get the generation of an app by id or name
41
+ *
42
+ * @param appIdOrName The id or name of the app to get the generation for
43
+ * @param herokuApi The Heroku API client to use
44
+ * @returns The generation of the app
45
+ */
46
+ async function getGenerationByAppId(appIdOrName, herokuApi) {
47
+ const { body: app } = await herokuApi.get(`/apps/${appIdOrName}`, {
48
+ headers: { Accept: 'application/vnd.heroku+json; version=3.sdk' },
49
+ });
50
+ return getGeneration(app);
23
51
  }
24
- exports.isCedarApp = isCedarApp;
52
+ exports.getGenerationByAppId = getGenerationByAppId;
@@ -51,7 +51,7 @@ async function logDisplayer(heroku, options) {
51
51
  core_1.ux.error(err.stack, { exit: 1 });
52
52
  }
53
53
  });
54
- const firApp = await (0, generation_1.isFirApp)(options.app, heroku);
54
+ const firApp = (await (0, generation_1.getGenerationByAppId)(options.app, heroku)) === 'fir';
55
55
  const isTail = firApp || options.tail;
56
56
  const requestBodyParameters = {
57
57
  source: options.source,
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.renderInfo = exports.displayNat = exports.displayShieldState = void 0;
4
4
  const core_1 = require("@oclif/core");
5
+ const generation_1 = require("../apps/generation");
5
6
  function displayShieldState(space) {
6
7
  return space.shield ? 'on' : 'off';
7
8
  }
@@ -30,7 +31,7 @@ function renderInfo(space, json) {
30
31
  State: space.state,
31
32
  Shield: displayShieldState(space),
32
33
  'Outbound IPs': displayNat(space.outbound_ips),
33
- Generation: space.generation.name,
34
+ Generation: (0, generation_1.getGeneration)(space),
34
35
  'Created at': space.created_at,
35
36
  }, ['ID', 'Team', 'Region', 'CIDR', 'Data CIDR', 'State', 'Shield', 'Outbound IPs', 'Generation', 'Created at']);
36
37
  }
@@ -10078,7 +10078,6 @@
10078
10078
  "type": "option"
10079
10079
  },
10080
10080
  "app": {
10081
- "char": "a",
10082
10081
  "description": "filter by app name",
10083
10082
  "name": "app",
10084
10083
  "hasDynamicHelp": false,
@@ -14451,5 +14450,5 @@
14451
14450
  ]
14452
14451
  }
14453
14452
  },
14454
- "version": "10.1.0-beta.0"
14453
+ "version": "10.1.0-beta.2"
14455
14454
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "heroku",
3
3
  "description": "CLI to interact with Heroku",
4
- "version": "10.1.0-beta.0",
4
+ "version": "10.1.0-beta.2",
5
5
  "author": "Heroku",
6
6
  "bin": "./bin/run",
7
7
  "bugs": "https://github.com/heroku/cli/issues",
@@ -46,7 +46,7 @@
46
46
  "execa": "5.1.1",
47
47
  "filesize": "^10.1.2",
48
48
  "foreman": "^3.0.1",
49
- "fs-extra": "^9.1",
49
+ "fs-extra": "^11.3.0",
50
50
  "github-url-to-object": "^4.0.4",
51
51
  "glob": "^10.3.10",
52
52
  "got": "^11.8.6",
@@ -82,25 +82,27 @@
82
82
  },
83
83
  "devDependencies": {
84
84
  "@heroku-cli/schema": "^1.0.25",
85
+ "@istanbuljs/nyc-config-typescript": "^1.0.2",
85
86
  "@oclif/test": "^2.3.28",
86
87
  "@types/ansi-styles": "^3.2.1",
87
88
  "@types/bytes": "^3.1.4",
88
89
  "@types/chai": "^4.3.14",
89
90
  "@types/chai-as-promised": "^7.1.8",
90
91
  "@types/debug": "^4.1.2",
91
- "@types/fs-extra": "^7.0.0",
92
+ "@types/fs-extra": "^11.0.4",
92
93
  "@types/glob": "^7.1.1",
93
94
  "@types/inquirer": "^8.2.10",
94
95
  "@types/lodash": "^4.14.123",
95
- "@types/mocha": "^10.0.6",
96
+ "@types/mocha": "^10.0.10",
96
97
  "@types/node": "20.14.8",
97
- "@types/node-fetch": "^2.1.6",
98
+ "@types/node-fetch": "^2.6.11",
98
99
  "@types/phoenix": "^1.4.0",
99
100
  "@types/proxyquire": "^1.3.28",
100
101
  "@types/psl": "^1.1.3",
101
102
  "@types/redis-parser": "^3.0.3",
102
103
  "@types/shell-escape": "^0.2.0",
103
104
  "@types/shell-quote": "^1.7.5",
105
+ "@types/sinon": "^17.0.3",
104
106
  "@types/ssh2": "^1.15.0",
105
107
  "@types/std-mocks": "^1.0.4",
106
108
  "@types/strftime": "^0.9.8",
@@ -117,7 +119,7 @@
117
119
  "globby": "^10.0.2",
118
120
  "lodash": "^4.17.11",
119
121
  "lolex": "^3.1.0",
120
- "mocha": "^9.2.2",
122
+ "mocha": "^10.8.2",
121
123
  "nock": "^13.5.1",
122
124
  "nyc": "^15.1.0",
123
125
  "oclif": "4.14.36",
@@ -125,11 +127,12 @@
125
127
  "qqjs": "0.3.11",
126
128
  "read-pkg": "^4.0.1",
127
129
  "rimraf": "5.0.5",
128
- "sinon": "^7.2.4",
130
+ "sinon": "^19.0.2",
131
+ "source-map-support": "^0.5.21",
129
132
  "std-mocks": "^2.0.0",
130
133
  "strip-ansi": "6.0.1",
131
134
  "tmp": "^0.2.3",
132
- "ts-node": "^10.9.1",
135
+ "ts-node": "^10.9.2",
133
136
  "typescript": "4.8.4"
134
137
  },
135
138
  "engines": {
@@ -377,6 +380,9 @@
377
380
  "build": "rm -rf lib && tsc",
378
381
  "build:dev": "rm -rf lib && tsc --sourcemap",
379
382
  "lint": "eslint . --ext .ts --config ../../.eslintrc --ignore-path ../../.eslintignore-lib",
383
+ "coverage": "nyc --reporter=lcov --reporter=text-summary npm run test",
384
+ "coverage:html": "nyc --reporter=html --reporter=text-summary npm run test",
385
+ "coverage:check": "nyc --check-coverage --branches 80 --statements 80 --functions 80 --lines 80 npm run test",
380
386
  "postpublish": "rm -f oclif.manifest.json",
381
387
  "prepack": "yarn run build && oclif manifest",
382
388
  "pretest": "tsc -p test --noEmit && cd ../.. && yarn build",
@@ -384,11 +390,11 @@
384
390
  "test:integration": "yarn pretest && mocha --forbid-only \"test/**/*.integration.test.ts\"",
385
391
  "test:smoke": "yarn pretest && mocha --forbid-only \"test/**/smoke.acceptance.test.ts\"",
386
392
  "test:unit:justTest:local": "nyc mocha \"test/**/*.unit.test.ts\"",
387
- "test:unit:justTest:ci": "nyc mocha --forbid-only \"test/**/*.unit.test.ts\"",
393
+ "test:unit:justTest:ci": "nyc --reporter=lcov --reporter=text-summary mocha --forbid-only \"test/**/*.unit.test.ts\"",
388
394
  "test": "yarn pretest && yarn test:unit:justTest:ci",
389
395
  "test:local": "yarn pretest && yarn test:unit:justTest:local",
390
396
  "version": "oclif readme --multi && git add README.md ../../docs"
391
397
  },
392
398
  "types": "lib/index.d.ts",
393
- "gitHead": "bfb87461ae78095d0e540e99db5357ec91d59aae"
399
+ "gitHead": "c2b9e24329ccd077ed5ccdfb6f721c4ffff21e91"
394
400
  }