heroku 8.2.1-beta.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/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/lib/hooks/update/brew.js +0 -1
- package/oclif.manifest.json +1 -1
- package/package.json +12 -4
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/lib/hooks/update/brew.js
CHANGED
|
@@ -24,7 +24,6 @@ const brewHook = async function () {
|
|
|
24
24
|
let cellarPath;
|
|
25
25
|
if (binPath && binPath.startsWith(path.join(brewRoot, 'Cellar'))) {
|
|
26
26
|
cellarPath = path.resolve(binPath, path.dirname(path.relative(binPath, path.join(brewRoot, 'Cellar/heroku'))));
|
|
27
|
-
console.error('brew update path:', cellarPath);
|
|
28
27
|
}
|
|
29
28
|
const fetchInstallReceipt = async () => {
|
|
30
29
|
if (!cellarPath)
|
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",
|
|
@@ -29,10 +29,17 @@
|
|
|
29
29
|
"@oclif/plugin-legacy": "^1.3.0",
|
|
30
30
|
"@oclif/plugin-not-found": "2.3.16",
|
|
31
31
|
"@oclif/plugin-plugins": "2.4.3",
|
|
32
|
-
"@oclif/plugin-update": "3.1.
|
|
32
|
+
"@oclif/plugin-update": "3.1.10",
|
|
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
|
}
|