heroku 8.2.1-beta.0 → 8.3.1-beta.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 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
- let error = new Error('Received SIGINT')
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
- let error = new Error('Received SIGTERM')
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
- let cliError = error
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 sendToRollbar(data: any): Promise<void>;
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 {};
@@ -1,9 +1,23 @@
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 root = require('../package.json');
6
16
  const isDev = process.env.IS_DEV_ENVIRONMENT === 'true';
17
+ const { version } = root;
18
+ const config = new core_1.Config({ root });
19
+ const heroku = new command_1.APIClient(config);
20
+ const token = heroku.auth;
7
21
  const debug = require('debug')('global_telemetry');
8
22
  const rollbar = new Rollbar({
9
23
  accessToken: '41f8730238814af69c248e2f7ca59ff2',
@@ -11,24 +25,71 @@ const rollbar = new Rollbar({
11
25
  captureUnhandledRejections: true,
12
26
  environment: isDev ? 'development' : 'production',
13
27
  });
28
+ registerInstrumentations({
29
+ instrumentations: [],
30
+ });
31
+ const resource = Resource
32
+ .default()
33
+ .merge(new Resource({
34
+ [SemanticResourceAttributes.SERVICE_NAME]: 'heroku-cli',
35
+ [SemanticResourceAttributes.SERVICE_VERSION]: version,
36
+ }));
37
+ const provider = new NodeTracerProvider({
38
+ resource,
39
+ });
40
+ const headers = { Authorization: `Bearer ${token}` };
41
+ const exporter = new OTLPTraceExporter({
42
+ url: isDev ? 'https://backboard-staging.herokuapp.com/otel/v1/traces' : 'https://backboard.heroku.com/otel/v1/traces',
43
+ headers,
44
+ compression: 'none',
45
+ });
46
+ exports.processor = new BatchSpanProcessor(exporter);
47
+ provider.addSpanProcessor(exports.processor);
48
+ function initializeInstrumentation() {
49
+ provider.register();
50
+ }
51
+ exports.initializeInstrumentation = initializeInstrumentation;
14
52
  function setupTelemetry(config, opts) {
15
53
  const now = new Date();
16
54
  const cmdStartTime = now.getTime();
17
- return {
18
- command: opts.Command.id,
19
- os: config.platform,
20
- version: config.version,
21
- exitCode: 0,
22
- exitState: [''],
23
- cliRunDuration: 0,
24
- commandRunDuration: cmdStartTime,
25
- lifecycleHookCompletion: {
26
- init: true,
27
- prerun: true,
28
- postrun: false,
29
- command_not_found: false,
30
- },
31
- };
55
+ const isHelpOrVersionCmd = (opts.id === 'version' || opts.id === '--help');
56
+ const isRegularCmd = Boolean(opts.Command);
57
+ if (isHelpOrVersionCmd) {
58
+ return {
59
+ command: opts.id,
60
+ os: config.platform,
61
+ version: config.version,
62
+ exitCode: 0,
63
+ exitState: 'successful',
64
+ cliRunDuration: 0,
65
+ commandRunDuration: cmdStartTime,
66
+ lifecycleHookCompletion: {
67
+ init: true,
68
+ prerun: false,
69
+ postrun: false,
70
+ command_not_found: false,
71
+ },
72
+ isVersionOrHelp: true,
73
+ };
74
+ }
75
+ if (isRegularCmd) {
76
+ return {
77
+ command: opts.Command.id,
78
+ os: config.platform,
79
+ version: config.version,
80
+ exitCode: 0,
81
+ exitState: 'successful',
82
+ cliRunDuration: 0,
83
+ commandRunDuration: cmdStartTime,
84
+ lifecycleHookCompletion: {
85
+ init: true,
86
+ prerun: true,
87
+ postrun: false,
88
+ command_not_found: false,
89
+ },
90
+ isVersionOrHelp: false,
91
+ };
92
+ }
32
93
  }
33
94
  exports.setupTelemetry = setupTelemetry;
34
95
  function computeDuration(cmdStartTime) {
@@ -40,11 +101,11 @@ function computeDuration(cmdStartTime) {
40
101
  exports.computeDuration = computeDuration;
41
102
  function reportCmdNotFound(config) {
42
103
  return {
43
- command: '',
104
+ command: 'invalid_command',
44
105
  os: config.platform,
45
106
  version: config.version,
46
107
  exitCode: 0,
47
- exitState: ['command_not_found'],
108
+ exitState: 'command_not_found',
48
109
  cliRunDuration: 0,
49
110
  commandRunDuration: 0,
50
111
  lifecycleHookCompletion: {
@@ -53,30 +114,81 @@ function reportCmdNotFound(config) {
53
114
  postrun: false,
54
115
  command_not_found: true,
55
116
  },
117
+ isVersionOrHelp: false,
56
118
  };
57
119
  }
58
120
  exports.reportCmdNotFound = reportCmdNotFound;
59
- async function sendTelemetry(currentTelemetry) {
121
+ async function sendTelemetry(currentTelemetry, rollbarCb) {
60
122
  // send telemetry to honeycomb and rollbar
61
- let telemetry = currentTelemetry;
123
+ const telemetry = currentTelemetry;
62
124
  if (telemetry instanceof Error) {
63
- telemetry = { error_message: telemetry.message, error_stack: telemetry.stack };
64
- telemetry.cliRunDuration = currentTelemetry.cliRunDuration;
65
- await sendToRollbar(telemetry);
125
+ await Promise.all([
126
+ sendToRollbar(telemetry, rollbarCb),
127
+ sendToHoneycomb(telemetry),
128
+ ]);
129
+ }
130
+ else {
131
+ await sendToHoneycomb(telemetry);
66
132
  }
67
- // add sendToHoneycomb function here
68
133
  }
69
134
  exports.sendTelemetry = sendTelemetry;
70
- async function sendToRollbar(data) {
135
+ async function sendToHoneycomb(data) {
136
+ try {
137
+ const tracer = api_1.default.trace.getTracer('heroku-cli', version);
138
+ const span = tracer.startSpan('node_app_execution');
139
+ if (data instanceof Error) {
140
+ span.recordException(data);
141
+ span.setStatus({
142
+ code: api_1.SpanStatusCode.ERROR,
143
+ message: data.message,
144
+ });
145
+ }
146
+ else {
147
+ span.setAttribute('heroku_client.command', data.command);
148
+ span.setAttribute('heroku_client.os', data.os);
149
+ span.setAttribute('heroku_client.version', data.version);
150
+ span.setAttribute('heroku_client.exit_code', data.exitCode);
151
+ span.setAttribute('heroku_client.exit_state', data.exitState);
152
+ span.setAttribute('heroku_client.cli_run_duration', data.cliRunDuration);
153
+ span.setAttribute('heroku_client.command_run_duration', data.commandRunDuration);
154
+ span.setAttribute('heroku_client.lifecycle_hook.init', data.lifecycleHookCompletion.init);
155
+ span.setAttribute('heroku_client.lifecycle_hook.prerun', data.lifecycleHookCompletion.prerun);
156
+ span.setAttribute('heroku_client.lifecycle_hook.postrun', data.lifecycleHookCompletion.postrun);
157
+ span.setAttribute('heroku_client.lifecycle_hook.command_not_found', data.lifecycleHookCompletion.command_not_found);
158
+ }
159
+ span.end();
160
+ exports.processor.forceFlush();
161
+ }
162
+ catch (_a) {
163
+ debug('could not send telemetry');
164
+ }
165
+ }
166
+ exports.sendToHoneycomb = sendToHoneycomb;
167
+ async function sendToRollbar(data, rollbarCb) {
168
+ // Make this awaitable so we can wait for it to finish before exiting
169
+ let promiseResolve;
170
+ const rollbarPromise = new Promise((resolve, reject) => {
171
+ promiseResolve = () => {
172
+ if (rollbarCb) {
173
+ try {
174
+ rollbarCb();
175
+ }
176
+ catch (error) {
177
+ reject(error);
178
+ }
179
+ }
180
+ resolve(null);
181
+ };
182
+ });
183
+ const rollbarError = { name: data.name, message: data.message, stack: data.stack, cli_run_duration: data.cliRunDuration };
71
184
  try {
72
185
  // send data to rollbar
73
- rollbar.error('Failed to complete execution', data, () => {
74
- process.exit(1);
75
- });
186
+ rollbar.error('Failed to complete execution', rollbarError, promiseResolve);
76
187
  }
77
188
  catch (_a) {
78
189
  debug('Could not send error report');
79
- process.exit(1);
190
+ return Promise.reject();
80
191
  }
192
+ return rollbarPromise;
81
193
  }
82
194
  exports.sendToRollbar = sendToRollbar;
@@ -0,0 +1,3 @@
1
+ import { Hook } from '@oclif/core';
2
+ declare const performance_analytics: Hook<'init'>;
3
+ export default performance_analytics;
@@ -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;
@@ -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)
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "8.2.1-beta.0",
2
+ "version": "8.3.1-beta.0",
3
3
  "commands": {
4
4
  "console": {
5
5
  "id": "console",
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.2.1-beta.0",
4
+ "version": "8.3.1-beta.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",
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": "01134e64e08279e1e03a5ed475572df68f6136c8"
335
+ "gitHead": "149a9a3d83a99cc1ba49905f813dbec454f7c2b1"
328
336
  }