@automattic/vip 3.8.4 → 3.8.6

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.
@@ -15,6 +15,7 @@ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e;
15
15
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
16
16
  // eslint-disable-next-line no-duplicate-imports
17
17
 
18
+ const usage = 'vip import media abort';
18
19
  const appQuery = `
19
20
  id,
20
21
  name,
@@ -44,16 +45,23 @@ const ABORT_IMPORT_MUTATION = (0, _graphqlTag.default)`
44
45
  }
45
46
  }
46
47
  `;
48
+
49
+ // Command examples
50
+ const examples = [{
51
+ usage: `vip @example-app.production import media abort`,
52
+ description: 'Abort the media file import that is currently in progress on the production environment of the "example-app" application.'
53
+ }];
47
54
  (0, _command.default)({
48
55
  appContext: true,
49
56
  appQuery,
50
57
  envContext: true,
51
58
  requiredArgs: 0,
59
+ usage,
52
60
  requireConfirm: `
53
- ${_chalk.default.red.bold("By running this command, the Media Import running on your App will stop and can't be resumed.")}
54
- ${_chalk.default.red.bold('Are you sure you want to abort this Media Import?')}
61
+ ${_chalk.default.red.bold('Running this command will stop the currently running media import. The import process cannot be resumed.')}
62
+ ${_chalk.default.red.bold('Are you sure you want to abort this media import?')}
55
63
  `
56
- }).argv(process.argv, async (arg, {
64
+ }).examples(examples).argv(process.argv, async (arg, {
57
65
  app,
58
66
  env
59
67
  }) => {
@@ -66,14 +74,14 @@ ${_chalk.default.red.bold('Are you sure you want to abort this Media Import?')}
66
74
  await track('import_media_command_error', {
67
75
  errorType: 'unsupported-app'
68
76
  });
69
- exit.withError('The type of application you specified does not currently support Media imports.');
77
+ exit.withError('The type of application you specified does not currently support media file imports.');
70
78
  }
71
79
  const api = (0, _api.default)();
72
80
  await track('import_media_abort_execute');
73
81
  const progressTracker = new _progress.MediaImportProgressTracker([]);
74
82
  progressTracker.prefix = `
75
83
  =============================================================
76
- Aborting the Media Import running on your App
84
+ Aborting this media import.
77
85
  `;
78
86
  try {
79
87
  await api.mutate({
@@ -25,12 +25,23 @@ const appQuery = `
25
25
  }
26
26
  }
27
27
  `;
28
+ const usage = 'vip import media status';
29
+
30
+ // Command examples
31
+ const examples = [{
32
+ usage: 'vip @example-app.production import media status',
33
+ description: 'Check the status of the most recent media import.\n' + ' * If the import is still in progress, the command will poll until the import is complete.\n' + ' * If the import is already complete, the command will download an error log for the most recent import.'
34
+ }, {
35
+ usage: 'vip @example-app.production import media status --saveErrorLog --exportFileErrorsToJson',
36
+ description: 'Check the status of the most recent media import and automatically download the error log in JSON format.'
37
+ }];
28
38
  (0, _command.default)({
29
39
  appContext: true,
30
40
  appQuery,
31
41
  envContext: true,
32
- requiredArgs: 0
33
- }).option('exportFileErrorsToJson', 'Export any file errors encountered to a JSON file instead of a plain text file', false).option('saveErrorLog', 'Download file-error logs without prompting', 'prompt').argv(process.argv, async (arg, {
42
+ requiredArgs: 0,
43
+ usage
44
+ }).option('exportFileErrorsToJson', 'Format an error log in JSON. Default is TXT.').option('saveErrorLog', 'Skip the confirmation prompt and download an error log automatically.', 'prompt').examples(examples).argv(process.argv, async (arg, {
34
45
  app,
35
46
  env,
36
47
  exportFileErrorsToJson,
@@ -45,7 +56,7 @@ const appQuery = `
45
56
  await track('import_media_command_error', {
46
57
  errorType: 'unsupported-app'
47
58
  });
48
- exit.withError('The type of application you specified does not currently support this feature');
59
+ exit.withError('The type of application you specified does not currently support this feature.');
49
60
  }
50
61
  await track('import_media_check_status_command_execute');
51
62
  const progressTracker = new _progress.MediaImportProgressTracker([]);
@@ -40,22 +40,28 @@ const START_IMPORT_MUTATION = (0, _graphqlTag.default)`
40
40
  }
41
41
  }
42
42
  `;
