@automattic/vip 2.7.0-dev1 → 2.8.0-alpha

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 CHANGED
@@ -27,6 +27,13 @@ By default, we record information about the usage of this tool using an in-house
27
27
 
28
28
  ## Changelog
29
29
 
30
+ ### 2.7.0 (07 December 2021)
31
+ - #941 [dev-env] Bump lando CLI dependency
32
+ - #938 Hide roll back message after SQL Import failure for launched sites
33
+ - #936 Sets jest maxWorkers to 4
34
+
35
+ https://github.com/Automattic/vip/releases/tag/v2.7.0
36
+
30
37
  ### 2.6.0 (23 November 2021)
31
38
  - #921 [dev-env] Introuces update to change existing environment
32
39
  - #928 [dev-env] Switch lando to use our fork
Binary file
@@ -36,7 +36,8 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
36
36
  /**
37
37
  * Internal dependencies
38
38
  */
39
- const baseUsage = 'vip config envvar set'; // Command examples
39
+ const baseUsage = 'vip config envvar set';
40
+ const NEW_RELIC_ENVVAR_KEY = 'NEW_RELIC_LICENSE_KEY'; // Command examples
40
41
 
41
42
  const examples = [{
42
43
  usage: `${baseUsage} MY_VARIABLE`,
@@ -63,6 +64,12 @@ async function setEnvVarCommand(arg, opt) {
63
64
  process.exit(1);
64
65
  }
65
66
 
67
+ if (NEW_RELIC_ENVVAR_KEY === name) {
68
+ await (0, _tracker.trackEvent)('envvar_set_newrelic_key', trackingParams);
69
+ console.log(_chalk.default.bold.red('Setting the New Relic key is not permitted.'), 'If you want to set your own New Relic key, please contact our support team through the usual channels.');
70
+ process.exit(1);
71
+ }
72
+
66
73
  let value;
67
74
 
68
75
  if (opt.fromFile) {
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ *
5
+ * @format
6
+ */
7
+
8
+ /**
9
+ * External dependencies
10
+ */
11
+
12
+ /**
13
+ * Internal dependencies
14
+ */
15
+ "use strict";
16
+
17
+ var _command = _interopRequireDefault(require("../lib/cli/command"));
18
+
19
+ var _devEnvironmentCore = require("../lib/dev-environment/dev-environment-core");
20
+
21
+ var _devEnvironmentCli = require("../lib/dev-environment/dev-environment-cli");
22
+
23
+ var _devEnvironment = require("../lib/constants/dev-environment");
24
+
25
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
26
+
27
+ const examples = [{
28
+ usage: `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} list`,
29
+ description: 'Return information about all local dev environments'
30
+ }];
31
+ (0, _command.default)().examples(examples).argv(process.argv, async () => {
32
+ try {
33
+ await (0, _devEnvironmentCore.printAllEnvironmentsInfo)();
34
+ } catch (error) {
35
+ (0, _devEnvironmentCli.handleCLIException)(error);
36
+ }
37
+ });
@@ -20,4 +20,4 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
20
20
 
21
21
  (0, _command.default)({
22
22
  requiredArgs: 1
23
- }).command('create', 'Create a new local dev environment').command('update', 'Update an already created local dev environment').command('start', 'Start a local dev environment').command('stop', 'Stop a local dev environment').command('destroy', 'Remove containers, networks, volumes and configuration files of a local dev environment').command('info', 'Provides basic info about one or multiple local dev environments').command('exec', 'Execute an operation on a dev environment').command('import', 'Import data into a local WordPress environment').argv(process.argv);
23
+ }).command('create', 'Create a new local dev environment').command('update', 'Update an already created local dev environment').command('start', 'Start a local dev environment').command('stop', 'Stop a local dev environment').command('destroy', 'Remove containers, networks, volumes and configuration files of a local dev environment').command('info', 'Provides basic info about one or multiple local dev environments').command('list', 'Provides basic info about all local dev environments').command('exec', 'Execute an operation on a dev environment').command('import', 'Import data into a local WordPress environment').argv(process.argv);
@@ -9,6 +9,7 @@ Object.defineProperty(exports, "__esModule", {
9
9
  value: true
10
10
  });
11
11
  exports.getLogs = getLogs;
12
+ exports.followLogs = followLogs;
12
13
  exports.validateInputs = validateInputs;
13
14
  exports.appQuery = void 0;
14
15
 
@@ -18,7 +19,7 @@ var _rollbar = require("../lib/rollbar");
18
19
 
19
20
  var _tracker = require("../lib/tracker");
20
21
 
21
- var logsLib = _interopRequireWildcard(require("../lib/logs/logs"));
22
+ var logsLib = _interopRequireWildcard(require("../lib/app-logs/app-logs"));
22
23
 
23
24
  var exit = _interopRequireWildcard(require("../lib/cli/exit"));
24
25
 
@@ -30,26 +31,25 @@ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj &&
30
31
 
31
32
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
32
33
 
33
- const LIMIT_MAX = 5000;
34
34
  const LIMIT_MIN = 1;
35
+ const LIMIT_MAX = 5000;
35
36
  const ALLOWED_TYPES = ['app', 'batch'];
36
37
  const ALLOWED_FORMATS = ['csv', 'json', 'text'];
38
+ const DEFAULT_POLLING_DELAY_IN_SECONDS = 30;
39
+ const MIN_POLLING_DELAY_IN_SECONDS = 5;
40
+ const MAX_POLLING_DELAY_IN_SECONDS = 300;
37
41
 
38
42
  async function getLogs(arg, opt) {
39
43
  validateInputs(opt.type, opt.limit, opt.format);
40
- const trackingParams = {
41
- command: 'vip logs',
42
- org_id: opt.app.organization.id,
43
- app_id: opt.app.id,
44
- env_id: opt.env.id,
45
- type: opt.type,
46
- limit: opt.limit,
47
- format: opt.format
48
- };
44
+ const trackingParams = getBaseTrackingParams(opt);
49
45
  await (0, _tracker.trackEvent)('logs_command_execute', trackingParams);
50
- let logs = [];
46
+ let logs;
51
47
 
52
48
  try {
49
+ if (opt.follow) {
50
+ return await followLogs(opt);
51
+ }
52
+
53
53
  logs = await logsLib.getRecentLogs(opt.app.id, opt.env.id, opt.type, opt.limit);
54
54
  } catch (error) {
55
55
  _rollbar.rollbar.error(error);
@@ -61,15 +61,91 @@ async function getLogs(arg, opt) {
61
61
  }
62
62
 
63
63
  await (0, _tracker.trackEvent)('logs_command_success', { ...trackingParams,
64
- logs_output: logs.length
64
+ total: logs.nodes.length
65
65
  });
66
66
 
67
- if (!logs.length) {
67
+ if (!logs.nodes.length) {
68
68
  console.error('No logs found');
69
69
  return;
70
- } // Strip out __typename
70
+ }
71
+
72
+ printLogs(logs.nodes, opt.format);
73
+ }
74
+
75
+ async function followLogs(opt) {
76
+ let after = null;
77
+ let isFirstRequest = true; // How many times have we polled?
78
+
79
+ let requestNumber = 0;
80
+ const trackingParams = getBaseTrackingParams(opt); // Set an initial default delay
81
+
82
+ let delay = DEFAULT_POLLING_DELAY_IN_SECONDS;
83
+
84
+ while (true) {
85
+ const limit = isFirstRequest ? opt.limit : LIMIT_MAX;
86
+ requestNumber++;
87
+ trackingParams.request_number = requestNumber;
88
+ trackingParams.request_delay = delay;
89
+ trackingParams.limit = limit;
90
+ let logs;
91
+
92
+ try {
93
+ var _logs;
94
+
95
+ logs = await logsLib.getRecentLogs(opt.app.id, opt.env.id, opt.type, limit, after);
96
+ await (0, _tracker.trackEvent)('logs_command_follow_success', { ...trackingParams,
97
+ total: (_logs = logs) === null || _logs === void 0 ? void 0 : _logs.nodes.length
98
+ });
99
+ } catch (error) {
100
+ await (0, _tracker.trackEvent)('logs_command_follow_error', { ...trackingParams,
101
+ error: error.message
102
+ }); // If the first request fails we don't want to retry (it's probably not recoverable)
103
+
104
+ if (isFirstRequest) {
105
+ console.error('Error fetching initial logs');
106
+ break;
107
+ } // Increase the delay on errors to avoid overloading the server, up to a max of 5 minutes
71
108
 
72
109
 
110
+ delay += DEFAULT_POLLING_DELAY_IN_SECONDS;
111
+ delay = Math.min(delay, MAX_POLLING_DELAY_IN_SECONDS);
112
+ console.error(`Failed to fetch logs. Trying again in ${delay} seconds`);
113
+
114
+ _rollbar.rollbar.error(error);
115
+ }
116
+
117
+ if (logs) {
118
+ var _logs2, _logs3, _logs4;
119
+
120
+ if ((_logs2 = logs) !== null && _logs2 !== void 0 && _logs2.nodes.length) {
121
+ printLogs(logs.nodes, opt.format);
122
+ }
123
+
124
+ after = (_logs3 = logs) === null || _logs3 === void 0 ? void 0 : _logs3.nextCursor;
125
+ isFirstRequest = false; // Keep a sane lower limit of MIN_POLLING_DELAY_IN_SECONDS just in case something goes wrong in the server-side
126
+
127
+ delay = Math.max(((_logs4 = logs) === null || _logs4 === void 0 ? void 0 : _logs4.pollingDelaySeconds) || DEFAULT_POLLING_DELAY_IN_SECONDS, MIN_POLLING_DELAY_IN_SECONDS);
128
+ }
129
+
130
+ await new Promise(resolve => setTimeout(resolve, delay * 1000));
131
+ }
132
+ }
133
+
134
+ function getBaseTrackingParams(opt) {
135
+ return {
136
+ command: 'vip logs',
137
+ org_id: opt.app.organization.id,
138
+ app_id: opt.app.id,
139
+ env_id: opt.env.id,
140
+ type: opt.type,
141
+ limit: opt.limit,
142
+ follow: opt.follow || false,
143
+ format: opt.format
144
+ };
145
+ }
146
+
147
+ function printLogs(logs, format) {
148
+ // Strip out __typename
73
149
  logs = logs.map(log => {
74
150
  const {
75
151
  timestamp,
@@ -82,7 +158,7 @@ async function getLogs(arg, opt) {
82
158
  });
83
159
  let output = '';
84
160
 
85
- if (opt.format && 'text' === opt.format) {
161
+ if (format && 'text' === format) {
86
162
  const rows = [];
87
163
 
88
164
  for (const {
@@ -93,7 +169,7 @@ async function getLogs(arg, opt) {
93
169
  output = rows.join('\n');
94
170
  }
95
171
  } else {
96
- output = (0, _format.formatData)(logs, opt.format);
172
+ output = (0, _format.formatData)(logs, format);
97
173
  }
98
174
 
99
175
  console.log(output);
@@ -108,8 +184,8 @@ function validateInputs(type, limit, format) {
108
184
  exit.withError(`Invalid format: ${format}. The supported formats are: ${ALLOWED_FORMATS.join(', ')}.`);
109
185
  }
110
186
 
111
- if (!Number.isInteger(limit) || limit < LIMIT_MIN || limit > LIMIT_MAX) {
112
- exit.withError(`Invalid limit: ${limit}. It should be a number between ${LIMIT_MIN} and ${LIMIT_MAX}.`);
187
+ if (!Number.isInteger(limit) || limit < LIMIT_MIN || limit > logsLib.LIMIT_MAX) {
188
+ exit.withError(`Invalid limit: ${limit}. It should be a number between ${LIMIT_MIN} and ${logsLib.LIMIT_MAX}.`);
113
189
  }
114
190
  }
115
191
 
@@ -133,7 +209,7 @@ exports.appQuery = appQuery;
133
209
  appQuery,
134
210
  envContext: true,
135
211
  module: 'logs'
136
- }).option('type', 'The type of logs to be returned: "app" or "batch"', 'app').option('limit', 'The maximum number of log lines', 500).option('format', 'Output the log lines in CSV or JSON format', 'text').examples([{
212
+ }).option('type', 'The type of logs to be returned: "app" or "batch"', 'app').option('limit', 'The maximum number of log lines', 500).option('follow', 'Keep fetching new logs as they are generated').option('format', 'Output the log lines in CSV or JSON format', 'text').examples([{
137
213
  usage: 'vip @mysite.production logs',
138
214
  description: 'Get the most recent app logs'
139
215
  }, {
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ *
5
+ * @format
6
+ */
7
+
8
+ /**
9
+ * External dependencies
10
+ */
11
+
12
+ /**
13
+ * Internal dependencies
14
+ */
15
+ "use strict";
16
+
17
+ Object.defineProperty(exports, "__esModule", {
18
+ value: true
19
+ });
20
+ exports.whoamiCommand = whoamiCommand;
21
+
22
+ var _user = require("../lib/api/user");
23
+
24
+ var _command = _interopRequireDefault(require("../lib/cli/command"));
25
+
26
+ var _tracker = require("../lib/tracker");
27
+
28
+ var exit = _interopRequireWildcard(require("../lib/cli/exit"));
29
+
30
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
31
+
32
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
33
+
34
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
35
+
36
+ async function whoamiCommand() {
37
+ const trackingParams = {
38
+ command: 'vip whoami'
39
+ };
40
+ await (0, _tracker.trackEvent)('whoami_command_execute', trackingParams);
41
+ let currentUser = {};
42
+
43
+ try {
44
+ currentUser = await (0, _user.getCurrentUserInfo)();
45
+ } catch (err) {
46
+ await (0, _tracker.trackEvent)('whoami_command_error', { ...trackingParams,
47
+ error: err.message
48
+ });
49
+ exit.withError(`Failed to fetch information about the currently logged-in user error: ${err.message}`);
50
+ }
51
+
52
+ await (0, _tracker.trackEvent)('whoami_command_success', trackingParams);
53
+ const output = [`- Howdy ${currentUser.displayName}!`, `- Your user ID is ${currentUser.id}`];
54
+
55
+ if (currentUser.isVIP) {
56
+ output.push('- Your account has VIP Staff permissions');
57
+ }
58
+
59
+ console.log(output.join('\n'));
60
+ }
61
+
62
+ (0, _command.default)({
63
+ usage: 'vip whoami'
64
+ }).examples([{
65
+ usage: 'vip whoami',
66
+ description: 'Display details about the currently logged-in user.'
67
+ }]).argv(process.argv, whoamiCommand);
package/dist/bin/vip.js CHANGED
@@ -48,7 +48,7 @@ const runCmd = async function () {
48
48
  await _token.default.purge();
49
49
  await (0, _tracker.trackEvent)('logout_command_execute');
50
50
  console.log('You are successfully logged out.');
51
- }).command('app', 'List and modify your VIP applications').command('config', 'Set configuration for your VIP applications').command('dev-env', 'Use local dev-environment').command('import', 'Import media or SQL files into your VIP applications').command('logs', 'Get logs from your VIP applications').command('search-replace', 'Perform search and replace tasks on files').command('sync', 'Sync production to a development environment').command('wp', 'Run WP CLI commands against an environment');
51
+ }).command('app', 'List and modify your VIP applications').command('config', 'Set configuration for your VIP applications').command('dev-env', 'Use local dev-environment').command('import', 'Import media or SQL files into your VIP applications').command('logs', 'Get logs from your VIP applications').command('search-replace', 'Perform search and replace tasks on files').command('sync', 'Sync production to a development environment').command('whoami', 'Display details about the currently logged-in user').command('wp', 'Run WP CLI commands against an environment');
52
52
  cmd.argv(process.argv);
53
53
  };
54
54
 
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.getCurrentUserInfo = getCurrentUserInfo;
7
+
8
+ var _graphqlTag = _interopRequireDefault(require("graphql-tag"));
9
+
10
+ var _api = _interopRequireDefault(require("../api"));
11
+
12
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
13
+
14
+ /**
15
+ *
16
+ * @format
17
+ */
18
+
19
+ /**
20
+ * External dependencies
21
+ */
22
+
23
+ /**
24
+ * Internal dependencies
25
+ */
26
+ const QUERY_CURRENT_USER = (0, _graphqlTag.default)`
27
+ query Me {
28
+ me {
29
+ id
30
+ displayName
31
+ isVIP
32
+ organizationRoles {
33
+ nodes {
34
+ organizationId
35
+ roleId
36
+ }
37
+ }
38
+ }
39
+ }
40
+ `;
41
+
42
+ async function getCurrentUserInfo() {
43
+ const api = await (0, _api.default)();
44
+ const response = await api.query({
45
+ query: QUERY_CURRENT_USER
46
+ });
47
+ const {
48
+ data: {
49
+ me
50
+ }
51
+ } = response;
52
+
53
+ if (!me) {
54
+ throw new Error('The API did not return any information about the user.');
55
+ }
56
+
57
+ return me;
58
+ }
package/dist/lib/api.js CHANGED
@@ -52,7 +52,9 @@ function disableGlobalGraphQLErrorHandling() {
52
52
  globalGraphQLErrorHandlingEnabled = false;
53
53
  }
54
54
 
55
- async function API() {
55
+ async function API({
56
+ exitOnError = true
57
+ } = {}) {
56
58
  const authToken = await _token.default.get();
57
59
  const headers = {
58
60
  'User-Agent': _env.default.userAgent,
@@ -71,7 +73,10 @@ async function API() {
71
73
  graphQLErrors.forEach(error => {
72
74
  console.error(_chalk.default.red('Error:'), error.message);
73
75
  });
74
- process.exit();
76
+
77
+ if (exitOnError) {
78
+ process.exit();
79
+ }
75
80
  }
76
81
  });
77
82
  const withToken = (0, _context.setContext)(async () => {
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.getRecentLogs = getRecentLogs;
7
+ exports.LIMIT_MAX = void 0;
8
+
9
+ var _graphqlTag = _interopRequireDefault(require("graphql-tag"));
10
+
11
+ var _api = _interopRequireDefault(require("../api"));
12
+
13
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
14
+
15
+ /**
16
+ *
17
+ * @format
18
+ */
19
+
20
+ /**
21
+ * External dependencies
22
+ */
23
+
24
+ /**
25
+ * Internal dependencies
26
+ */
27
+ const LIMIT_MAX = 5000;
28
+ exports.LIMIT_MAX = LIMIT_MAX;
29
+ const QUERY_ENVIRONMENT_LOGS = (0, _graphqlTag.default)`
30
+ query GetAppLogs( $appId: Int, $envId: Int, $type: AppEnvironmentLogType, $limit: Int, $after: String ) {
31
+ app( id: $appId ) {
32
+ environments( id: $envId ) {
33
+ id
34
+ logs( type: $type, limit: $limit, after: $after ) {
35
+ nodes {
36
+ timestamp
37
+ message
38
+ }
39
+ nextCursor
40
+ pollingDelaySeconds
41
+ }
42
+ }
43
+ }
44
+ }
45
+ `;
46
+
47
+ async function getRecentLogs(appId, envId, type, limit, after) {
48
+ var _response$data, _response$data$app, _response$data$app$en;
49
+
50
+ const api = await (0, _api.default)({
51
+ exitOnError: false
52
+ });
53
+ const response = await api.query({
54
+ query: QUERY_ENVIRONMENT_LOGS,
55
+ variables: {
56
+ appId,
57
+ envId,
58
+ type,
59
+ limit,
60
+ after
61
+ }
62
+ });
63
+ const logs = response === null || response === void 0 ? void 0 : (_response$data = response.data) === null || _response$data === void 0 ? void 0 : (_response$data$app = _response$data.app) === null || _response$data$app === void 0 ? void 0 : (_response$data$app$en = _response$data$app.environments[0]) === null || _response$data$app$en === void 0 ? void 0 : _response$data$app$en.logs;
64
+
65
+ if (!(logs !== null && logs !== void 0 && logs.nodes)) {
66
+ throw new Error('Unable to query logs');
67
+ }
68
+
69
+ return logs;
70
+ }
@@ -370,5 +370,5 @@ function addDevEnvConfigurationOptions(command) {
370
370
  }
371
371
 
372
372
  function getWordpressImageTags() {
373
- return ['5.8.1', '5.8', '5.7.3', '5.7.2'];
373
+ return ['5.9', '5.8.3', '5.8.2', '5.8.1', '5.8', '5.7.3', '5.7.2'];
374
374
  }
@@ -48,6 +48,7 @@ const IMPORT_SQL_PROGRESS_QUERY = (0, _graphqlTag.default)`
48
48
  environments(id: $envId) {
49
49
  id
50
50
  isK8sResident
51
+ launched
51
52
  jobs(types: "sql_import") {
52
53
  id
53
54
  type
@@ -106,7 +107,8 @@ async function getStatus(api, appId, envId) {
106
107
  const [environment] = environments;
107
108
  const {
108
109
  importStatus,
109
- jobs
110
+ jobs,
111
+ launched
110
112
  } = environment;
111
113
 
112
114
  if (!environment.isK8sResident && !(jobs !== null && jobs !== void 0 && jobs.length)) {
@@ -116,15 +118,16 @@ async function getStatus(api, appId, envId) {
116
118
  const [importJob] = jobs;
117
119
  return {
118
120
  importStatus,
119
- importJob
121
+ importJob,
122
+ launched
120
123
  };
121
124
  }
122
125
 
123
- function getErrorMessage(importFailed) {
126
+ function getErrorMessage(importFailed, launched = false) {
124
127
  debug({
125
128
  importFailed
126
129
  });
127
- const rollbackMessage = `Your site is ${_chalk.default.blue('automatically being rolled back')} to the last backup prior to your import job.
130
+ const rollbackMessage = launched ? '' : `Your site is ${_chalk.default.blue('automatically being rolled back')} to the last backup prior to your import job.
128
131
  `;
129
132
  let message = importFailed.error;
130
133
 
@@ -262,7 +265,8 @@ ${maybeExitPrompt}
262
265
  }
263
266
 
264
267
  const {
265
- importStatus
268
+ importStatus,
269
+ launched
266
270
  } = status;
267
271
  let {
268
272
  importJob
@@ -352,7 +356,8 @@ ${maybeExitPrompt}
352
356
 
353
357
  if (!jobSteps.length) {
354
358
  return reject({
355
- error: 'Could not enumerate the import job steps'
359
+ error: 'Could not enumerate the import job steps',
360
+ launched
356
361
  });
357
362
  }
358
363
 
@@ -375,7 +380,8 @@ ${maybeExitPrompt}
375
380
  commandOutput: failedImportStep.output,
376
381
  error: 'Import step failed',
377
382
  stepName: failedImportStep.name,
378
- errorText: failedImportStep.error
383
+ errorText: failedImportStep.error,
384
+ launched
379
385
  });
380
386
  }
381
387
 
@@ -385,7 +391,8 @@ ${maybeExitPrompt}
385
391
  if (jobStatus === 'error') {
386
392
  return reject({
387
393
  error: 'Import job failed',
388
- steps: jobSteps
394
+ steps: jobSteps,
395
+ launched
389
396
  });
390
397
  }
391
398
 
@@ -425,7 +432,7 @@ ${maybeExitPrompt}
425
432
  progressTracker.print({
426
433
  clearAfter: true
427
434
  });
428
- exit.withError(getErrorMessage(importFailed));
435
+ exit.withError(getErrorMessage(importFailed, importFailed.launched));
429
436
  }
430
437
  }
431
438