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.
- package/lib/commands/addons/create.js +17 -0
- package/lib/commands/buildpacks/index.js +9 -3
- package/lib/commands/git/remote.js +1 -1
- package/lib/commands/pipelines/diff.d.ts +1 -1
- package/lib/commands/pipelines/diff.js +24 -14
- package/lib/commands/pipelines/promote.js +1 -1
- package/lib/commands/run/inside.d.ts +5 -2
- package/lib/commands/run/inside.js +17 -11
- package/lib/commands/spaces/create.js +14 -0
- package/lib/commands/telemetry/add.d.ts +2 -3
- package/lib/commands/telemetry/add.js +9 -9
- package/lib/commands/telemetry/update.d.ts +2 -2
- package/lib/commands/telemetry/update.js +5 -5
- package/lib/global_telemetry.js +1 -1
- package/lib/lib/api.d.ts +12 -3
- package/lib/lib/api.js +15 -14
- package/lib/lib/buildpacks/buildpacks.d.ts +2 -4
- package/lib/lib/buildpacks/buildpacks.js +37 -8
- package/lib/lib/pipelines/ownership.d.ts +3 -2
- package/lib/lib/pipelines/render-pipeline.d.ts +2 -1
- package/lib/lib/pipelines/render-pipeline.js +6 -6
- package/oclif.manifest.json +716 -713
- package/package.json +2 -3
|
@@ -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
|
|
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
|
|
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
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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 (
|
|
93
|
+
catch (_b) {
|
|
83
94
|
return { name: appName, repo: githubApp.repo, hash: undefined };
|
|
84
95
|
}
|
|
85
|
-
return { name: appName, repo: githubApp.repo, hash:
|
|
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.
|
|
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(
|
|
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.
|
|
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
|
|
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,
|
|
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)(
|
|
19
|
-
dyno:
|
|
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.
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
11
|
-
const {
|
|
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(
|
|
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.
|
|
48
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 --
|
|
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
|
|
11
|
-
const {
|
|
12
|
-
if (!(
|
|
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)(`
|
package/lib/global_telemetry.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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<
|
|
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
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
4
|
-
export declare function
|
|
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
|
-
|
|
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>;
|