heroku 9.3.2-beta.0 → 10.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/lib/commands/access/index.js +1 -1
- package/lib/commands/apps/errors.js +1 -1
- package/lib/commands/apps/favorites/add.js +1 -1
- package/lib/commands/buildpacks/add.js +1 -1
- package/lib/commands/buildpacks/info.js +1 -1
- package/lib/commands/buildpacks/set.js +1 -1
- package/lib/commands/buildpacks/versions.js +1 -1
- package/lib/commands/logs.d.ts +5 -3
- package/lib/commands/logs.js +61 -22
- package/lib/commands/members/index.js +1 -1
- package/lib/commands/orgs/open.js +1 -1
- package/lib/commands/pg/copy.js +1 -1
- package/lib/commands/pipelines/promote.js +3 -3
- package/lib/commands/ps/type.js +2 -3
- package/lib/commands/spaces/create.d.ts +6 -6
- package/lib/commands/spaces/create.js +24 -12
- package/lib/commands/spaces/index.d.ts +2 -2
- package/lib/commands/spaces/index.js +6 -1
- package/lib/commands/spaces/info.js +5 -3
- package/lib/commands/spaces/wait.js +5 -3
- package/lib/commands/telemetry/add.d.ts +18 -0
- package/lib/commands/telemetry/add.js +70 -0
- package/lib/commands/telemetry/index.d.ts +13 -0
- package/lib/commands/telemetry/index.js +43 -0
- package/lib/commands/telemetry/info.d.ts +10 -0
- package/lib/commands/telemetry/info.js +24 -0
- package/lib/commands/telemetry/remove.d.ts +15 -0
- package/lib/commands/telemetry/remove.js +63 -0
- package/lib/commands/telemetry/update.d.ts +16 -0
- package/lib/commands/telemetry/update.js +60 -0
- package/lib/file.js +0 -2
- package/lib/lib/apps/generation.d.ts +4 -0
- package/lib/lib/apps/generation.js +24 -0
- package/lib/lib/run/colorize.js +1 -1
- package/lib/lib/run/log-displayer.d.ts +3 -2
- package/lib/lib/run/log-displayer.js +41 -33
- package/lib/lib/spaces/spaces.d.ts +4 -2
- package/lib/lib/spaces/spaces.js +2 -1
- package/lib/lib/telemetry/util.d.ts +3 -0
- package/lib/lib/telemetry/util.js +29 -0
- package/oclif.manifest.json +348 -53
- package/package.json +6 -6
- package/lib/lib/run/line-transform.d.ts +0 -4
- package/lib/lib/run/line-transform.js +0 -26
package/README.md
CHANGED
|
@@ -71,6 +71,7 @@ For other issues, [submit a support ticket](https://help.heroku.com/).
|
|
|
71
71
|
* [`heroku spaces`](docs/spaces.md) - list available spaces
|
|
72
72
|
* [`heroku status`](docs/status.md) - display current status of the Heroku platform
|
|
73
73
|
* [`heroku teams`](docs/teams.md) - manage teams
|
|
74
|
+
* [`heroku telemetry`](docs/telemetry.md) - list telemetry drains
|
|
74
75
|
* [`heroku update`](docs/update.md) - update the Heroku CLI
|
|
75
76
|
* [`heroku version`](docs/version.md)
|
|
76
77
|
* [`heroku webhooks`](docs/webhooks.md) - list webhooks on an app
|
|
@@ -48,7 +48,7 @@ function buildCollaboratorsArray(collaboratorsRaw, admins) {
|
|
|
48
48
|
class AccessIndex extends command_1.Command {
|
|
49
49
|
async run() {
|
|
50
50
|
var _a, _b;
|
|
51
|
-
const { flags
|
|
51
|
+
const { flags } = await this.parse(AccessIndex);
|
|
52
52
|
const { app: appName, json } = flags;
|
|
53
53
|
const { body: app } = await this.heroku.get(`/apps/${appName}`);
|
|
54
54
|
let { body: collaborators } = await this.heroku.get(`/apps/${appName}/collaborators`);
|
|
@@ -37,7 +37,7 @@ const sumErrors = (errors) => {
|
|
|
37
37
|
class Errors extends command_1.Command {
|
|
38
38
|
async run() {
|
|
39
39
|
const { flags } = await this.parse(Errors);
|
|
40
|
-
const hours = Number.parseInt(flags.hours);
|
|
40
|
+
const hours = Number.parseInt(flags.hours, 10);
|
|
41
41
|
const NOW = new Date().toISOString();
|
|
42
42
|
const YESTERDAY = new Date(Date.now() - (hours * 60 * 60 * 1000)).toISOString();
|
|
43
43
|
const DATE_QUERY = `start_time=${YESTERDAY}&end_time=${NOW}&step=1h`;
|
|
@@ -9,7 +9,7 @@ class Add extends command_1.Command {
|
|
|
9
9
|
const { app } = flags;
|
|
10
10
|
core_1.ux.action.start(`Adding ${color_1.default.app(app)} to favorites`);
|
|
11
11
|
const { body: favorites } = await this.heroku.get('/favorites?type=app', { hostname: 'particleboard.heroku.com' });
|
|
12
|
-
if (favorites.
|
|
12
|
+
if (favorites.some(f => f.resource_name === app)) {
|
|
13
13
|
throw new Error(`${color_1.default.app(app)} is already a favorite app.`);
|
|
14
14
|
}
|
|
15
15
|
try {
|
|
@@ -17,7 +17,7 @@ class Add extends command_1.Command {
|
|
|
17
17
|
spliceIndex = buildpacks.length;
|
|
18
18
|
}
|
|
19
19
|
else {
|
|
20
|
-
// eslint-disable-next-line unicorn/no-array-
|
|
20
|
+
// eslint-disable-next-line unicorn/no-array-method-this-argument
|
|
21
21
|
const foundIndex = buildpackCommand.findIndex(buildpacks, flags.index);
|
|
22
22
|
spliceIndex = (foundIndex === -1) ? buildpacks.length : foundIndex;
|
|
23
23
|
}
|
|
@@ -9,7 +9,7 @@ class Info extends command_1.Command {
|
|
|
9
9
|
const { args } = await this.parse(Info);
|
|
10
10
|
const registry = new buildpack_registry_1.BuildpackRegistry();
|
|
11
11
|
true_myth_1.Result.match({
|
|
12
|
-
Ok:
|
|
12
|
+
Ok: () => { },
|
|
13
13
|
Err: err => {
|
|
14
14
|
this.error(`Could not publish the buildpack.\n${err}`);
|
|
15
15
|
},
|
|
@@ -17,7 +17,7 @@ class Set extends command_1.Command {
|
|
|
17
17
|
spliceIndex = 0;
|
|
18
18
|
}
|
|
19
19
|
else {
|
|
20
|
-
// eslint-disable-next-line unicorn/no-array-
|
|
20
|
+
// eslint-disable-next-line unicorn/no-array-method-this-argument
|
|
21
21
|
const foundIndex = buildpackCommand.findIndex(buildpacks, flags.index);
|
|
22
22
|
spliceIndex = (foundIndex === -1) ? buildpacks.length : foundIndex;
|
|
23
23
|
}
|
package/lib/commands/logs.d.ts
CHANGED
|
@@ -4,13 +4,15 @@ export default class Logs extends Command {
|
|
|
4
4
|
static examples: string[];
|
|
5
5
|
static flags: {
|
|
6
6
|
app: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
7
|
-
|
|
7
|
+
dyno: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
8
|
+
'force-colors': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
9
|
+
'no-color': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
8
10
|
num: import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
9
11
|
ps: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
10
|
-
|
|
12
|
+
remote: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
11
13
|
source: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
12
14
|
tail: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
13
|
-
|
|
15
|
+
type: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
14
16
|
};
|
|
15
17
|
run(): Promise<void>;
|
|
16
18
|
}
|
package/lib/commands/logs.js
CHANGED
|
@@ -1,45 +1,84 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
|
4
|
-
// tslint:disable:file-name-casing
|
|
5
3
|
const color_1 = require("@heroku-cli/color");
|
|
6
4
|
const command_1 = require("@heroku-cli/command");
|
|
7
5
|
const completions_1 = require("@heroku-cli/command/lib/completions");
|
|
8
6
|
const log_displayer_1 = require("../lib/run/log-displayer");
|
|
7
|
+
const tsheredoc_1 = require("tsheredoc");
|
|
9
8
|
class Logs extends command_1.Command {
|
|
10
9
|
async run() {
|
|
11
10
|
const { flags } = await this.parse(Logs);
|
|
12
|
-
|
|
11
|
+
const { app, dyno, 'force-colors': forceColors, num, ps, source, tail, type } = flags;
|
|
12
|
+
if (forceColors)
|
|
13
|
+
color_1.default.enabled = true;
|
|
13
14
|
await (0, log_displayer_1.default)(this.heroku, {
|
|
14
|
-
app
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
tail
|
|
19
|
-
|
|
15
|
+
app,
|
|
16
|
+
dyno,
|
|
17
|
+
lines: num || 100,
|
|
18
|
+
source,
|
|
19
|
+
tail,
|
|
20
|
+
type: type || ps,
|
|
20
21
|
});
|
|
21
22
|
}
|
|
22
23
|
}
|
|
23
24
|
exports.default = Logs;
|
|
24
|
-
Logs.description =
|
|
25
|
-
|
|
25
|
+
Logs.description = (0, tsheredoc_1.default) `
|
|
26
|
+
display recent log output
|
|
27
|
+
disable colors with --no-color, HEROKU_LOGS_COLOR=0, or HEROKU_COLOR=0
|
|
28
|
+
`;
|
|
26
29
|
Logs.examples = [
|
|
27
|
-
'
|
|
28
|
-
'
|
|
29
|
-
'
|
|
30
|
-
'
|
|
30
|
+
'heroku logs --app=my-app',
|
|
31
|
+
'heroku logs --num=50 --app=my-app',
|
|
32
|
+
'heroku logs --dyno=web-123-456 --app=my-app',
|
|
33
|
+
'heroku logs --type=web --app=my-app',
|
|
34
|
+
'heroku logs --app=my-app --tail',
|
|
31
35
|
];
|
|
32
36
|
Logs.flags = {
|
|
33
37
|
app: command_1.flags.app({ required: true }),
|
|
34
|
-
remote: command_1.flags.remote(),
|
|
35
|
-
num: command_1.flags.integer({ char: 'n', description: 'number of lines to display' }),
|
|
36
|
-
ps: command_1.flags.string({ char: 'p', description: 'hidden alias for dyno', hidden: true }),
|
|
37
38
|
dyno: command_1.flags.string({
|
|
38
39
|
char: 'd',
|
|
39
|
-
description: 'only show output from this dyno
|
|
40
|
+
description: 'only show output from this dyno (such as "web-123-456" or "worker.2")',
|
|
41
|
+
}),
|
|
42
|
+
'force-colors': command_1.flags.boolean({
|
|
43
|
+
description: 'force use of colors (even on non-tty output)',
|
|
44
|
+
}),
|
|
45
|
+
// supports-color NPM package will parse ARGV looking for flag `--no-color`, but
|
|
46
|
+
// we need to define it here for OClif not to error out on an inexistent flag.
|
|
47
|
+
'no-color': command_1.flags.boolean({
|
|
48
|
+
default: false,
|
|
49
|
+
hidden: true,
|
|
50
|
+
relationships: [
|
|
51
|
+
{ type: 'none', flags: ['force-colors'] },
|
|
52
|
+
],
|
|
53
|
+
}),
|
|
54
|
+
num: command_1.flags.integer({
|
|
55
|
+
char: 'n',
|
|
56
|
+
description: 'number of lines to display (ignored for Fir generation apps)',
|
|
57
|
+
}),
|
|
58
|
+
ps: command_1.flags.string({
|
|
59
|
+
char: 'p',
|
|
60
|
+
hidden: true,
|
|
61
|
+
description: 'hidden alias for type',
|
|
62
|
+
relationships: [
|
|
63
|
+
{ type: 'none', flags: ['dyno'] },
|
|
64
|
+
],
|
|
65
|
+
completion: completions_1.ProcessTypeCompletion,
|
|
66
|
+
}),
|
|
67
|
+
remote: command_1.flags.remote(),
|
|
68
|
+
source: command_1.flags.string({
|
|
69
|
+
char: 's',
|
|
70
|
+
description: 'only show output from this source (such as "app" or "heroku")',
|
|
71
|
+
}),
|
|
72
|
+
tail: command_1.flags.boolean({
|
|
73
|
+
char: 't',
|
|
74
|
+
default: false,
|
|
75
|
+
description: 'continually stream logs (defaults to true for Fir generation apps)',
|
|
76
|
+
}),
|
|
77
|
+
type: command_1.flags.string({
|
|
78
|
+
description: 'only show output from this process type (such as "web" or "worker")',
|
|
79
|
+
relationships: [
|
|
80
|
+
{ type: 'none', flags: ['dyno', 'ps'] },
|
|
81
|
+
],
|
|
40
82
|
completion: completions_1.ProcessTypeCompletion,
|
|
41
83
|
}),
|
|
42
|
-
source: command_1.flags.string({ char: 's', description: 'only show output from this source (such as "app" or "heroku")' }),
|
|
43
|
-
tail: command_1.flags.boolean({ char: 't', description: 'continually stream logs' }),
|
|
44
|
-
'force-colors': command_1.flags.boolean({ description: 'force use of colors (even on non-tty output)' }),
|
|
45
84
|
};
|
|
@@ -29,7 +29,7 @@ class MembersIndex extends command_1.Command {
|
|
|
29
29
|
let teamInvites = [];
|
|
30
30
|
if (teamInfo.type === 'team') {
|
|
31
31
|
const { body: orgFeatures } = await this.heroku.get(`/teams/${team}/features`);
|
|
32
|
-
if (orgFeatures.
|
|
32
|
+
if (orgFeatures.some((feature => feature.name === 'team-invite-acceptance' && feature.enabled))) {
|
|
33
33
|
const invitesResponse = await this.heroku.get(`/teams/${team}/invitations`, { headers: {
|
|
34
34
|
Accept: 'application/vnd.heroku+json; version=3.team-invitations',
|
|
35
35
|
},
|
|
@@ -10,7 +10,7 @@ class OrgsOpen extends command_1.Command {
|
|
|
10
10
|
await open(url);
|
|
11
11
|
}
|
|
12
12
|
async run() {
|
|
13
|
-
const { flags
|
|
13
|
+
const { flags } = await this.parse(OrgsOpen);
|
|
14
14
|
const team = flags.team;
|
|
15
15
|
const { body: org } = await this.heroku.get(`/teams/${team}`);
|
|
16
16
|
await OrgsOpen.openUrl(`https://dashboard.heroku.com/teams/${org.name}`);
|
package/lib/commands/pg/copy.js
CHANGED
|
@@ -38,7 +38,7 @@ class Copy extends command_1.Command {
|
|
|
38
38
|
const { flags, args } = await this.parse(Copy);
|
|
39
39
|
const { 'wait-interval': waitInterval, verbose, confirm, app } = flags;
|
|
40
40
|
const pgbackups = (0, backups_1.default)(app, this.heroku);
|
|
41
|
-
const interval = Math.max(3, Number.parseInt(waitInterval || '0')) || 3;
|
|
41
|
+
const interval = Math.max(3, Number.parseInt(waitInterval || '0', 10)) || 3;
|
|
42
42
|
const [source, target] = await Promise.all([getAttachmentInfo(this.heroku, args.source, app), getAttachmentInfo(this.heroku, args.target, app)]);
|
|
43
43
|
if (source.url === target.url)
|
|
44
44
|
throw new Error('Cannot copy database onto itself');
|
|
@@ -35,7 +35,7 @@ function isFailed(promotionTarget) {
|
|
|
35
35
|
}
|
|
36
36
|
function pollPromotionStatus(heroku, id, needsReleaseCommand) {
|
|
37
37
|
return heroku.get(`/pipeline-promotions/${id}/promotion-targets`).then(function ({ body: targets }) {
|
|
38
|
-
if (targets.every(isComplete)) {
|
|
38
|
+
if (targets.every(isComplete)) {
|
|
39
39
|
return targets;
|
|
40
40
|
}
|
|
41
41
|
//
|
|
@@ -98,7 +98,7 @@ async function getRelease(heroku, app, releaseId) {
|
|
|
98
98
|
return release;
|
|
99
99
|
}
|
|
100
100
|
async function streamReleaseCommand(heroku, targets, promotion) {
|
|
101
|
-
if (targets.length !== 1 || targets.every(isComplete)) {
|
|
101
|
+
if (targets.length !== 1 || targets.every(isComplete)) {
|
|
102
102
|
return pollPromotionStatus(heroku, promotion.id, false);
|
|
103
103
|
}
|
|
104
104
|
const target = targets[0];
|
|
@@ -190,7 +190,7 @@ class Promote extends command_1.Command {
|
|
|
190
190
|
memo[app.name] = details;
|
|
191
191
|
return memo;
|
|
192
192
|
}, {});
|
|
193
|
-
if (promotionTargets.every(isSucceeded)) {
|
|
193
|
+
if (promotionTargets.every(isSucceeded)) {
|
|
194
194
|
core_1.ux.log('\nPromotion successful');
|
|
195
195
|
}
|
|
196
196
|
else {
|
package/lib/commands/ps/type.js
CHANGED
|
@@ -51,7 +51,6 @@ const displayFormation = async (heroku, app) => {
|
|
|
51
51
|
}
|
|
52
52
|
return {
|
|
53
53
|
// this rule does not realize `size` isn't used on an array
|
|
54
|
-
/* eslint-disable unicorn/explicit-length-check */
|
|
55
54
|
type: color_1.default.green(d.type || ''),
|
|
56
55
|
size: color_1.default.cyan(d.size),
|
|
57
56
|
qty: color_1.default.yellow(`${d.quantity}`),
|
|
@@ -99,12 +98,12 @@ class Type extends command_1.Command {
|
|
|
99
98
|
if (!argv || argv.length === 0)
|
|
100
99
|
return [];
|
|
101
100
|
const { body: formation } = await this.heroku.get(`/apps/${app}/formation`);
|
|
102
|
-
if (argv.
|
|
101
|
+
if (argv.some(a => a.match(/=/))) {
|
|
103
102
|
return (0, lodash_1.compact)(argv.map(arg => {
|
|
104
103
|
const match = arg.match(/^([a-zA-Z0-9_]+)=([\w-]+)$/);
|
|
105
104
|
const type = match && match[1];
|
|
106
105
|
const size = match && match[2];
|
|
107
|
-
if (!type || !size || !formation.
|
|
106
|
+
if (!type || !size || !formation.some(p => p.type === type)) {
|
|
108
107
|
throw new Error(`Type ${color_1.default.red(type || '')} not found in process formation.\nTypes: ${color_1.default.yellow(formation.map(f => f.type)
|
|
109
108
|
.join(', '))}`);
|
|
110
109
|
}
|
|
@@ -4,15 +4,15 @@ export default class Create extends Command {
|
|
|
4
4
|
static description: string;
|
|
5
5
|
static examples: string[];
|
|
6
6
|
static flags: {
|
|
7
|
-
space: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
8
7
|
channel: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
9
|
-
region: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
10
|
-
features: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
11
|
-
'log-drain-url': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
12
|
-
shield: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
13
8
|
cidr: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
14
|
-
'kpi-url': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
15
9
|
'data-cidr': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
10
|
+
generation: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
11
|
+
'kpi-url': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
12
|
+
'log-drain-url': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
13
|
+
region: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
14
|
+
shield: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
15
|
+
space: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
16
16
|
team: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
17
17
|
};
|
|
18
18
|
static args: {
|
|
@@ -6,11 +6,10 @@ const core_1 = require("@oclif/core");
|
|
|
6
6
|
const tsheredoc_1 = require("tsheredoc");
|
|
7
7
|
const spaces_1 = require("../../lib/spaces/spaces");
|
|
8
8
|
const completions_1 = require("../../lib/autocomplete/completions");
|
|
9
|
-
const parsers_1 = require("../../lib/spaces/parsers");
|
|
10
9
|
class Create extends command_1.Command {
|
|
11
10
|
async run() {
|
|
12
11
|
const { flags, args } = await this.parse(Create);
|
|
13
|
-
const { channel, region, features, 'log-drain-url': logDrainUrl, shield, cidr, 'kpi-url': kpiUrl, 'data-cidr': dataCidr, team } = flags;
|
|
12
|
+
const { channel, region, features, generation, 'log-drain-url': logDrainUrl, shield, cidr, 'kpi-url': kpiUrl, 'data-cidr': dataCidr, team } = flags;
|
|
14
13
|
const spaceName = flags.space || args.space;
|
|
15
14
|
if (!spaceName) {
|
|
16
15
|
core_1.ux.error((0, tsheredoc_1.default) `
|
|
@@ -23,9 +22,21 @@ class Create extends command_1.Command {
|
|
|
23
22
|
const spaceType = shield ? 'Shield' : 'Standard';
|
|
24
23
|
core_1.ux.action.start(`Creating space ${color_1.default.green(spaceName)} in team ${color_1.default.cyan(team)}`);
|
|
25
24
|
const { body: space } = await this.heroku.post('/spaces', {
|
|
25
|
+
headers: {
|
|
26
|
+
Accept: 'application/vnd.heroku+json; version=3.sdk',
|
|
27
|
+
},
|
|
26
28
|
body: {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
channel_name: channel,
|
|
30
|
+
cidr,
|
|
31
|
+
data_cidr: dataCidr,
|
|
32
|
+
// features: splitCsv(features),
|
|
33
|
+
generation,
|
|
34
|
+
kpi_url: kpiUrl,
|
|
35
|
+
log_drain_url: logDrainUrl,
|
|
36
|
+
name: spaceName,
|
|
37
|
+
region,
|
|
38
|
+
shield,
|
|
39
|
+
team,
|
|
29
40
|
},
|
|
30
41
|
});
|
|
31
42
|
core_1.ux.action.stop();
|
|
@@ -33,8 +44,8 @@ class Create extends command_1.Command {
|
|
|
33
44
|
core_1.ux.warn(`Use ${color_1.default.cmd('heroku spaces:wait')} to track allocation.`);
|
|
34
45
|
core_1.ux.styledHeader(space.name);
|
|
35
46
|
core_1.ux.styledObject({
|
|
36
|
-
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), 'Created at': space.created_at,
|
|
37
|
-
}, ['ID', 'Team', 'Region', 'CIDR', 'Data CIDR', 'State', 'Shield', 'Created at']);
|
|
47
|
+
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,
|
|
48
|
+
}, ['ID', 'Team', 'Region', 'CIDR', 'Data CIDR', 'State', 'Shield', 'Generation', 'Created at']);
|
|
38
49
|
}
|
|
39
50
|
}
|
|
40
51
|
exports.default = Create;
|
|
@@ -57,15 +68,16 @@ Create.examples = [(0, tsheredoc_1.default) `
|
|
|
57
68
|
Created at: 2016-01-06T03:23:13Z
|
|
58
69
|
`];
|
|
59
70
|
Create.flags = {
|
|
60
|
-
space: command_1.flags.string({ char: 's', description: 'name of space to create' }),
|
|
61
71
|
channel: command_1.flags.string({ hidden: true }),
|
|
62
|
-
region: command_1.flags.string({ description: 'region name', completion: completions_1.RegionCompletion }),
|
|
63
|
-
features: command_1.flags.string({ hidden: true, description: 'a list of features separated by commas' }),
|
|
64
|
-
'log-drain-url': command_1.flags.string({ hidden: true, description: 'direct log drain url' }),
|
|
65
|
-
shield: command_1.flags.boolean({ hidden: true, description: 'create a Shield space' }),
|
|
66
72
|
cidr: command_1.flags.string({ description: 'RFC-1918 CIDR the space will use' }),
|
|
67
|
-
'kpi-url': command_1.flags.string({ hidden: true, description: 'self-managed KPI endpoint to use' }),
|
|
68
73
|
'data-cidr': command_1.flags.string({ description: 'RFC-1918 CIDR used by Heroku Data resources for the space' }),
|
|
74
|
+
// features: flags.string({hidden: true, description: 'a list of features separated by commas'}),
|
|
75
|
+
generation: command_1.flags.string({ description: 'generation for space', default: 'cedar', options: ['cedar', 'fir'] }),
|
|
76
|
+
'kpi-url': command_1.flags.string({ hidden: true, description: 'self-managed KPI endpoint to use' }),
|
|
77
|
+
'log-drain-url': command_1.flags.string({ hidden: true, description: 'direct log drain url' }),
|
|
78
|
+
region: command_1.flags.string({ description: 'region name', completion: completions_1.RegionCompletion }),
|
|
79
|
+
shield: command_1.flags.boolean({ hidden: true, description: 'create a Shield space' }),
|
|
80
|
+
space: command_1.flags.string({ char: 's', description: 'name of space to create' }),
|
|
69
81
|
team: command_1.flags.team({ required: true }),
|
|
70
82
|
};
|
|
71
83
|
Create.args = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command } from '@heroku-cli/command';
|
|
2
|
-
import
|
|
3
|
-
declare type SpaceArray = Array<Required<
|
|
2
|
+
import { Space } from '../../lib/types/fir';
|
|
3
|
+
declare type SpaceArray = Array<Required<Space>>;
|
|
4
4
|
export default class Index extends Command {
|
|
5
5
|
static topic: string;
|
|
6
6
|
static description: string;
|
|
@@ -7,7 +7,11 @@ class Index extends command_1.Command {
|
|
|
7
7
|
async run() {
|
|
8
8
|
const { flags } = await this.parse(Index);
|
|
9
9
|
const { team, json } = flags;
|
|
10
|
-
let { body: spaces } = await this.heroku.get('/spaces'
|
|
10
|
+
let { body: spaces } = await this.heroku.get('/spaces', {
|
|
11
|
+
headers: {
|
|
12
|
+
Accept: 'application/vnd.heroku+json; version=3.sdk',
|
|
13
|
+
},
|
|
14
|
+
});
|
|
11
15
|
if (team) {
|
|
12
16
|
spaces = spaces.filter(s => s.team.name === team);
|
|
13
17
|
}
|
|
@@ -39,6 +43,7 @@ class Index extends command_1.Command {
|
|
|
39
43
|
Team: { get: space => space.team.name },
|
|
40
44
|
Region: { get: space => space.region.name },
|
|
41
45
|
State: { get: space => space.state },
|
|
46
|
+
Generation: { get: space => space.generation },
|
|
42
47
|
createdAt: {
|
|
43
48
|
header: 'Created At',
|
|
44
49
|
get: space => space.created_at,
|
|
@@ -17,14 +17,16 @@ class Info extends command_1.Command {
|
|
|
17
17
|
See more help with --help
|
|
18
18
|
`));
|
|
19
19
|
}
|
|
20
|
-
|
|
20
|
+
const headers = {
|
|
21
|
+
Accept: 'application/vnd.heroku+json; version=3.fir',
|
|
22
|
+
};
|
|
21
23
|
if (!flags.json) {
|
|
22
|
-
headers
|
|
24
|
+
headers['Accept-Expansion'] = 'region';
|
|
23
25
|
}
|
|
24
26
|
const { body: space } = await this.heroku.get(`/spaces/${spaceName}`, { headers });
|
|
25
27
|
if (space.state === 'allocated') {
|
|
26
28
|
try {
|
|
27
|
-
const { body: outbound_ips } = await this.heroku.get(`/spaces/${spaceName}/nat
|
|
29
|
+
const { body: outbound_ips } = await this.heroku.get(`/spaces/${spaceName}/nat`, { headers: { Accept: 'application/vnd.heroku+json; version=3.fir' } });
|
|
28
30
|
space.outbound_ips = outbound_ips;
|
|
29
31
|
}
|
|
30
32
|
catch (error) {
|
|
@@ -25,9 +25,11 @@ class Wait extends command_1.Command {
|
|
|
25
25
|
const deadline = new Date(Date.now() + timeout);
|
|
26
26
|
const action = new spinner_1.default();
|
|
27
27
|
action.start(`Waiting for space ${color_1.default.green(spaceName)} to allocate`);
|
|
28
|
-
|
|
28
|
+
const headers = {
|
|
29
|
+
Accept: 'application/vnd.heroku+json; version=3.fir',
|
|
30
|
+
};
|
|
29
31
|
if (!flags.json) {
|
|
30
|
-
headers
|
|
32
|
+
headers['Accept-Expansion'] = 'region';
|
|
31
33
|
}
|
|
32
34
|
let { body: space } = await this.heroku.get(`/spaces/${spaceName}`, { headers });
|
|
33
35
|
while (space.state === 'allocating') {
|
|
@@ -39,7 +41,7 @@ class Wait extends command_1.Command {
|
|
|
39
41
|
space = updatedSpace;
|
|
40
42
|
}
|
|
41
43
|
try {
|
|
42
|
-
const { body: nat } = await this.heroku.get(`/spaces/${spaceName}/nat
|
|
44
|
+
const { body: nat } = await this.heroku.get(`/spaces/${spaceName}/nat`, { headers: { Accept: 'application/vnd.heroku+json; version=3.fir' } });
|
|
43
45
|
space.outbound_ips = nat;
|
|
44
46
|
}
|
|
45
47
|
catch (error) {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Command } from '@heroku-cli/command';
|
|
2
|
+
export default class Add extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static flags: {
|
|
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>;
|
|
7
|
+
space: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
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
|
+
transport: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
11
|
+
};
|
|
12
|
+
static args: {
|
|
13
|
+
headers: import("@oclif/core/lib/interfaces/parser").Arg<string, Record<string, unknown>>;
|
|
14
|
+
};
|
|
15
|
+
static example: string;
|
|
16
|
+
private getTypeAndName;
|
|
17
|
+
run(): Promise<void>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const command_1 = require("@heroku-cli/command");
|
|
4
|
+
const core_1 = require("@oclif/core");
|
|
5
|
+
const tsheredoc_1 = require("tsheredoc");
|
|
6
|
+
const util_1 = require("../../lib/telemetry/util");
|
|
7
|
+
class Add extends command_1.Command {
|
|
8
|
+
constructor() {
|
|
9
|
+
super(...arguments);
|
|
10
|
+
this.getTypeAndName = function (app, space) {
|
|
11
|
+
if (app) {
|
|
12
|
+
return { type: 'app', name: app };
|
|
13
|
+
}
|
|
14
|
+
return { type: 'space', name: space };
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
async run() {
|
|
18
|
+
const { flags, args } = await this.parse(Add);
|
|
19
|
+
const { app, space, signals, endpoint, transport } = flags;
|
|
20
|
+
const { headers } = args;
|
|
21
|
+
const typeAndName = this.getTypeAndName(app, space);
|
|
22
|
+
const drainConfig = {
|
|
23
|
+
owner: {
|
|
24
|
+
type: typeAndName.type,
|
|
25
|
+
id: typeAndName.name,
|
|
26
|
+
},
|
|
27
|
+
signals: (0, util_1.validateAndFormatSignals)(signals),
|
|
28
|
+
exporter: {
|
|
29
|
+
endpoint,
|
|
30
|
+
type: `otlp${transport}`,
|
|
31
|
+
headers: JSON.parse(headers),
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
if (app) {
|
|
35
|
+
const { body: drain } = await this.heroku.post(`/apps/${app}/telemetry-drains`, {
|
|
36
|
+
body: drainConfig,
|
|
37
|
+
headers: {
|
|
38
|
+
Accept: 'application/vnd.heroku+json; version=3.sdk',
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
core_1.ux.log(`successfully added drain ${drain.exporter.endpoint}`);
|
|
42
|
+
}
|
|
43
|
+
else if (space) {
|
|
44
|
+
const { body: drain } = await this.heroku.post(`/spaces/${space}/telemetry-drains`, {
|
|
45
|
+
body: drainConfig,
|
|
46
|
+
headers: {
|
|
47
|
+
Accept: 'application/vnd.heroku+json; version=3.sdk',
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
core_1.ux.log(`successfully added drain ${drain.exporter.endpoint}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.default = Add;
|
|
55
|
+
Add.description = 'Add and configure a new telemetry drain. Defaults to collecting all telemetry unless otherwise specified.';
|
|
56
|
+
Add.flags = {
|
|
57
|
+
app: command_1.flags.app({ exactlyOne: ['app', 'remote', 'space'], description: 'app to add a drain to' }),
|
|
58
|
+
remote: command_1.flags.remote({ description: 'git remote of app to add a drain to' }),
|
|
59
|
+
space: command_1.flags.string({ char: 's', description: 'space to add a drain to' }),
|
|
60
|
+
signals: command_1.flags.string({ default: 'all', description: 'comma-delimited list of signals to collect (traces, metrics, logs). Use "all" to collect all signals.' }),
|
|
61
|
+
endpoint: command_1.flags.string({ required: true, description: 'drain url' }),
|
|
62
|
+
transport: command_1.flags.string({ required: true, options: ['http', 'grpc'], description: 'transport protocol for the drain' }),
|
|
63
|
+
};
|
|
64
|
+
Add.args = {
|
|
65
|
+
headers: core_1.Args.string({ required: true, description: 'custom headers to configure the drain in json format' }),
|
|
66
|
+
};
|
|
67
|
+
Add.example = (0, tsheredoc_1.default)(`
|
|
68
|
+
Add a telemetry drain to an app to collect logs and traces:
|
|
69
|
+
$ heroku telemetry:add --signals logs,traces --endpoint https://my-endpoint.com --transport http 'x-drain-example-team: API_KEY x-drain-example-dataset: METRICS_DATASET'
|
|
70
|
+
`);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Command } from '@heroku-cli/command';
|
|
2
|
+
import { TelemetryDrains } from '../../lib/types/telemetry';
|
|
3
|
+
export default class Index extends Command {
|
|
4
|
+
static topic: string;
|
|
5
|
+
static description: string;
|
|
6
|
+
static flags: {
|
|
7
|
+
space: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
8
|
+
app: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
9
|
+
};
|
|
10
|
+
static example: string;
|
|
11
|
+
run(): Promise<void>;
|
|
12
|
+
protected display(telemetryDrains: TelemetryDrains, ownerType: 'App' | 'Space'): void;
|
|
13
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const command_1 = require("@heroku-cli/command");
|
|
4
|
+
const core_1 = require("@oclif/core");
|
|
5
|
+
class Index extends command_1.Command {
|
|
6
|
+
async run() {
|
|
7
|
+
const { flags } = await this.parse(Index);
|
|
8
|
+
const { app, space } = flags;
|
|
9
|
+
if (app) {
|
|
10
|
+
const { body: appTelemetryDrains } = await this.heroku.get(`/apps/${app}/telemetry-drains`, {
|
|
11
|
+
headers: {
|
|
12
|
+
Accept: 'application/vnd.heroku+json; version=3.sdk',
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
this.display(appTelemetryDrains, 'App');
|
|
16
|
+
}
|
|
17
|
+
else if (space) {
|
|
18
|
+
const { body: spaceTelemetryDrains } = await this.heroku.get(`/spaces/${space}/telemetry-drains`, {
|
|
19
|
+
headers: {
|
|
20
|
+
Accept: 'application/vnd.heroku+json; version=3.sdk',
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
this.display(spaceTelemetryDrains, 'Space');
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
display(telemetryDrains, ownerType) {
|
|
27
|
+
core_1.ux.styledHeader(`${ownerType} Telemetry Drains`);
|
|
28
|
+
core_1.ux.table(telemetryDrains, {
|
|
29
|
+
ID: { get: telemetryDrain => telemetryDrain.id },
|
|
30
|
+
Signals: { get: telemetryDrain => telemetryDrain.signals },
|
|
31
|
+
Endpoint: { get: telemetryDrain => telemetryDrain.exporter.endpoint },
|
|
32
|
+
[ownerType]: { get: telemetryDrain => telemetryDrain.owner.name },
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.default = Index;
|
|
37
|
+
Index.topic = 'telemetry';
|
|
38
|
+
Index.description = 'list telemetry drains';
|
|
39
|
+
Index.flags = {
|
|
40
|
+
space: command_1.flags.string({ char: 's', description: 'filter by space name', exactlyOne: ['app', 'space'] }),
|
|
41
|
+
app: command_1.flags.app({ description: 'filter by app name' }),
|
|
42
|
+
};
|
|
43
|
+
Index.example = '$ heroku telemetry';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Command } from '@heroku-cli/command';
|
|
2
|
+
export default class Info extends Command {
|
|
3
|
+
static topic: string;
|
|
4
|
+
static description: string;
|
|
5
|
+
static args: {
|
|
6
|
+
telemetry_drain_id: import("@oclif/core/lib/interfaces/parser").Arg<string, Record<string, unknown>>;
|
|
7
|
+
};
|
|
8
|
+
static example: string;
|
|
9
|
+
run(): Promise<void>;
|
|
10
|
+
}
|