heroku 10.0.0-alpha.2 → 10.0.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.
@@ -49,10 +49,27 @@ class Create extends command_1.Command {
49
49
  const argv = restParse.argv
50
50
  // oclif duplicates specified args in argv
51
51
  .filter(arg => arg !== servicePlan);
52
+ const inferenceRegex = /^heroku-inference/;
53
+ const isInferenceAddon = inferenceRegex.test(servicePlan);
52
54
  if (restParse.nonExistentFlags && restParse.nonExistentFlags.length > 0) {
53
55
  process.stderr.write(` ${color_1.default.yellow('›')} For example: ${color_1.default.cyan(`heroku addons:create -a ${app} ${restParse.raw[0].input} -- ${restParse.nonExistentFlags.join(' ')}`)}`);
54
56
  process.stderr.write(` ${color_1.default.yellow('›')} See https://devcenter.heroku.com/changelog-items/2925 for more info.\n`);
55
57
  }
58
+ if (isInferenceAddon) {
59
+ core_1.ux.warn('\n\nThis pilot feature is a Beta Service. You may opt to try such Beta Service in your sole discretion. ' +
60
+ 'Any use of the Beta Service is subject to the applicable Beta Services Terms provided at ' +
61
+ 'https://www.salesforce.com/company/legal/customer-agreements/. While use of the pilot feature itself is free, ' +
62
+ 'to the extent such use consumes a generally available Service, you may be charged for that consumption as set ' +
63
+ 'forth in the Documentation. Your continued use of this pilot feature constitutes your acceptance of the foregoing.\n\n' +
64
+ 'For clarity and without limitation, the various third-party machine learning and generative artificial intelligence ' +
65
+ '(AI) models and applications (each a “Platform”) integrated with the Beta Service are Non-SFDC Applications, ' +
66
+ 'as that term is defined in the Beta Services Terms. Note that these third-party Platforms include features that use ' +
67
+ 'generative AI technology. Due to the nature of generative AI, the output that a Platform generates may be ' +
68
+ 'unpredictable, and may include inaccurate or harmful responses. Before using any generative AI output, Customer is ' +
69
+ 'solely responsible for reviewing the output for accuracy, safety, and compliance with applicable laws and third-party ' +
70
+ 'acceptable use policies. In addition, Customer’s use of each Platform may be subject to the Platform’s own terms and ' +
71
+ 'conditions, compliance with which Customer is solely responsible.\n');
72
+ }
56
73
  const config = parseConfig(argv);
57
74
  let addon;
58
75
  try {
@@ -2,17 +2,23 @@
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 color_1 = require("@heroku-cli/color");
5
6
  const buildpacks_1 = require("../../lib/buildpacks/buildpacks");
6
7
  class Index extends command_1.Command {
7
8
  async run() {
8
9
  const { flags } = await this.parse(Index);
9
10
  const buildpacksCommand = new buildpacks_1.BuildpackCommand(this.heroku);
10
- const buildpacks = await buildpacksCommand.fetch(flags.app);
11
+ const { body: app } = await this.heroku.get(`/apps/${flags.app}`, {
12
+ headers: {
13
+ Accept: 'application/vnd.heroku+json; version=3.sdk',
14
+ },
15
+ });
16
+ const buildpacks = await buildpacksCommand.fetch(flags.app, app.generation === 'fir');
11
17
  if (buildpacks.length === 0) {
12
- this.log(`${flags.app} has no Buildpack URL set.`);
18
+ this.log(`${color_1.default.app(flags.app)} has no Buildpacks.`);
13
19
  }
14
20
  else {
15
- core_1.ux.styledHeader(`${flags.app} Buildpack URL${buildpacks.length > 1 ? 's' : ''}`);
21
+ core_1.ux.styledHeader(`${color_1.default.app(flags.app)} Buildpack${buildpacks.length > 1 ? 's' : ''}`);
16
22
  buildpacksCommand.display(buildpacks, '');
17
23
  }
18
24
  }
@@ -36,7 +36,7 @@ GitRemote.example = `# set git remote heroku to https://git.heroku.com/example.g
36
36
  $ heroku git:remote -a example
37
37
 
38
38
  # set git remote heroku-staging to https://git.heroku.com/example.git
39
- $ heroku git:remote --remote heroku-staging -a example`;
39
+ $ heroku git:remote --remote heroku-staging -a example-staging`;
40
40
  GitRemote.flags = {
41
41
  app: command_1.flags.string({ char: 'a', description: 'the Heroku app to use' }),
42
42
  remote: command_1.flags.string({ char: 'r', description: 'the git remote to create' }),
@@ -13,7 +13,7 @@ export default class PipelinesDiff extends Command {
13
13
  remote: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
14
14
  };
15
15
  kolkrabbi: KolkrabbiAPI;
16
- getAppInfo: (appName: string, appId: string) => Promise<AppInfo>;
16
+ getAppInfo: (appName: string, appId: string, generation: string) => Promise<AppInfo>;
17
17
  run(): Promise<undefined>;
18
18
  }
19
19
  export {};
@@ -60,7 +60,8 @@ class PipelinesDiff extends command_1.Command {
60
60
  constructor() {
61
61
  super(...arguments);
62
62
  this.kolkrabbi = new kolkrabbi_api_1.default(this.config.userAgent, () => this.heroku.auth);
63
- this.getAppInfo = async (appName, appId) => {
63
+ this.getAppInfo = async (appName, appId, generation) => {
64
+ var _a;
64
65
  // Find GitHub connection for the app
65
66
  const githubApp = await this.kolkrabbi.getAppLink(appId)
66
67
  .catch(() => {
@@ -68,21 +69,31 @@ class PipelinesDiff extends command_1.Command {
68
69
  });
69
70
  // Find the commit hash of the latest release for this app
70
71
  let slug;
72
+ let ociImages;
73
+ let commit;
71
74
  try {
72
75
  const { body: releases } = await (0, api_1.getReleases)(this.heroku, appId);
73
76
  const release = releases.find(r => r.status === 'succeeded');
74
- if (!release || !release.slug) {
77
+ if (!release || !(release.slug || release.oci_image)) {
75
78
  throw new Error(`no release found for ${appName}`);
76
79
  }
77
- slug = await this.heroku.get(`/apps/${appId}/slugs/${release.slug.id}`, {
78
- headers: { Accept: api_1.V3_HEADER },
79
- }).then(res => res.body);
80
- // tslint:disable-next-line: no-unused
80
+ if (generation === 'cedar' && release.slug) {
81
+ slug = await this.heroku.get(`/apps/${appId}/slugs/${release.slug.id}`, {
82
+ headers: { Accept: api_1.SDK_HEADER },
83
+ }).then(res => res.body);
84
+ commit = slug.commit;
85
+ }
86
+ else if (generation === 'fir' && release.oci_image) {
87
+ ociImages = await this.heroku.get(`/apps/${appId}/oci-images/${release.oci_image.id}`, {
88
+ headers: { Accept: api_1.SDK_HEADER },
89
+ }).then(res => res.body);
90
+ commit = (_a = ociImages[0]) === null || _a === void 0 ? void 0 : _a.commit;
91
+ }
81
92
  }
82
- catch (_a) {
93
+ catch (_b) {
83
94
  return { name: appName, repo: githubApp.repo, hash: undefined };
84
95
  }
85
- return { name: appName, repo: githubApp.repo, hash: slug.commit };
96
+ return { name: appName, repo: githubApp.repo, hash: commit };
86
97
  };
87
98
  }
88
99
  async run() {
@@ -96,6 +107,7 @@ class PipelinesDiff extends command_1.Command {
96
107
  return;
97
108
  }
98
109
  const targetAppId = coupling.app.id;
110
+ const generation = coupling.generation;
99
111
  core_1.ux.action.start('Fetching apps from pipeline');
100
112
  const allApps = await (0, api_1.listPipelineApps)(this.heroku, coupling.pipeline.id);
101
113
  core_1.ux.action.stop();
@@ -104,20 +116,18 @@ class PipelinesDiff extends command_1.Command {
104
116
  return core_1.ux.error(`Unable to diff ${targetAppName}`);
105
117
  }
106
118
  const downstreamStage = PROMOTION_ORDER[PROMOTION_ORDER.indexOf(sourceStage) + 1];
107
- if (!downstreamStage || PROMOTION_ORDER.indexOf(sourceStage) < 0) { // eslint-disable-line unicorn/prefer-includes
119
+ if (!downstreamStage || !PROMOTION_ORDER.includes(sourceStage)) {
108
120
  return core_1.ux.error(`Unable to diff ${targetAppName}`);
109
121
  }
110
- const downstreamApps = allApps.filter(function (app) {
111
- return app.coupling.stage === downstreamStage;
112
- });
122
+ const downstreamApps = allApps.filter(app => app.pipelineCoupling.stage === downstreamStage);
113
123
  if (downstreamApps.length === 0) {
114
124
  return core_1.ux.error(`Cannot diff ${targetAppName} as there are no downstream apps configured`);
115
125
  }
116
126
  // Fetch GitHub repo/latest release hash for [target, downstream[0], .., downstream[n]] apps
117
- const appInfoPromises = [this.getAppInfo(targetAppName, targetAppId)];
127
+ const appInfoPromises = [this.getAppInfo(targetAppName, targetAppId, generation)];
118
128
  downstreamApps.forEach(app => {
119
129
  if (app.name && app.id) {
120
- appInfoPromises.push(this.getAppInfo(app.name, app.id));
130
+ appInfoPromises.push(this.getAppInfo(app.name, app.id, generation));
121
131
  }
122
132
  });
123
133
  core_1.ux.action.start('Fetching release info for all apps');
@@ -166,7 +166,7 @@ class Promote extends command_1.Command {
166
166
  else {
167
167
  const targetStage = PROMOTION_ORDER[PROMOTION_ORDER.indexOf(sourceStage) + 1];
168
168
  assertValidPromotion(appNameOrId, sourceStage, targetStage);
169
- targetApps = allApps.filter(app => app.coupling.stage === targetStage);
169
+ targetApps = allApps.filter(app => app.pipelineCoupling.stage === targetStage);
170
170
  assertApps(appNameOrId, targetApps, targetStage);
171
171
  promotionActionName = `Starting promotion to ${targetStage}`;
172
172
  }
@@ -1,14 +1,17 @@
1
1
  import { Command } from '@heroku-cli/command';
2
2
  export default class RunInside extends Command {
3
3
  static description: string;
4
- static hidden: boolean;
5
- static examples: string[];
4
+ static example: string;
6
5
  static flags: {
7
6
  app: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
8
7
  remote: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
9
8
  'exit-code': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
9
  listen: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
11
10
  };
11
+ static args: {
12
+ DYNO_NAME: import("@oclif/core/lib/interfaces/parser").Arg<string, Record<string, unknown>>;
13
+ COMMAND: import("@oclif/core/lib/interfaces/parser").Arg<string, Record<string, unknown>>;
14
+ };
12
15
  static strict: boolean;
13
16
  run(): Promise<void>;
14
17
  }
@@ -5,18 +5,16 @@ const core_1 = require("@oclif/core");
5
5
  const debug_1 = require("debug");
6
6
  const dyno_1 = require("../../lib/run/dyno");
7
7
  const helpers_1 = require("../../lib/run/helpers");
8
+ const tsheredoc_1 = require("tsheredoc");
8
9
  const debug = (0, debug_1.default)('heroku:run:inside');
9
10
  class RunInside extends command_1.Command {
10
11
  async run() {
11
- const { flags, argv } = await this.parse(RunInside);
12
- if (argv.length < 2) {
13
- throw new Error('Usage: heroku run:inside DYNO COMMAND\n\nExample: heroku run:inside web.1 bash');
14
- }
12
+ const { flags, args } = await this.parse(RunInside);
15
13
  const opts = {
16
14
  'exit-code': flags['exit-code'],
17
15
  app: flags.app,
18
- command: (0, helpers_1.buildCommand)(argv.slice(1)),
19
- dyno: argv[0],
16
+ command: (0, helpers_1.buildCommand)([args.COMMAND]),
17
+ dyno: args.DYNO_NAME,
20
18
  heroku: this.heroku,
21
19
  listen: flags.listen,
22
20
  };
@@ -36,15 +34,23 @@ class RunInside extends command_1.Command {
36
34
  }
37
35
  }
38
36
  exports.default = RunInside;
39
- RunInside.description = 'run a one-off process inside an existing heroku dyno';
40
- RunInside.hidden = true;
41
- RunInside.examples = [
42
- '$ heroku run:inside web.1 bash',
43
- ];
37
+ RunInside.description = 'run a one-off process inside an existing heroku dyno (for Fir-generation apps only)';
38
+ RunInside.example = (0, tsheredoc_1.default) `
39
+ Run bash
40
+ $ heroku run:inside web-848cd4f64d-pvpr2 bash
41
+ Run a command supplied by a script
42
+ $ heroku run:inside web-848cd4f64d-pvpr2 -- myscript.sh
43
+ Run a command declared for the worker process type in a Procfile
44
+ $ heroku run:inside worker
45
+ `;
44
46
  RunInside.flags = {
45
47
  app: command_1.flags.app({ required: true }),
46
48
  remote: command_1.flags.remote(),
47
49
  'exit-code': command_1.flags.boolean({ char: 'x', description: 'passthrough the exit code of the remote command' }),
48
50
  listen: command_1.flags.boolean({ description: 'listen on a local port', hidden: true }),
49
51
  };
52
+ RunInside.args = {
53
+ DYNO_NAME: core_1.Args.string({ required: true, description: 'name of the dyno to run command inside' }),
54
+ COMMAND: core_1.Args.string({ required: true, description: 'command to run' }),
55
+ };
50
56
  RunInside.strict = false;
@@ -21,6 +21,20 @@ class Create extends command_1.Command {
21
21
  const dollarAmountMonthly = shield ? '$3000' : '$1000';
22
22
  const dollarAmountHourly = shield ? '$4.17' : '$1.39';
23
23
  const spaceType = shield ? 'Shield' : 'Standard';
24
+ if (generation === 'fir') {
25
+ core_1.ux.warn((0, tsheredoc_1.default) `
26
+ Fir Pilot Features
27
+ Fir is currently a pilot service that is subject to the Beta Services Terms
28
+ (https://www.salesforce.com/company/legal/) or a written Unified Pilot Agreement
29
+ if executed by Customer, and applicable terms in the Product Terms Directory
30
+ (https://ptd.salesforce.com/?_ga=2.247987783.1372150065.1709219475-629000709.1639001992).
31
+ Use of this pilot or beta service is at the Customer's sole discretion.
32
+
33
+ Please note that we’re actively developing and adding new features, and not all
34
+ existing features are currently available. See the Dev Center
35
+ (https://devcenter.heroku.com/articles/generations) for more info.
36
+ `);
37
+ }
24
38
  core_1.ux.action.start(`Creating space ${color_1.default.green(spaceName)} in team ${color_1.default.cyan(team)}`);
25
39
  const { body: space } = await this.heroku.post('/spaces', {
26
40
  headers: {
@@ -3,14 +3,13 @@ export default class Add extends Command {
3
3
  static description: string;
4
4
  static flags: {
5
5
  app: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
6
- remote: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
6
+ headers: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
7
7
  space: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
8
8
  signals: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
9
- endpoint: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
10
9
  transport: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
11
10
  };
12
11
  static args: {
13
- headers: import("@oclif/core/lib/interfaces/parser").Arg<string, Record<string, unknown>>;
12
+ endpoint: import("@oclif/core/lib/interfaces/parser").Arg<string, Record<string, unknown>>;
14
13
  };
15
14
  static example: string;
16
15
  run(): Promise<void>;
@@ -7,8 +7,8 @@ const util_1 = require("../../lib/telemetry/util");
7
7
  class Add extends command_1.Command {
8
8
  async run() {
9
9
  const { flags, args } = await this.parse(Add);
10
- const { app, space, signals, endpoint, transport } = flags;
11
- const { headers } = args;
10
+ const { app, headers, space, signals, transport } = flags;
11
+ const { endpoint } = args;
12
12
  let id;
13
13
  if (app) {
14
14
  const { body: herokuApp } = await this.heroku.get(`/apps/${app}`, {
@@ -20,6 +20,7 @@ class Add extends command_1.Command {
20
20
  const { body: herokuSpace } = await this.heroku.get(`/spaces/${space}`);
21
21
  id = herokuSpace.id;
22
22
  }
23
+ const exporterHeaders = headers || '{}';
23
24
  const drainConfig = {
24
25
  owner: {
25
26
  type: app ? 'app' : 'space',
@@ -29,7 +30,7 @@ class Add extends command_1.Command {
29
30
  exporter: {
30
31
  endpoint,
31
32
  type: `otlp${transport}`,
32
- headers: JSON.parse(headers),
33
+ headers: JSON.parse(exporterHeaders),
33
34
  },
34
35
  };
35
36
  const { body: drain } = await this.heroku.post('/telemetry-drains', {
@@ -44,17 +45,16 @@ class Add extends command_1.Command {
44
45
  exports.default = Add;
45
46
  Add.description = 'Add and configure a new telemetry drain. Defaults to collecting all telemetry unless otherwise specified.';
46
47
  Add.flags = {
47
- app: command_1.flags.app({ exactlyOne: ['app', 'remote', 'space'], description: 'app to add a drain to' }),
48
- remote: command_1.flags.remote({ description: 'git remote of app to add a drain to' }),
48
+ app: command_1.flags.string({ char: 'a', exactlyOne: ['app', 'space'], description: 'app to add a drain to' }),
49
+ headers: command_1.flags.string({ description: 'custom headers to configure the drain in json format' }),
49
50
  space: command_1.flags.string({ char: 's', description: 'space to add a drain to' }),
50
51
  signals: command_1.flags.string({ default: 'all', description: 'comma-delimited list of signals to collect (traces, metrics, logs). Use "all" to collect all signals.' }),
51
- endpoint: command_1.flags.string({ required: true, description: 'drain url' }),
52
- transport: command_1.flags.string({ required: true, options: ['http', 'grpc'], description: 'transport protocol for the drain' }),
52
+ transport: command_1.flags.string({ default: 'http', options: ['http', 'grpc'], description: 'transport protocol for the drain' }),
53
53
  };
54
54
  Add.args = {
55
- headers: core_1.Args.string({ required: true, description: 'custom headers to configure the drain in json format' }),
55
+ endpoint: core_1.Args.string({ required: true, description: 'drain url' }),
56
56
  };
57
57
  Add.example = (0, tsheredoc_1.default)(`
58
58
  Add a telemetry drain to an app to collect logs and traces:
59
- $ heroku telemetry:add --app myapp --signals logs,traces --endpoint https://my-endpoint.com --transport http '{"x-drain-example-team": "API_KEY", "x-drain-example-dataset": "METRICS_DATASET"}'
59
+ $ heroku telemetry:add https://my-endpoint.com --app myapp --signals logs,traces --headers '{"x-drain-example-team": "API_KEY", "x-drain-example-dataset": "METRICS_DATASET"}'
60
60
  `);
@@ -4,11 +4,11 @@ export default class Update extends Command {
4
4
  static description: string;
5
5
  static args: {
6
6
  telemetry_drain_id: import("@oclif/core/lib/interfaces/parser").Arg<string, Record<string, unknown>>;
7
- headers: import("@oclif/core/lib/interfaces/parser").Arg<string | undefined, Record<string, unknown>>;
8
7
  };
9
8
  static flags: {
10
- signals: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
11
9
  endpoint: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
10
+ headers: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
11
+ signals: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
12
12
  transport: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
13
13
  };
14
14
  static example: string;
@@ -7,9 +7,9 @@ const util_1 = require("../../lib/telemetry/util");
7
7
  class Update extends command_1.Command {
8
8
  async run() {
9
9
  const { args, flags } = await this.parse(Update);
10
- const { telemetry_drain_id, headers } = args;
11
- const { signals, endpoint, transport } = flags;
12
- if (!(headers || signals || endpoint || transport)) {
10
+ const { telemetry_drain_id } = args;
11
+ const { endpoint, headers, signals, transport } = flags;
12
+ if (!(endpoint || headers || signals || transport)) {
13
13
  core_1.ux.error((0, tsheredoc_1.default)(`
14
14
  Requires either --signals, --endpoint, --transport or HEADERS to be provided.
15
15
  See more help with --help
@@ -48,11 +48,11 @@ Update.topic = 'telemetry';
48
48
  Update.description = 'updates a telemetry drain with provided attributes (attributes not provided remain unchanged)';
49
49
  Update.args = {
50
50
  telemetry_drain_id: core_1.Args.string({ required: true, description: 'ID of the drain to update' }),
51
- headers: core_1.Args.string({ description: 'custom headers to configure the drain in json format' }),
52
51
  };
53
52
  Update.flags = {
54
- signals: command_1.flags.string({ description: 'comma-delimited list of signals to collect (traces, metrics, logs). Use "all" to collect all signals.' }),
55
53
  endpoint: command_1.flags.string({ description: 'drain url' }),
54
+ headers: command_1.flags.string({ description: 'custom headers to configure the drain in json format' }),
55
+ signals: command_1.flags.string({ description: 'comma-delimited list of signals to collect (traces, metrics, logs). Use "all" to collect all signals.' }),
56
56
  transport: command_1.flags.string({ options: ['http', 'grpc'], description: 'transport protocol for the drain' }),
57
57
  };
58
58
  Update.example = (0, tsheredoc_1.default)(`
@@ -43,7 +43,7 @@ const provider = new NodeTracerProvider({
43
43
  });
44
44
  const headers = { Authorization: `Bearer ${process.env.IS_HEROKU_TEST_ENV !== 'true' ? getToken() : ''}` };
45
45
  const exporter = new OTLPTraceExporter({
46
- url: isDev ? 'https://backboard-staging.herokuapp.com/otel/v1/traces' : 'https://backboard.heroku.com/otel/v1/traces',
46
+ url: isDev ? 'https://backboard.staging.herokudev.com/otel/v1/traces' : 'https://backboard.heroku.com/otel/v1/traces',
47
47
  headers,
48
48
  compression: 'none',
49
49
  });
package/lib/lib/api.d.ts CHANGED
@@ -1,6 +1,8 @@
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
4
  export declare const V3_HEADER = "application/vnd.heroku+json; version=3";
5
+ export declare const SDK_HEADER = "application/vnd.heroku+json; version=3.sdk";
4
6
  export declare const FILTERS_HEADER: string;
5
7
  export declare const PIPELINES_HEADER: string;
6
8
  export declare function createAppSetup(heroku: APIClient, body: {
@@ -12,17 +14,24 @@ export declare function createPipeline(heroku: APIClient, name: any, owner: any)
12
14
  export declare function createPipelineTransfer(heroku: APIClient, pipeline: Heroku.Pipeline): Promise<import("http-call").HTTP<unknown>>;
13
15
  export declare function destroyPipeline(heroku: APIClient, name: any, pipelineId: any): Promise<import("http-call").HTTP<unknown>>;
14
16
  export declare function findPipelineByName(heroku: APIClient, idOrName: string): Promise<import("http-call").HTTP<Heroku.Pipeline[]>>;
15
- export declare function getCoupling(heroku: APIClient, app: string): Promise<import("http-call").HTTP<Heroku.PipelineCoupling>>;
17
+ export interface PipelineCouplingSdk extends Required<PipelineCoupling> {
18
+ generation: 'fir' | 'cedar';
19
+ }
20
+ export declare function getCoupling(heroku: APIClient, app: string): Promise<import("http-call").HTTP<PipelineCouplingSdk>>;
16
21
  export declare function getPipeline(heroku: APIClient, id: string): Promise<import("http-call").HTTP<Heroku.Pipeline>>;
17
22
  export declare function updatePipeline(heroku: APIClient, id: string, body: Heroku.Pipeline): Promise<import("http-call").HTTP<Heroku.Pipeline>>;
18
23
  export declare function getTeam(heroku: APIClient, teamId: any): Promise<import("http-call").HTTP<Heroku.Team>>;
19
24
  export declare function getAccountInfo(heroku: APIClient, id?: string): Promise<import("http-call").HTTP<Heroku.Account>>;
20
25
  export declare function getAppSetup(heroku: APIClient, buildId: any): Promise<import("http-call").HTTP<Heroku.AppSetup>>;
21
- export declare function listPipelineApps(heroku: APIClient, pipelineId: string): Promise<Array<Heroku.App>>;
26
+ export interface AppWithPipelineCoupling extends App {
27
+ pipelineCoupling: PipelineCouplingSdk;
28
+ [k: string]: unknown;
29
+ }
30
+ export declare function listPipelineApps(heroku: APIClient, pipelineId: string): Promise<Array<AppWithPipelineCoupling>>;
22
31
  export declare function patchCoupling(heroku: APIClient, id: string, stage: string): Promise<import("http-call").HTTP<Heroku.PipelineCoupling>>;
23
32
  export declare function removeCoupling(heroku: APIClient, app: string): Promise<import("http-call").HTTP<unknown>>;
24
33
  export declare function updateCoupling(heroku: APIClient, app: string, stage: string): Promise<import("http-call").HTTP<Heroku.PipelineCoupling>>;
25
- export declare function getReleases(heroku: APIClient, appId: string): Promise<import("http-call").HTTP<Heroku.Release[]>>;
34
+ export declare function getReleases(heroku: APIClient, appId: string): Promise<import("http-call").HTTP<Release[]>>;
26
35
  export declare function getPipelineConfigVars(heroku: APIClient, pipelineID: string): Promise<import("http-call").HTTP<Heroku.ConfigVars>>;
27
36
  export declare function setPipelineConfigVars(heroku: APIClient, pipelineID: string, body: Heroku.ConfigVars | Record<string, null>): Promise<import("http-call").HTTP<Heroku.ConfigVars>>;
28
37
  export declare function createTestRun(heroku: APIClient, body: Heroku.TestRun): Promise<import("http-call").HTTP<Heroku.TestRun>>;
package/lib/lib/api.js CHANGED
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.updateTestRun = exports.getTestNodes = exports.createTestRun = exports.setPipelineConfigVars = exports.getPipelineConfigVars = exports.getReleases = exports.updateCoupling = exports.removeCoupling = exports.patchCoupling = exports.listPipelineApps = exports.getAppSetup = exports.getAccountInfo = exports.getTeam = exports.updatePipeline = exports.getPipeline = exports.getCoupling = exports.findPipelineByName = exports.destroyPipeline = exports.createPipelineTransfer = exports.createPipeline = exports.createCoupling = exports.postCoupling = exports.createAppSetup = exports.PIPELINES_HEADER = exports.FILTERS_HEADER = exports.V3_HEADER = void 0;
4
- const lodash_1 = require("lodash");
3
+ exports.updateTestRun = exports.getTestNodes = exports.createTestRun = exports.setPipelineConfigVars = exports.getPipelineConfigVars = exports.getReleases = exports.updateCoupling = exports.removeCoupling = exports.patchCoupling = exports.listPipelineApps = exports.getAppSetup = exports.getAccountInfo = exports.getTeam = exports.updatePipeline = exports.getPipeline = exports.getCoupling = exports.findPipelineByName = exports.destroyPipeline = exports.createPipelineTransfer = exports.createPipeline = exports.createCoupling = exports.postCoupling = exports.createAppSetup = exports.PIPELINES_HEADER = exports.FILTERS_HEADER = exports.SDK_HEADER = exports.V3_HEADER = void 0;
5
4
  exports.V3_HEADER = 'application/vnd.heroku+json; version=3';
5
+ exports.SDK_HEADER = 'application/vnd.heroku+json; version=3.sdk';
6
6
  exports.FILTERS_HEADER = `${exports.V3_HEADER}.filters`;
7
7
  exports.PIPELINES_HEADER = `${exports.V3_HEADER}.pipelines`;
8
8
  const CI_HEADER = `${exports.V3_HEADER}.ci`;
@@ -53,7 +53,9 @@ function findPipelineByName(heroku, idOrName) {
53
53
  }
54
54
  exports.findPipelineByName = findPipelineByName;
55
55
  function getCoupling(heroku, app) {
56
- return heroku.get(`/apps/${app}/pipeline-couplings`);
56
+ return heroku.get(`/apps/${app}/pipeline-couplings`, {
57
+ headers: { Accept: exports.SDK_HEADER },
58
+ });
57
59
  }
58
60
  exports.getCoupling = getCoupling;
59
61
  function getPipeline(heroku, id) {
@@ -89,17 +91,16 @@ function getAppSetup(heroku, buildId) {
89
91
  }
90
92
  exports.getAppSetup = getAppSetup;
91
93
  function listCouplings(heroku, pipelineId) {
92
- return heroku.get(`/pipelines/${pipelineId}/pipeline-couplings`);
94
+ return heroku.get(`/pipelines/${pipelineId}/pipeline-couplings`, {
95
+ headers: { Accept: exports.SDK_HEADER },
96
+ });
93
97
  }
94
- function listPipelineApps(heroku, pipelineId) {
95
- return listCouplings(heroku, pipelineId).then(({ body: couplings }) => {
96
- const appIds = couplings.map(coupling => (coupling.app && coupling.app.id) || '');
97
- return getAppFilter(heroku, appIds).then(({ body: apps }) => {
98
- const couplingsByAppId = (0, lodash_1.keyBy)(couplings, coupling => coupling.app && coupling.app.id);
99
- return apps.map(app => {
100
- return Object.assign(Object.assign({}, app), { coupling: couplingsByAppId[app.id] });
101
- });
102
- });
98
+ async function listPipelineApps(heroku, pipelineId) {
99
+ const { body: couplings } = await listCouplings(heroku, pipelineId);
100
+ const appIds = couplings.map(coupling => coupling.app.id || '');
101
+ const { body: apps } = await getAppFilter(heroku, appIds);
102
+ return apps.map(app => {
103
+ return Object.assign(Object.assign({}, app), { pipelineCoupling: couplings.find(coupling => coupling.app.id === app.id) });
103
104
  });
104
105
  }
105
106
  exports.listPipelineApps = listPipelineApps;
@@ -121,7 +122,7 @@ function updateCoupling(heroku, app, stage) {
121
122
  exports.updateCoupling = updateCoupling;
122
123
  function getReleases(heroku, appId) {
123
124
  return heroku.get(`/apps/${appId}/releases`, {
124
- headers: { Accept: exports.V3_HEADER, Range: 'version ..; order=desc' },
125
+ headers: { Accept: exports.SDK_HEADER, Range: 'version ..; order=desc' },
125
126
  partial: true,
126
127
  });
127
128
  }
@@ -11,10 +11,8 @@ export declare class BuildpackCommand {
11
11
  heroku: APIClient;
12
12
  registry: BuildpackRegistry;
13
13
  constructor(heroku: APIClient);
14
- fetch(app: string): Promise<any[]>;
15
- mapBuildpackResponse(buildpacks: {
16
- body: any;
17
- }): BuildpackResponse[];
14
+ fetch(app: string, isFirApp?: boolean): Promise<any[]>;
15
+ mapBuildpackResponse(buildpacks: BuildpackResponse[]): BuildpackResponse[];
18
16
  display(buildpacks: BuildpackResponse[], indent: string): void;
19
17
  registryNameToUrl(buildpack: string): Promise<string>;
20
18
  findUrl(buildpacks: BuildpackResponse[], buildpack: string): Promise<number>;
@@ -7,19 +7,48 @@ const core_1 = require("@oclif/core");
7
7
  const lodash_1 = require("lodash");
8
8
  const true_myth_1 = require("true-myth");
9
9
  const push_1 = require("../git/push");
10
- const validUrl = require('valid-url');
10
+ const validator_1 = require("validator");
11
11
  class BuildpackCommand {
12
12
  constructor(heroku) {
13
13
  this.heroku = heroku;
14
14
  this.registry = new buildpack_registry_1.BuildpackRegistry();
15
15
  }
16
- async fetch(app) {
17
- const buildpacks = await this.heroku.get(`/apps/${app}/buildpack-installations`);
16
+ async fetch(app, isFirApp = false) {
17
+ let buildpacks;
18
+ if (isFirApp) {
19
+ const { body: releases } = await this.heroku.request(`/apps/${app}/releases`, {
20
+ partial: true,
21
+ headers: {
22
+ Range: 'version ..; max=10, order=desc',
23
+ Accept: 'application/vnd.heroku+json; version=3.sdk',
24
+ },
25
+ });
26
+ if (releases.length === 0 || releases[0].oci_image === null)
27
+ return [];
28
+ const latestImageId = releases[0].oci_image.id;
29
+ const { body: ociImages } = await this.heroku.get(`/apps/${app}/oci-images/${latestImageId}`, {
30
+ headers: {
31
+ Accept: 'application/vnd.heroku+json; version=3.sdk',
32
+ },
33
+ });
34
+ buildpacks = ociImages[0].buildpacks.map((b, index) => {
35
+ return {
36
+ buildpack: {
37
+ url: b.id || b.homepage,
38
+ name: b.id,
39
+ },
40
+ ordinal: index,
41
+ };
42
+ });
43
+ }
44
+ else {
45
+ const { body: buildpackInstallations } = await this.heroku.get(`/apps/${app}/buildpack-installations`);
46
+ buildpacks = buildpackInstallations;
47
+ }
18
48
  return this.mapBuildpackResponse(buildpacks);
19
49
  }
20
50
  mapBuildpackResponse(buildpacks) {
21
- const body = buildpacks.body;
22
- return body.map((bp) => {
51
+ return buildpacks.map((bp) => {
23
52
  bp.buildpack.url = bp.buildpack.url.replace(/^urn:buildpack:/, '');
24
53
  return bp;
25
54
  });
@@ -35,11 +64,11 @@ class BuildpackCommand {
35
64
  }
36
65
  }
37
66
  async registryNameToUrl(buildpack) {
38
- if (validUrl.isWebUri(buildpack)) {
67
+ if ((0, validator_1.isURL)(buildpack)) {
39
68
  return buildpack;
40
69
  }
41
70
  true_myth_1.Result.match({
42
- Ok: _ => { },
71
+ Ok: () => { },
43
72
  Err: err => {
44
73
  core_1.ux.error(`Could not find the buildpack: ${buildpack}. ${err}`, { exit: 1 });
45
74
  },
@@ -93,7 +122,7 @@ class BuildpackCommand {
93
122
  return this.put(app, buildpackUpdates);
94
123
  }
95
124
  async put(app, buildpackUpdates) {
96
- const buildpacks = await this.heroku.put(`/apps/${app}/buildpack-installations`, {
125
+ const { body: buildpacks } = await this.heroku.put(`/apps/${app}/buildpack-installations`, {
97
126
  headers: { Range: '' },
98
127
  body: { updates: buildpackUpdates },
99
128
  });
@@ -1,4 +1,5 @@
1
1
  import { APIClient } from '@heroku-cli/command';
2
2
  import * as Heroku from '@heroku-cli/schema';
3
- export declare function warnMixedOwnership(pipelineApps: Array<Heroku.App>, pipeline: Heroku.Pipeline, owner: string): void;
4
- export declare function getOwner(heroku: APIClient, apps: Array<Heroku.App>, pipeline: Heroku.Pipeline): Promise<any>;
3
+ import { AppWithPipelineCoupling } from '../api';
4
+ export declare function warnMixedOwnership(pipelineApps: Array<AppWithPipelineCoupling>, pipeline: Heroku.Pipeline, owner: string): void;
5
+ export declare function getOwner(heroku: APIClient, apps: Array<AppWithPipelineCoupling>, pipeline: Heroku.Pipeline): Promise<any>;
@@ -1,6 +1,7 @@
1
1
  import { APIClient } from '@heroku-cli/command';
2
2
  import * as Heroku from '@heroku-cli/schema';
3
- export default function renderPipeline(heroku: APIClient, pipeline: Heroku.Pipeline, pipelineApps: Array<Heroku.App>, { withOwners, showOwnerWarning }?: {
3
+ import { AppWithPipelineCoupling } from '../api';
4
+ export default function renderPipeline(heroku: APIClient, pipeline: Heroku.Pipeline, pipelineApps: Array<AppWithPipelineCoupling>, { withOwners, showOwnerWarning }?: {
4
5
  withOwners: boolean;
5
6
  showOwnerWarning: boolean;
6
7
  }): Promise<void>;