43
+ const usage = 'vip import media';
43
44
  const debug = (0, _debug.default)('vip:vip-import-media');
44
45
 
45
46
  // Command examples for the `vip import media` help prompt
46
47
  const examples = [{
47
- usage: 'vip import media @mysite.production https://<path_to_publicly_accessible_archive>',
48
- description: 'Start a media import with the contents of the archive file in the URL'
48
+ usage: 'vip @example-app.production import media https://example.com/uploads.tar.gz',
49
+ description: 'Import the archived file "uploads.tar.gz" from a publicly accessible URL to a production environment.'
50
+ },
51
+ // Format error logs
52
+ {
53
+ usage: 'vip @example-app.production import media https://example.com/uploads.tar.gz --overwriteExistingFiles --exportFileErrorsToJson',
54
+ description: 'Overwrite existing files with the imported files if they have the same file path and name, and format the error log for the import in JSON.'
49
55
  },
50
56
  // `media status` subcommand
51
57
  {
52
- usage: 'vip import media status @mysite.production',
53
- description: 'Check the status of the most recent import. If an import is running, this will poll until it is complete.'
58
+ usage: 'vip @example-app.production import media status',
59
+ description: 'Check the status of the most recent media import.'
54
60
  },
55
61
  // `media abort` subcommand
56
62
  {
57
- usage: 'vip import media abort @mysite.production',
58
- description: 'Abort an ongoing import'
63
+ usage: 'vip @example-app.production import media abort',
64
+ description: 'Abort the currently running media import.'
59
65
  }];
