heroku 8.2.0 → 8.3.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 +3 -0
- package/bin/run +12 -3
- package/lib/global_telemetry.d.ts +15 -6
- package/lib/global_telemetry.js +143 -30
- package/lib/hooks/init/performance_analytics.d.ts +3 -0
- package/lib/hooks/init/performance_analytics.js +7 -0
- package/oclif.manifest.json +1 -1
- package/package.json +11 -3
package/README.md
CHANGED
|
@@ -56,13 +56,16 @@ For other issues, [submit a support ticket](https://help.heroku.com/).
|
|
|
56
56
|
* [`heroku logs`](docs/logs.md) - display recent log output
|
|
57
57
|
* [`heroku maintenance`](docs/maintenance.md) - enable/disable access to app
|
|
58
58
|
* [`heroku members`](docs/members.md) - manage organization members
|
|
59
|
+
* [`heroku notifications`](docs/notifications.md) - display notifications
|
|
59
60
|
* [`heroku orgs`](docs/orgs.md) - manage organizations
|
|
60
61
|
* [`heroku pg`](docs/pg.md) - manage postgresql databases
|
|
61
62
|
* [`heroku pipelines`](docs/pipelines.md) - list pipelines you have access to
|
|
62
63
|
* [`heroku plugins`](docs/plugins.md) - List installed plugins.
|
|
63
64
|
* [`heroku ps`](docs/ps.md) - Client tools for Heroku Exec
|
|
65
|
+
* [`heroku psql`](docs/psql.md) - open a psql shell to the database
|
|
64
66
|
* [`heroku redis`](docs/redis.md) - manage heroku redis instances
|
|
65
67
|
* [`heroku regions`](docs/regions.md) - list available regions for deployment
|
|
68
|
+
* [`heroku releases`](docs/releases.md) - display the releases for an app
|
|
66
69
|
* [`heroku reviewapps`](docs/reviewapps.md) - disable review apps and/or settings on an existing pipeline
|
|
67
70
|
* [`heroku run`](docs/run.md) - run a one-off process inside a Heroku dyno
|
|
68
71
|
* [`heroku sessions`](docs/sessions.md) - OAuth sessions
|
package/bin/run
CHANGED
|
@@ -10,6 +10,11 @@ const globalTelemetry = require('../lib/global_telemetry')
|
|
|
10
10
|
|
|
11
11
|
process.once('beforeExit', async code => {
|
|
12
12
|
// capture as successful exit
|
|
13
|
+
if (global.cliTelemetry.isVersionOrHelp) {
|
|
14
|
+
const cmdStartTime = global.cliTelemetry.commandRunDuration
|
|
15
|
+
global.cliTelemetry.commandRunDuration = globalTelemetry.computeDuration(cmdStartTime)
|
|
16
|
+
}
|
|
17
|
+
|
|
13
18
|
global.cliTelemetry.exitCode = code
|
|
14
19
|
global.cliTelemetry.cliRunDuration = globalTelemetry.computeDuration(cliStartTime)
|
|
15
20
|
const telemetryData = global.cliTelemetry
|
|
@@ -18,23 +23,27 @@ process.once('beforeExit', async code => {
|
|
|
18
23
|
|
|
19
24
|
process.on('SIGINT', async () => {
|
|
20
25
|
// capture as unsuccessful exit
|
|
21
|
-
|
|
26
|
+
const error = new Error('Received SIGINT')
|
|
22
27
|
error.cliRunDuration = globalTelemetry.computeDuration(cliStartTime)
|
|
23
28
|
await globalTelemetry.sendTelemetry(error)
|
|
29
|
+
process.exit(1)
|
|
24
30
|
})
|
|
25
31
|
|
|
26
32
|
process.on('SIGTERM', async () => {
|
|
27
33
|
// capture as unsuccessful exit
|
|
28
|
-
|
|
34
|
+
const error = new Error('Received SIGTERM')
|
|
29
35
|
error.cliRunDuration = globalTelemetry.computeDuration(cliStartTime)
|
|
30
36
|
await globalTelemetry.sendTelemetry(error)
|
|
37
|
+
process.exit(1)
|
|
31
38
|
})
|
|
32
39
|
|
|
40
|
+
globalTelemetry.initializeInstrumentation()
|
|
41
|
+
|
|
33
42
|
const oclif = require('@oclif/core')
|
|
34
43
|
|
|
35
44
|
oclif.run().then(require('@oclif/core/flush')).catch(async error => {
|
|
36
45
|
// capture any errors raised by oclif
|
|
37
|
-
|
|
46
|
+
const cliError = error
|
|
38
47
|
cliError.cliRunDuration = globalTelemetry.computeDuration(cliStartTime)
|
|
39
48
|
await globalTelemetry.sendTelemetry(cliError)
|
|
40
49
|
console.log(`Error: ${error.message}`)
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import 'dotenv/config';
|
|
2
|
+
export declare const processor: any;
|
|
2
3
|
interface Telemetry {
|
|
3
4
|
command: string;
|
|
4
5
|
os: string;
|
|
5
6
|
version: string;
|
|
6
7
|
exitCode: number;
|
|
7
|
-
exitState: string
|
|
8
|
+
exitState: string;
|
|
8
9
|
cliRunDuration: number;
|
|
9
10
|
commandRunDuration: number;
|
|
10
11
|
lifecycleHookCompletion: {
|
|
@@ -13,16 +14,21 @@ interface Telemetry {
|
|
|
13
14
|
postrun: boolean;
|
|
14
15
|
command_not_found: boolean;
|
|
15
16
|
};
|
|
17
|
+
isVersionOrHelp: boolean;
|
|
16
18
|
}
|
|
17
19
|
export interface TelemetryGlobal extends NodeJS.Global {
|
|
18
20
|
cliTelemetry?: Telemetry;
|
|
19
21
|
}
|
|
22
|
+
interface CLIError extends Error {
|
|
23
|
+
cliRunDuration?: string;
|
|
24
|
+
}
|
|
25
|
+
export declare function initializeInstrumentation(): void;
|
|
20
26
|
export declare function setupTelemetry(config: any, opts: any): {
|
|
21
27
|
command: any;
|
|
22
28
|
os: any;
|
|
23
29
|
version: any;
|
|
24
30
|
exitCode: number;
|
|
25
|
-
exitState: string
|
|
31
|
+
exitState: string;
|
|
26
32
|
cliRunDuration: number;
|
|
27
33
|
commandRunDuration: number;
|
|
28
34
|
lifecycleHookCompletion: {
|
|
@@ -31,14 +37,15 @@ export declare function setupTelemetry(config: any, opts: any): {
|
|
|
31
37
|
postrun: boolean;
|
|
32
38
|
command_not_found: boolean;
|
|
33
39
|
};
|
|
34
|
-
|
|
40
|
+
isVersionOrHelp: boolean;
|
|
41
|
+
} | undefined;
|
|
35
42
|
export declare function computeDuration(cmdStartTime: any): number;
|
|
36
43
|
export declare function reportCmdNotFound(config: any): {
|
|
37
44
|
command: string;
|
|
38
45
|
os: any;
|
|
39
46
|
version: any;
|
|
40
47
|
exitCode: number;
|
|
41
|
-
exitState: string
|
|
48
|
+
exitState: string;
|
|
42
49
|
cliRunDuration: number;
|
|
43
50
|
commandRunDuration: number;
|
|
44
51
|
lifecycleHookCompletion: {
|
|
@@ -47,7 +54,9 @@ export declare function reportCmdNotFound(config: any): {
|
|
|
47
54
|
postrun: boolean;
|
|
48
55
|
command_not_found: boolean;
|
|
49
56
|
};
|
|
57
|
+
isVersionOrHelp: boolean;
|
|
50
58
|
};
|
|
51
|
-
export declare function sendTelemetry(currentTelemetry: any): Promise<void>;
|
|
52
|
-
export declare function
|
|
59
|
+
export declare function sendTelemetry(currentTelemetry: any, rollbarCb?: () => void): Promise<void>;
|
|
60
|
+
export declare function sendToHoneycomb(data: Telemetry | CLIError): Promise<void>;
|
|
61
|
+
export declare function sendToRollbar(data: CLIError, rollbarCb?: () => void): Promise<unknown>;
|
|
53
62
|
export {};
|
package/lib/global_telemetry.js
CHANGED
|
@@ -1,9 +1,24 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.sendToRollbar = exports.sendTelemetry = exports.reportCmdNotFound = exports.computeDuration = exports.setupTelemetry = void 0;
|
|
4
|
-
const Rollbar = require("rollbar");
|
|
3
|
+
exports.sendToRollbar = exports.sendToHoneycomb = exports.sendTelemetry = exports.reportCmdNotFound = exports.computeDuration = exports.setupTelemetry = exports.initializeInstrumentation = exports.processor = void 0;
|
|
5
4
|
require("dotenv/config");
|
|
5
|
+
const Rollbar = require("rollbar");
|
|
6
|
+
const command_1 = require("@heroku-cli/command");
|
|
7
|
+
const core_1 = require("@oclif/core");
|
|
8
|
+
const api_1 = require("@opentelemetry/api");
|
|
9
|
+
const { Resource } = require('@opentelemetry/resources');
|
|
10
|
+
const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions');
|
|
11
|
+
const { registerInstrumentations } = require('@opentelemetry/instrumentation');
|
|
12
|
+
const { NodeTracerProvider } = require('@opentelemetry/sdk-trace-node');
|
|
13
|
+
const { BatchSpanProcessor } = require('@opentelemetry/sdk-trace-base');
|
|
14
|
+
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');
|
|
15
|
+
const { version } = require('../../../packages/cli/package.json');
|
|
6
16
|
const isDev = process.env.IS_DEV_ENVIRONMENT === 'true';
|
|
17
|
+
const path = require('path');
|
|
18
|
+
const root = path.resolve(__dirname, '../../../package.json');
|
|
19
|
+
const config = new core_1.Config({ root });
|
|
20
|
+
const heroku = new command_1.APIClient(config);
|
|
21
|
+
const token = heroku.auth;
|
|
7
22
|
const debug = require('debug')('global_telemetry');
|
|
8
23
|
const rollbar = new Rollbar({
|
|
9
24
|
accessToken: '41f8730238814af69c248e2f7ca59ff2',
|
|
@@ -11,24 +26,71 @@ const rollbar = new Rollbar({
|
|
|
11
26
|
captureUnhandledRejections: true,
|
|
12
27
|
environment: isDev ? 'development' : 'production',
|
|
13
28
|
});
|
|
29
|
+
registerInstrumentations({
|
|
30
|
+
instrumentations: [],
|
|
31
|
+
});
|
|
32
|
+
const resource = Resource
|
|
33
|
+
.default()
|
|
34
|
+
.merge(new Resource({
|
|
35
|
+
[SemanticResourceAttributes.SERVICE_NAME]: 'heroku-cli',
|
|
36
|
+
[SemanticResourceAttributes.SERVICE_VERSION]: version,
|
|
37
|
+
}));
|
|
38
|
+
const provider = new NodeTracerProvider({
|
|
39
|
+
resource,
|
|
40
|
+
});
|
|
41
|
+
const headers = { Authorization: `Bearer ${token}` };
|
|
42
|
+
const exporter = new OTLPTraceExporter({
|
|
43
|
+
url: isDev ? 'https://backboard-staging.herokuapp.com/otel/v1/traces' : 'https://backboard.heroku.com/otel/v1/traces',
|
|
44
|
+
headers,
|
|
45
|
+
compression: 'none',
|
|
46
|
+
});
|
|
47
|
+
exports.processor = new BatchSpanProcessor(exporter);
|
|
48
|
+
provider.addSpanProcessor(exports.processor);
|
|
49
|
+
function initializeInstrumentation() {
|
|
50
|
+
provider.register();
|
|
51
|
+
}
|
|
52
|
+
exports.initializeInstrumentation = initializeInstrumentation;
|
|
14
53
|
function setupTelemetry(config, opts) {
|
|
15
54
|
const now = new Date();
|
|
16
55
|
const cmdStartTime = now.getTime();
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
56
|
+
const isHelpOrVersionCmd = (opts.id === 'version' || opts.id === '--help');
|
|
57
|
+
const isRegularCmd = Boolean(opts.Command);
|
|
58
|
+
if (isHelpOrVersionCmd) {
|
|
59
|
+
return {
|
|
60
|
+
command: opts.id,
|
|
61
|
+
os: config.platform,
|
|
62
|
+
version: config.version,
|
|
63
|
+
exitCode: 0,
|
|
64
|
+
exitState: 'successful',
|
|
65
|
+
cliRunDuration: 0,
|
|
66
|
+
commandRunDuration: cmdStartTime,
|
|
67
|
+
lifecycleHookCompletion: {
|
|
68
|
+
init: true,
|
|
69
|
+
prerun: false,
|
|
70
|
+
postrun: false,
|
|
71
|
+
command_not_found: false,
|
|
72
|
+
},
|
|
73
|
+
isVersionOrHelp: true,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
if (isRegularCmd) {
|
|
77
|
+
return {
|
|
78
|
+
command: opts.Command.id,
|
|
79
|
+
os: config.platform,
|
|
80
|
+
version: config.version,
|
|
81
|
+
exitCode: 0,
|
|
82
|
+
exitState: 'successful',
|
|
83
|
+
cliRunDuration: 0,
|
|
84
|
+
commandRunDuration: cmdStartTime,
|
|
85
|
+
lifecycleHookCompletion: {
|
|
86
|
+
init: true,
|
|
87
|
+
prerun: true,
|
|
88
|
+
postrun: false,
|
|
89
|
+
command_not_found: false,
|
|
90
|
+
},
|
|
91
|
+
isVersionOrHelp: false,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
32
94
|
}
|
|
33
95
|
exports.setupTelemetry = setupTelemetry;
|
|
34
96
|
function computeDuration(cmdStartTime) {
|
|
@@ -40,11 +102,11 @@ function computeDuration(cmdStartTime) {
|
|
|
40
102
|
exports.computeDuration = computeDuration;
|
|
41
103
|
function reportCmdNotFound(config) {
|
|
42
104
|
return {
|
|
43
|
-
command: '',
|
|
105
|
+
command: 'invalid_command',
|
|
44
106
|
os: config.platform,
|
|
45
107
|
version: config.version,
|
|
46
108
|
exitCode: 0,
|
|
47
|
-
exitState:
|
|
109
|
+
exitState: 'command_not_found',
|
|
48
110
|
cliRunDuration: 0,
|
|
49
111
|
commandRunDuration: 0,
|
|
50
112
|
lifecycleHookCompletion: {
|
|
@@ -53,30 +115,81 @@ function reportCmdNotFound(config) {
|
|
|
53
115
|
postrun: false,
|
|
54
116
|
command_not_found: true,
|
|
55
117
|
},
|
|
118
|
+
isVersionOrHelp: false,
|
|
56
119
|
};
|
|
57
120
|
}
|
|
58
121
|
exports.reportCmdNotFound = reportCmdNotFound;
|
|
59
|
-
async function sendTelemetry(currentTelemetry) {
|
|
122
|
+
async function sendTelemetry(currentTelemetry, rollbarCb) {
|
|
60
123
|
// send telemetry to honeycomb and rollbar
|
|
61
|
-
|
|
124
|
+
const telemetry = currentTelemetry;
|
|
62
125
|
if (telemetry instanceof Error) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
126
|
+
await Promise.all([
|
|
127
|
+
sendToRollbar(telemetry, rollbarCb),
|
|
128
|
+
sendToHoneycomb(telemetry),
|
|
129
|
+
]);
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
await sendToHoneycomb(telemetry);
|
|
66
133
|
}
|
|
67
|
-
// add sendToHoneycomb function here
|
|
68
134
|
}
|
|
69
135
|
exports.sendTelemetry = sendTelemetry;
|
|
70
|
-
async function
|
|
136
|
+
async function sendToHoneycomb(data) {
|
|
137
|
+
try {
|
|
138
|
+
const tracer = api_1.default.trace.getTracer('heroku-cli', version);
|
|
139
|
+
const span = tracer.startSpan('node_app_execution');
|
|
140
|
+
if (data instanceof Error) {
|
|
141
|
+
span.recordException(data);
|
|
142
|
+
span.setStatus({
|
|
143
|
+
code: api_1.SpanStatusCode.ERROR,
|
|
144
|
+
message: data.message,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
span.setAttribute('heroku_client.command', data.command);
|
|
149
|
+
span.setAttribute('heroku_client.os', data.os);
|
|
150
|
+
span.setAttribute('heroku_client.version', data.version);
|
|
151
|
+
span.setAttribute('heroku_client.exit_code', data.exitCode);
|
|
152
|
+
span.setAttribute('heroku_client.exit_state', data.exitState);
|
|
153
|
+
span.setAttribute('heroku_client.cli_run_duration', data.cliRunDuration);
|
|
154
|
+
span.setAttribute('heroku_client.command_run_duration', data.commandRunDuration);
|
|
155
|
+
span.setAttribute('heroku_client.lifecycle_hook.init', data.lifecycleHookCompletion.init);
|
|
156
|
+
span.setAttribute('heroku_client.lifecycle_hook.prerun', data.lifecycleHookCompletion.prerun);
|
|
157
|
+
span.setAttribute('heroku_client.lifecycle_hook.postrun', data.lifecycleHookCompletion.postrun);
|
|
158
|
+
span.setAttribute('heroku_client.lifecycle_hook.command_not_found', data.lifecycleHookCompletion.command_not_found);
|
|
159
|
+
}
|
|
160
|
+
span.end();
|
|
161
|
+
exports.processor.forceFlush();
|
|
162
|
+
}
|
|
163
|
+
catch (_a) {
|
|
164
|
+
debug('could not send telemetry');
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
exports.sendToHoneycomb = sendToHoneycomb;
|
|
168
|
+
async function sendToRollbar(data, rollbarCb) {
|
|
169
|
+
// Make this awaitable so we can wait for it to finish before exiting
|
|
170
|
+
let promiseResolve;
|
|
171
|
+
const rollbarPromise = new Promise((resolve, reject) => {
|
|
172
|
+
promiseResolve = () => {
|
|
173
|
+
if (rollbarCb) {
|
|
174
|
+
try {
|
|
175
|
+
rollbarCb();
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
reject(error);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
resolve(null);
|
|
182
|
+
};
|
|
183
|
+
});
|
|
184
|
+
const rollbarError = { name: data.name, message: data.message, stack: data.stack, cli_run_duration: data.cliRunDuration };
|
|
71
185
|
try {
|
|
72
186
|
// send data to rollbar
|
|
73
|
-
rollbar.error('Failed to complete execution',
|
|
74
|
-
process.exit(1);
|
|
75
|
-
});
|
|
187
|
+
rollbar.error('Failed to complete execution', rollbarError, promiseResolve);
|
|
76
188
|
}
|
|
77
189
|
catch (_a) {
|
|
78
190
|
debug('Could not send error report');
|
|
79
|
-
|
|
191
|
+
return Promise.reject();
|
|
80
192
|
}
|
|
193
|
+
return rollbarPromise;
|
|
81
194
|
}
|
|
82
195
|
exports.sendToRollbar = sendToRollbar;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const telemetry = require("../../global_telemetry");
|
|
4
|
+
const performance_analytics = async function (options) {
|
|
5
|
+
global.cliTelemetry = telemetry.setupTelemetry(this.config, options);
|
|
6
|
+
};
|
|
7
|
+
exports.default = performance_analytics;
|
package/oclif.manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "heroku",
|
|
3
3
|
"description": "CLI to interact with Heroku",
|
|
4
|
-
"version": "8.
|
|
4
|
+
"version": "8.3.0",
|
|
5
5
|
"author": "Jeff Dickey @jdxcode",
|
|
6
6
|
"bin": "./bin/run",
|
|
7
7
|
"bugs": "https://github.com/heroku/cli/issues",
|
|
@@ -33,6 +33,13 @@
|
|
|
33
33
|
"@oclif/plugin-version": "^1.2.1",
|
|
34
34
|
"@oclif/plugin-warn-if-update-available": "2.0.29",
|
|
35
35
|
"@oclif/plugin-which": "2.2.8",
|
|
36
|
+
"@opentelemetry/api": "^1.4.1",
|
|
37
|
+
"@opentelemetry/exporter-trace-otlp-http": "^0.41.1",
|
|
38
|
+
"@opentelemetry/instrumentation": "^0.41.1",
|
|
39
|
+
"@opentelemetry/resources": "^1.15.1",
|
|
40
|
+
"@opentelemetry/sdk-trace-base": "^1.15.1",
|
|
41
|
+
"@opentelemetry/sdk-trace-node": "^1.15.1",
|
|
42
|
+
"@opentelemetry/semantic-conventions": "^1.15.1",
|
|
36
43
|
"ansi-escapes": "3.2.0",
|
|
37
44
|
"async-file": "^2.0.2",
|
|
38
45
|
"chalk": "^2.4.2",
|
|
@@ -254,7 +261,8 @@
|
|
|
254
261
|
"hooks": {
|
|
255
262
|
"init": [
|
|
256
263
|
"./lib/hooks/init/version",
|
|
257
|
-
"./lib/hooks/init/terms-of-service"
|
|
264
|
+
"./lib/hooks/init/terms-of-service",
|
|
265
|
+
"./lib/hooks/init/performance_analytics"
|
|
258
266
|
],
|
|
259
267
|
"prerun": [
|
|
260
268
|
"./lib/hooks/prerun/analytics"
|
|
@@ -324,5 +332,5 @@
|
|
|
324
332
|
"version": "oclif readme --multi && git add README.md ../../docs"
|
|
325
333
|
},
|
|
326
334
|
"types": "lib/index.d.ts",
|
|
327
|
-
"gitHead": "
|
|
335
|
+
"gitHead": "16f4f8e933025276b5fc703316c3221149c29eb6"
|
|
328
336
|
}
|