60
66
  function isSupportedUrl(urlToTest) {
61
67
  let url;
@@ -72,14 +78,15 @@ function isSupportedUrl(urlToTest) {
72
78
  envContext: true,
73
79
  module: 'import-media',
74
80
  requiredArgs: 1,
81
+ usage,
75
82
  requireConfirm: `
76
83
  ${_chalk.default.red.bold("NOTE: If the provided archive's directory structure contains an `uploads/` directory,")}
77
84
  ${_chalk.default.red.bold('only the files present inside that directory will be imported and the rest will be ignored.')}
78
85
  ${_chalk.default.red.bold('If no `uploads/` directory is found, all files will be imported, as is.')}
79
86
 
80
- Are you sure you want to import the contents of the url?
87
+ Are you sure you want to import the contents of the URL?
81
88
  `
82
- }).command('status', 'Check the status of the latest Media Import').command('abort', 'Abort the Media Import running for your App').option('exportFileErrorsToJson', 'Export any file errors encountered to a JSON file instead of a plain text file', false).option('saveErrorLog', 'Download file-error logs without prompting').option('overwriteExistingFiles', 'Overwrite any existing files', false).option('importIntermediateImages', 'Import intermediate image files', false).examples(examples).argv(process.argv, async (args, opts) => {
89
+ }).command('status', 'Check the status of a currently running media import or retrieve an error log of the most recent media import.').command('abort', 'Abort the media import currently in progress.').option('exportFileErrorsToJson', 'Format the error log in JSON. Default is TXT.').option('saveErrorLog', 'Skip the confirmation prompt and download an error log for the import automatically.').option('overwriteExistingFiles', 'Overwrite existing files with the imported files if they have the same path and file name.', false).option('importIntermediateImages', 'Include intermediate image files in the import.', false).examples(examples).argv(process.argv, async (args, opts) => {
83
90
  const {
84
91
  app,
85
92
  env,
@@ -93,7 +100,7 @@ Are you sure you want to import the contents of the url?
93
100
  console.log(_chalk.default.red(`
94
101
  Error:
95
102
  Invalid URL provided: ${url}
96
- Please make sure that it is a publicly accessible web URL containing an archive of the media files to import`));
103
+ Please make sure that it is a publicly accessible web URL containing an archive of the media files to import.`));
97
104
  return;
98
105
  }
99
106
  const track = _tracker.trackEventWithEnv.bind(null, app.id, env.id);
@@ -27,12 +27,20 @@ environments{
27
27
  }
28
28
  }
29
29
  `;
30
+ const usage = 'vip import sql status';
31
+
32
+ // Command examples
33
+ const examples = [{
34
+ usage: 'vip @example-app.develop import sql status',
35
+ description: 'Check the status of the most recent SQL database import to the develop environment of the "example-app" application.\n' + ' * If the import is still in progress, the command will poll until the import is complete.'
36
+ }];
30
37
  (0, _command.default)({
31
38
  appContext: true,
32
39
  appQuery,
33
40
  envContext: true,
34
- requiredArgs: 0
35
- }).argv(process.argv, async (arg, {
41
+ requiredArgs: 0,
42
+ usage
43
+ }).examples(examples).argv(process.argv, async (arg, {
36
44
  app,
37
45
  env
38
46
  }) => {
@@ -66,16 +66,17 @@ const START_IMPORT_MUTATION = (0, _graphqlTag.default)`
66
66
  }
67
67
  }
68
68
  `;
69
+ const usage = 'vip import sql';
69
70
  const debug = (0, _debug.default)('@automattic/vip:bin:vip-import-sql');
70
71
  const SQL_IMPORT_PREFLIGHT_PROGRESS_STEPS = [{
71
72
  id: 'replace',
72
- name: 'Performing Search and Replace'
73
+ name: 'Performing search and replace'
73
74
  }, {
74
75
  id: 'upload',
75
76
  name: 'Uploading file'
76
77
  }, {
77
78
  id: 'queue_import',
78
- name: 'Queueing Import'
79
+ name: 'Queueing import'
79
80
  }];
80
81
 
81
82
  /**
@@ -156,7 +157,7 @@ async function gates(app, env, fileMeta) {
156
157
  await track('import_sql_command_error', {
157
158
  error_type: 'empty-import-status'
158
159
  });
159
- exit.withError('Could not determine the import status for this environment. Check the app/environment and if the problem persists, contact support for assistance');
160
+ exit.withError('Could not determine the import status for this environment. Check the app/environment and if the problem persists, contact support for assistance.');
160
161
  }
161
162
  const {
162
163
  importStatus: {
@@ -182,33 +183,33 @@ async function gates(app, env, fileMeta) {
182
183
  const examples = [
183
184
  // `sql` subcommand
184
185
  {
185
- usage: 'vip import sql @mysite.develop file.sql',
186
- description: 'Import the given SQL file to your site'
186
+ usage: 'vip @example-app.develop import sql file.sql',
187
+ description: 'Import the local SQL database backup file "file.sql" to the develop environment of the "example-app" application.'
187
188
  },
188
189
  // `search-replace` flag
189
190
  {
190
- usage: 'vip import sql @mysite.develop file.sql --search-replace="https://from.example.com,https://to.example.com"',
191
- description: 'Perform a Search and Replace, then import the replaced file to your site.\n' + ' * Ensure there are no spaces between your search-replace parameters'
191
+ usage: 'vip @example-app.develop import sql file.sql --search-replace="https://from.example.com,https://to.example.com"',
192
+ description: 'Perform a search and replace operation on the SQL database file during the import process.'
192
193
  },
193
194
  // `search-replace` flag
194
195
  {
195
- usage: 'vip import sql @mysite.develop file.sql --search-replace="https://from.example.com,https://to.example.com" --search-replace="example.com/from,example.com/to"',
196
- description: 'Perform multiple search and replace tasks, then import the updated file to your site.'
196
+ usage: 'vip @example-app.develop import sql file.sql --search-replace="from.example.com,to.example.com" --search-replace="example.com/from,example.com/to"',
197
+ description: 'Perform multiple search and replace operations on the SQL database file during the import process.'
197
198
  },
198
199
  // `in-place` flag
199
200
  {
200
- usage: 'vip import sql @mysite.develop file.sql --search-replace="https://from.example.com,https://to.example.com" --in-place',
201
- description: 'Search and Replace on the input `file.sql`, then import the updated file to your site'
201
+ usage: 'vip @example-app.develop import sql file.sql --search-replace="https://from.example.com,https://to.example.com" --in-place',
202
+ description: 'Perform a search and replace operation on "file.sql" locally, save the changes, then import the updated file.'
202
203
  },
203
204
  // `output` flag
204
205
  {
205
- usage: 'vip import sql @mysite.develop file.sql --search-replace="https://from.example.com,https://to.example.com" --output="output.sql"',
206
- description: 'Output the performed Search and Replace to the specified output file, then import the replaced file to your site\n' + ' * Has no effect when the `in-place` flag is used'
206
+ usage: 'vip @example-app.develop import sql file.sql --search-replace="https://from.example.com,https://to.example.com" --output="updated-file.sql"',
207
+ description: 'Create a copy of the imported file with the completed search and replace operations and save it locally to a file named "updated-file.sql".'
207
208
  },
208
209
  // `sql status` subcommand
209
210
  {
210
- usage: 'vip import sql status @mysite.develop',
211
- description: 'Check the status of the most recent import. If an import is running, this will poll until it is complete.'
211
+ usage: 'vip @example-app.develop import sql status',
212
+ description: 'Check the status of the most recent SQL database import to the develop environment of the "example-app" application.\n' + ' * This will continue to poll until the import is complete.'
212
213
  }];
213
214
  const promptToContinue = async ({
214
215
  launched,
@@ -224,7 +225,7 @@ const promptToContinue = async ({
224
225
  const promptResponse = await (0, _enquirer.prompt)({
225
226
  type: 'input',
226
227
  name: 'confirmedDomain',
227
- message: `You are about to import ${source} into a ${launched ? 'launched' : 'un-launched'} ${formattedEnvironment} site ${_chalk.default.yellow(domain)}.\nType '${_chalk.default.yellow(promptToMatch)}' (without the quotes) to continue:\n`
228
+ message: `You are about to import ${source} into the ${launched ? 'launched' : 'unlaunched'} ${formattedEnvironment} environment ${_chalk.default.yellow(domain)}.\nType '${_chalk.default.yellow(promptToMatch)}' (without the quotes) to continue:\n`
228
229
  });
229
230
  if (promptResponse.confirmedDomain.toUpperCase() !== promptToMatch) {
230
231
  await track('import_sql_unexpected_tables');
@@ -253,7 +254,7 @@ async function validateAndGetTableNames({
253
254
  } catch (validateErr) {
254
255
  console.log('');
255
256
  exit.withError(`${validateErr.message}\n
256
- If you are confident the file does not contain unsupported statements, you can retry the command with the ${_chalk.default.yellow('--skip-validate')} option.
257
+ If you are confident that the file does not contain unsupported statements, you can retry the command with the ${_chalk.default.yellow('--skip-validate')} option.
257
258
  `);
258
259
  }
259
260
  // this can only be called after static validation of the SQL file
@@ -289,20 +290,20 @@ const displayPlaybook = ({
289
290
  siteArray = selectedEnvironmentObj?.wpSitesSDS?.nodes;
290
291
  }
291
292
  if (!tableNames.length) {
292
- debug('Validation was skipped, no playbook information will be displayed');
293
+ debug('Validation was skipped. No playbook information will be displayed.');
293
294
  } else {
294
295
  // output the table names
295
296
  console.log();
296
297
  if (!isMultiSite) {
297
- console.log('Below are a list of Tables that will be imported by this process:');
298
+ console.log('Tables that will be imported by this process:');
298
299
  console.log((0, _cliColumns.default)(tableNames));
299
300
  } else {
300
301
  // we have siteArray from the API, use it and the table names together
301
302
  if (siteArray === 'undefined' || !siteArray) {
302
- console.log(_chalk.default.yellowBright('Unable to determine the subsites affected by this import, please proceed only if you are confident on the contents in the import file.'));
303
+ console.log(_chalk.default.yellowBright('Unable to determine the network sites affected by this import. Please proceed only if you are confident that the contents of the file are valid for import.'));
303
304
  return;
304
305
  } else if (!siteArray?.length) {
305
- throw new Error('There were no sites in your multisite installation');
306
+ throw new Error('There were no sites in your multisite installation.');
306
307
  }
307
308
  const multiSiteBreakdown = siteArray.map(wpSite => {
308
309
  let siteRegex;
@@ -320,7 +321,7 @@ const displayPlaybook = ({
320
321
  };
321
322
  });
322
323
  if (launched) {
323
- console.log(_chalk.default.yellowBright('You are updating tables in a launched multi site installation. Sites in the same network may have their performance impacted by this operation.'));
324
+ console.log(_chalk.default.yellowBright('You are updating tables in a launched multisite environment. The performance of sites on the network might be impacted by this operation.'));
324
325
  }
325
326
  console.log(_chalk.default.yellow('The following sites will be affected by the import:'));
326
327
  multiSiteBreakdown.forEach(siteObject => {
@@ -336,8 +337,9 @@ void (0, _command.default)({
336
337
  appQuery,
337
338
  envContext: true,
338
339
  requiredArgs: 1,
339
- module: 'import-sql'
340
- }).command('status', 'Check the status of the current running import').option('skip-validate', 'Do not perform pre-upload file validation. If unsupported entries are present, the import is likely to fail').option('search-replace', 'Search for a given URL in the SQL file and replace it with another. The format for the value is `<from-url>,<to-url>` (e.g. --search-replace="https://from.com,https://to.com"').option('in-place', 'Search and Replace explicitly on the given input file').option('output', 'Specify the replacement output file for Search and Replace', 'process.stdout').examples(examples).argv(process.argv, async (arg, opts) => {
340
+ module: 'import-sql',
341
+ usage
342
+ }).command('status', 'Check the status of a SQL database import currently in progress.').option('skip-validate', 'Do not perform file validation prior to import. If the file contains unsupported entries, the import is likely to fail.').option('search-replace', 'Search for a string in the SQL file and replace it with a new string. Separate the values by a comma only; no spaces (e.g. --search-replace=“from,to”).').option('in-place', 'Perform a search and replace operation on a local SQL file, save the results to the file, then import the updated file.').option('output', 'Create a local copy of the imported file with the completed search and replace operations. Ignored if the command includes --in-place, or excludes a --search-replace operation. Accepts a local file path.').examples(examples).argv(process.argv, async (arg, opts) => {
341
343
  const {
342
344
  app,
343
345
  env
@@ -354,7 +356,7 @@ void (0, _command.default)({
354
356
  const isMultiSite = await (0, _isMultiSite.isMultiSiteInSiteMeta)(appId, envId);
355
357
  let fileMeta = await (0, _clientFileUploader.getFileMeta)(fileName);
356
358
  if (fileMeta.isCompressed) {
357
- console.log(_chalk.default.yellowBright('You are importing a compressed file. Validation and search-replace operation will be skipped.'));
359
+ console.log(_chalk.default.yellowBright('You are importing a compressed file. Validation and search-replace operations will be skipped.'));
358
360
  skipValidate = true;
359
361
  searchReplace = undefined;
360
362
  }
@@ -441,11 +443,11 @@ Processing the SQL import for your environment...
441
443
  } = await (0, _searchAndReplace.searchAndReplace)(fileName, searchReplace, {
442
444
  isImport: true,
443
445
  inPlace: opts.inPlace,
444
- output: true
446
+ output: opts.output ?? true // "true" creates a temp output file instead of printing to stdout, as we need to upload the output to S3.
445
447
  });
446
448
  if (typeof outputFileName !== 'string') {
447
449
  progressTracker.stepFailed('replace');
448
- return failWithError('Unable to determine location of the intermediate search & replace file.');
450
+ return failWithError('Unable to determine location of the intermediate search and replace file.');
449
451
  }
450
452
  fileNameToUpload = outputFileName;
451
453
  fileMeta = await (0, _clientFileUploader.getFileMeta)(fileNameToUpload);
@@ -29,7 +29,7 @@ async function vipImportValidateFilesCmd(arg = []) {
29
29
  const filePath = arg.path; // Extract the path of the file
30
30
 
31
31
  if (!(await (0, _vipImportValidateFiles.isDirectory)(filePath))) {
32
- console.error(_chalk.default.red('✕ Error:'), 'The given path is not a directory, please provide a valid directory path.');
32
+ console.error(_chalk.default.red('✕ Error:'), 'The given path is not a directory. Provide a valid directory path.');
33
33
  return;
34
34
  }
35
35
  let folderValidation = [];
@@ -66,7 +66,7 @@ async function vipImportValidateFilesCmd(arg = []) {
66
66
  * - Intermediate image validation
67
67
  */
68
68
  if (!files || !files.length || files.length <= 0) {
69
- console.error(_chalk.default.red('✕ Error:'), 'Media files directory cannot be empty');
69
+ console.error(_chalk.default.red('✕ Error:'), 'The media files directory cannot be empty.');
70
70
  }
71
71
 
72
72
  /**
@@ -147,10 +147,11 @@ async function vipImportValidateFilesCmd(arg = []) {
147
147
 
148
148
  await (0, _tracker.trackEvent)('import_validate_files_command_success', allErrors);
149
149
  }
150
+ const usage = 'vip import validate-files';
150
151
  (0, _command.default)({
151
152
  requiredArgs: 1,
152
- format: true
153
+ usage
153
154
  }).examples([{
154
- usage: 'vip import validate-files <folder_name>',
155
- description: 'Run the import validation against the folder of media files'
155
+ usage: 'vip import validate-files /Users/user-name/Desktop/uploads',
156
+ description: 'Validate the directory structure and contents of the local directory "uploads/".'
156
157
  }]).argv(process.argv, vipImportValidateFilesCmd);
@@ -7,12 +7,14 @@ var _sql = require("../lib/validations/sql");
7
7
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
8
8
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
9
9
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
10
+ const usage = 'vip import validate-sql';
10
11
  (0, _command.default)({
11
- requiredArgs: 1
12
- }).example('vip import validate-sql <file>', 'Run the import validation against file').argv(process.argv, async arg => {
12
+ requiredArgs: 1,
13
+ usage
14
+ }).example('vip import validate-sql file.sql', 'Scan the local file named "file.sql" for SQL validation errors and potential incompatibilities with platform databases.').argv(process.argv, async arg => {
13
15
  const filename = arg[0];
14
16
  if (!filename) {
15
- exit.withError('You must pass in a filename');
17
+ exit.withError('You must pass in a filename.');
16
18
  }
17
19
  await (0, _sql.validate)(filename);
18
20
  });
@@ -4,6 +4,6 @@
4
4
  var _command = _interopRequireDefault(require("../lib/cli/command"));
5
5
  var _tracker = require("../lib/tracker");
6
6
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
7
- (0, _command.default)().command('sql', 'Import SQL to your database from a file').command('validate-sql', 'Validate your SQL dump').command('validate-files', 'Validate your media file library').command('media', 'Import media files to the production environment of your application from a compressed web archive').example('vip import sql @mysite.develop <file.sql>', 'Import the given SQL file to your site').example('vip import media @mysite.production https://<path_to_publicly_accessible_archive>', 'Import contents of the given archive file into the media library of your site').argv(process.argv, async () => {
7
+ (0, _command.default)().command('sql', 'Import a SQL database file to an environment.').command('validate-sql', 'Validate a local SQL database file prior to import.').command('validate-files', 'Validate the directory structure and contents of a local media file directory prior to archiving and uploading it to a publicly accessible URL.').command('media', 'Import media files to an environment from an archived file located at a publicly accessible URL.').example('vip @example-app.develop import sql example-file.sql', 'Import the local SQL database file "example-file.sql" to the develop environment.').example('vip @example-app.production import media https://www.example.com/uploads.tar.gz', 'Import an archived file from a publicly accessible URL to the production environment.').argv(process.argv, async () => {
8
8
  await (0, _tracker.trackEvent)('vip_import_command_execute');
9
9
  });
@@ -7,6 +7,7 @@ exports.followLogs = followLogs;
7
7
  exports.getLogs = getLogs;
8
8
  exports.validateInputs = validateInputs;
9
9
  var _chalk = _interopRequireDefault(require("chalk"));
10
+ var _cliTable = _interopRequireDefault(require("cli-table3"));
10
11
  var _promises = require("timers/promises");
11
12
  var logsLib = _interopRequireWildcard(require("../lib/app-logs/app-logs"));
12
13
  var _command = _interopRequireDefault(require("../lib/cli/command"));
@@ -18,6 +19,7 @@ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e;
18
19
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
19
20
  const LIMIT_MIN = 1;
20
21
  const LIMIT_MAX = 5000;
22
+ const LIMIT_DEFAULT = 500;
21
23
  const ALLOWED_TYPES = ['app', 'batch'];
22
24
  const ALLOWED_FORMATS = ['csv', 'json', 'table'];
23
25
  const DEFAULT_POLLING_DELAY_IN_SECONDS = 30;
@@ -139,14 +141,30 @@ function printLogs(logs, format) {
139
141
  });
140
142
  let output = '';
141
143
  if (format && 'table' === format) {
142
- const rows = [];
144
+ const options = {
145
+ wordWrap: true,
146
+ wrapOnWordBoundary: true,
147
+ head: ['Timestamp', 'Message'],
148
+ style: {
149
+ head: ['cyan'],
150
+ border: ['grey']
151
+ }
152
+ };
153
+ if (process.stdout.isTTY && process.stdout.columns) {
154
+ options.colWidths = ['YYYY-MM-DDTHH:MM:SS.nnnnnnnnnZ'.length + 2 /* padding */, Math.max(process.stdout.columns - '│ │ │'.length - 'YYYY-MM-DDTHH:MM:SS.nnnnnnnnnZ'.length, 20)];
155
+ } else {
156
+ options.style.head = [];
157
+ options.style.border = [];
158
+ }
159
+ const table = new _cliTable.default(options);
143
160
  for (const {
144
161
  timestamp,
145
162
  message
146
163
  } of logs) {
147
- rows.push(`${timestamp} ${message}`);
148
- output = rows.join('\n');
164
+ const msg = message.trimRight().replace(/\t/g, ' ');
165
+ table.push([timestamp, msg]);
149
166
  }
167
+ output = table.toString();
150
168
  } else {
151
169
  output = (0, _format.formatData)(logs, format);
152
170
  }
@@ -159,6 +177,9 @@ function printLogs(logs, format) {
159
177
  * @param {string} format
160
178
  */
161
179
  function validateInputs(type, limit, format) {
180
+ if (limit === undefined) {
181
+ limit = LIMIT_DEFAULT;
182
+ }
162
183
  if (!ALLOWED_TYPES.includes(type)) {
163
184
  exit.withError(`Invalid type: ${type}. The supported types are: ${ALLOWED_TYPES.join(', ')}.`);
164
185
  }
@@ -188,7 +209,9 @@ const appQuery = exports.appQuery = `
188
209
  appQuery,
189
210
  envContext: true,
190
211
  module: 'logs'
191
- }).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', 'table').examples([{
212
+ }).option('type', 'The type of logs to be returned: "app" or "batch"', 'app')
213
+ // The default limit is set manually in the validateInputs function to address validation issues, avoiding incorrect replacement of the default value.
214
+ .option('limit', `The maximum number of log lines (defaults to ${LIMIT_DEFAULT})`).option('follow', 'Keep fetching new logs as they are generated').option('format', 'Output the log lines in CSV or JSON format', 'table').examples([{
192
215
  usage: 'vip @mysite.production logs',
193
216
  description: 'Get the most recent app logs'
194
217
  }, {
@@ -29,7 +29,7 @@ const examples = [
29
29
  }];
30
30
  (0, _command.default)({
31
31
  requiredArgs: 1
32
- }).option('search-replace', 'Specify the <from> and <to> pairs to be replaced').option('in-place', 'Perform the search and replace explicitly on the input file').option('output', 'Specify the replacement output file for Search and Replace').examples(examples).argv(process.argv, async (arg, opt) => {
32
+ }).option('search-replace', 'Specify the <from> and <to> pairs to be replaced').option('in-place', 'Perform the search and replace explicitly on the input file').option('output', 'Create a local copy of the file with the completed search and replace operations. Ignored if the command includes --in-place. Accepts a local file path. Defaults to STDOUT.').examples(examples).argv(process.argv, async (arg, opt) => {
33
33
  // TODO: tracks event for usage of this command stand alone
34
34
  const {
35
35
  searchReplace,
@@ -21,21 +21,21 @@ const appQuery = `id,name,environments{
21
21
  childEnvContext: true,
22
22
  module: 'sync',
23
23
  requireConfirm: 'Are you sure you want to sync from production?'
24
- }).argv(process.argv, async (arg, opts) => {
24
+ }).example('vip @example-app.develop sync', 'Sync the production environment database of the "example-app" application to the develop environment.').argv(process.argv, async (arg, opts) => {
25
25
  const api = (0, _api.default)();
26
26
  let syncing = false;
27
27
  await (0, _tracker.trackEvent)('sync_command_execute');
28
28
  try {
29
29
  await api.mutate({
30
30
  mutation: (0, _graphqlTag.default)`
31
- mutation SyncEnvironmentMutation($input: AppEnvironmentSyncInput) {
32
- syncEnvironment(input: $input) {
33
- environment {
34
- id
31
+ mutation SyncEnvironmentMutation($input: AppEnvironmentSyncInput) {
32
+ syncEnvironment(input: $input) {
33
+ environment {
34
+ id
35
+ }
35
36
  }
36
37
  }
37
- }
38
- `,
38
+ `,
39
39
  variables: {
40
40
  input: {
41
41
  id: opts.app.id,
@@ -99,28 +99,28 @@ const appQuery = `id,name,environments{
99
99
  // The rest of the iterations are just for moving the spinner
100
100
  api.query({
101
101
  query: (0, _graphqlTag.default)`
102
- query App($id: Int, $sync: Int) {
103
- app(id: $id) {
104
- id
105
- name
106
- environments {
102
+ query App($id: Int, $sync: Int) {
103
+ app(id: $id) {
107
104
  id
108
105
  name
109
- defaultDomain
110
- branch
111
- datacenter
112
- syncProgress(sync: $sync) {
113
- status
114
- sync
115
- steps {
116
- name
106
+ environments {
107
+ id
108
+ name
109
+ defaultDomain
110
+ branch
111
+ datacenter
112
+ syncProgress(sync: $sync) {
117
113
  status
114
+ sync
115
+ steps {
116
+ name
117
+ status
118
+ }
118
119
  }
119
120
  }
120
121
  }
121
122
  }
122
- }
123
- `,
123
+ `,
124
124
  fetchPolicy: 'network-only',
125
125
  variables: {
126
126
  id: opts.app.id,
@@ -19,7 +19,7 @@ var _tracker = require("../lib/tracker");
19
19
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
20
20
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
21
21
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
22
- const ALLOWED_NODEJS_VERSIONS = ['14', '16', '18'];
22
+ const ALLOWED_NODEJS_VERSIONS = ['18', '20', '22'];
23
23
  const appQuery = exports.appQuery = `
24
24
  id
25
25
  name
@@ -42,7 +42,7 @@ const appQuery = exports.appQuery = `
42
42
  }
43
43
  `;
44
44
  let suppressOutput = false;
45
- let outputJson = false;
45
+ let outputFormat = 'table';
46
46
  let harmoniaArgs = [];
47
47
 
48
48
  /**
@@ -321,7 +321,7 @@ async function handleResults(harmonia, results) {
321
321
  });
322
322
 
323
323
  // If the output is JSON, reenable the logToConsole output and print-out the json format.
324
- if (outputJson) {
324
+ if (outputFormat === 'json') {
325
325
  suppressOutput = false;
326
326
  logToConsole(harmonia.resultsJSON());
327
327
  process.exit(0);
@@ -383,9 +383,9 @@ async function validateArgs(opt) {
383
383
  }
384
384
 
385
385
  // If the JSON option is enabled, all the stdout should be suppressed to prevent polluting the output.
386
- if (opt.json) {
386
+ if (opt.format === 'json') {
387
387
  suppressOutput = true;
388
- outputJson = true;
388
+ outputFormat = 'json';
389
389
  }
390
390
  if (opt.app) {
391
391
  // Get build information from API and store it in the env object
@@ -459,10 +459,10 @@ if (parsedAlias.app) {
459
459
  } else {
460
460
  logToConsole(_chalk.default.bold.yellow('Warning: ') + 'The preflight tests are running without a provided application and/or environment.\n' + 'Some app-dependent configurations, such as environment variables, might not defined.');
461
461
  }
462
- (0, _command.default)(commandOpts).option('verbose', 'Increase logging level to include app build and server boot up messages', false).option('node-version', 'Select a specific target Node.JS version in semver format (MAJOR.MINOR.PATCH) or a MAJOR').option('wait', 'Configure the time to wait in ms for the app to boot up. Do not change unless you have issues', 3000).option(['p', 'port'], 'Configure the port to use for the app (defaults to a random port between 3001 and 3999)').option('json', 'Output the results as JSON', false).option(['P', 'path'], 'Path to the app to be tested', process.cwd()).examples([{
462
+ (0, _command.default)(commandOpts).option('verbose', 'Increase logging level to include app build and server boot up messages', false).option('node-version', `Select a specific target Node.JS version in semver format (MAJOR.MINOR.PATCH) or a MAJOR (${ALLOWED_NODEJS_VERSIONS.join(', ')})`).option('wait', 'Configure the time to wait in ms for the app to boot up. Do not change unless you have issues', 3000).option(['p', 'port'], 'Configure the port to use for the app (defaults to a random port between 3001 and 3999)').option('format', 'Output the log lines in CSV or JSON format', 'table').option(['P', 'path'], 'Path to the app to be tested', process.cwd()).examples([{
463
463
  usage: 'vip @mysite.production validate preflight',
464
464
  description: 'Runs the preflight tests to validate if your application is ready to be deployed to VIP Go'
465
465
  }, {
466
- usage: 'vip @mysite.production validate preflight --json > results.json',
466
+ usage: 'vip @mysite.production validate preflight --format json > results.json',
467
467
  description: 'Runs the preflight tests, but output the results in JSON format, and redirect the output to a file'
468
468
  }]).argv(process.argv, vipValidatePreflightCommand);