@automattic/vip 2.39.5 → 2.40.0-dev.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.
@@ -26,38 +26,11 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
26
26
  /**
27
27
  * Internal dependencies
28
28
  */
29
- const appQuery = `
30
- id,
31
- name,
32
- type,
33
- typeId
34
- organization { id, name },
35
- environments{
36
- id
37
- appId
38
- type
39
- name
40
- launched
41
- isK8sResident
42
- syncProgress { status }
43
- primaryDomain { name }
44
- wpSites {
45
- nodes {
46
- homeUrl
47
- id
48
- }
49
- }
50
- }
51
- `;
52
29
  const START_DEPLOY_MUTATION = (0, _graphqlTag.default)`
53
30
  mutation StartCustomDeploy($input: AppEnvironmentCustomDeployInput) {
54
31
  startCustomDeploy(input: $input) {
55
- app {
56
- id
57
- name
58
- }
59
- message
60
32
  success
33
+ message
61
34
  }
62
35
  }
63
36
  `;
@@ -90,21 +63,22 @@ async function appDeployCmd(arg = [], opts = {}) {
90
63
  const env = opts.env;
91
64
  const [fileName] = arg;
92
65
  const fileMeta = await (0, _clientFileUploader.getFileMeta)(fileName);
93
- const inputBasename = fileMeta.basename;
94
66
  debug('Options: ', opts);
95
67
  debug('Args: ', arg);
96
- const appId = env.appId;
97
- const envId = env.id;
68
+ debug('Validating custom deploy key...');
69
+ const {
70
+ appId,
71
+ envId,
72
+ ...validatedArgs
73
+ } = await (0, _customDeploy.validateCustomDeployKey)(app, env);
74
+ debug('Validated environment data: ', {
75
+ appId,
76
+ envId,
77
+ validatedArgs
78
+ });
98
79
  const track = _tracker.trackEventWithEnv.bind(null, appId, envId);
99
- if (!(0, _customDeploy.isSupportedApp)(app)) {
100
- await track('deploy_app_command_error', {
101
- error_type: 'unsupported-app'
102
- });
103
- exit.withError('The type of application you specified does not currently support deploys.');
104
- }
105
- debug('Validating custom deploy key if present...');
106
- await (0, _customDeploy.validateCustomDeployKey)(envId);
107
- await (0, _customDeploy.validateFile)(app, env, fileMeta);
80
+ debug('Validating file...');
81
+ await (0, _customDeploy.validateFile)(appId, envId, fileMeta);
108
82
  await track('deploy_app_command_execute');
109
83
 
110
84
  // Upload file as different name to avoid overwriting existing same named files
@@ -114,13 +88,12 @@ async function appDeployCmd(arg = [], opts = {}) {
114
88
  fileMeta.basename = `${datePrefix}-${fileMeta.basename}`;
115
89
  const deployMessage = opts.message ?? '';
116
90
  const forceDeploy = opts.force;
117
- const domain = env?.primaryDomain?.name ? env.primaryDomain.name : `#${env.id}`;
118
91
  if (!forceDeploy) {
119
92
  const promptParams = {
120
- launched: Boolean(env.launched),
121
- formattedEnvironment: (0, _format.formatEnvironment)(env.type),
93
+ launched: Boolean(validatedArgs.launched),
94
+ formattedEnvironment: (0, _format.formatEnvironment)(validatedArgs.envType),
122
95
  track,
123
- domain
96
+ domain: validatedArgs.primaryDomainName
124
97
  };
125
98
  await promptToContinue(promptParams);
126
99
  }
@@ -185,8 +158,8 @@ Processing the file for deployment to your environment...
185
158
  result
186
159
  } = await (0, _clientFileUploader.uploadImportSqlFileToS3)(uploadParams);
187
160
  startDeployVariables.input = {
188
- id: app.id,
189
- environmentId: env.id,
161
+ id: appId,
162
+ environmentId: envId,
190
163
  basename: fileMeta.basename,
191
164
  checksum,
192
165
  deployMessage
@@ -240,8 +213,8 @@ Processing the file for deployment to your environment...
240
213
  progressTracker.print({
241
214
  clearAfter: true
242
215
  });
243
- const deploymentsUrl = `https://dashboard.wpvip.com/apps/${appId}/${env.type}/code/deployments`;
244
- console.log(`\n✅ ${_chalk.default.bold(_chalk.default.underline(_chalk.default.magenta(inputBasename)))} has been sent for deployment to ${_chalk.default.bold(_chalk.default.blue(domain))}. \nTo check deployment status, go to ${_chalk.default.bold('VIP Dashboard')}: ${deploymentsUrl}`);
216
+ const deploymentsUrl = `https://dashboard.wpvip.com/apps/${appId}/${validatedArgs.envUniqueLabel}/code/deployments`;
217
+ console.log(`\n✅ ${_chalk.default.bold(_chalk.default.underline(_chalk.default.magenta(fileMeta.basename)))} has been sent for deployment to ${_chalk.default.bold(_chalk.default.blue(validatedArgs.primaryDomainName))}. \nTo check deployment status, go to ${_chalk.default.bold('VIP Dashboard')}: ${deploymentsUrl}`);
245
218
  }
246
219
 
247
220
  // Command examples for the `vip deploy app` help prompt
@@ -258,8 +231,5 @@ const examples = [
258
231
  description: 'Deploy the given compressed file to your site without prompting'
259
232
  }];
260
233
  void (0, _command.default)({
261
- appContext: true,
262
- appQuery,
263
- envContext: true,
264
234
  requiredArgs: 1
265
- }).examples(examples).option('message', 'Custom message for deploy').option('force', 'Skip prompt').argv(process.argv, appDeployCmd);
235
+ }).examples(examples).option('message', 'Custom message for deploy').option('force', 'Skip prompt').option('app', 'The application name or ID').option('env', 'The environment name or ID').argv(process.argv, appDeployCmd);
@@ -53,7 +53,6 @@ cmd.argv(process.argv, async (arg, opt) => {
53
53
  const lando = await (0, _devEnvironmentLando.bootstrapLando)();
54
54
  await (0, _devEnvironmentCli.validateDependencies)(lando, slug);
55
55
  debug('Args: ', arg, 'Options: ', opt);
56
- (0, _devEnvironmentCli.handleDeprecatedOptions)(opt);
57
56
  const trackingInfo = {
58
57
  slug,
59
58
  app: opt.app,
@@ -17,7 +17,6 @@ const userMap = {
17
17
  memcached: 'memcache',
18
18
  elasticsearch: 'elasticsearch',
19
19
  phpmyadmin: 'www-data',
20
- mailhog: 'mailhog',
21
20
  mailpit: 'root',
22
21
  photon: 'root'
23
22
  };
@@ -21,7 +21,6 @@ const cmd = (0, _command.default)().option('slug', 'Custom name of the dev envir
21
21
  cmd.examples(examples);
22
22
  cmd.argv(process.argv, async (arg, opt) => {
23
23
  const slug = await (0, _devEnvironmentCli.getEnvironmentName)(opt);
24
- (0, _devEnvironmentCli.handleDeprecatedOptions)(opt);
25
24
  const lando = await (0, _devEnvironmentLando.bootstrapLando)();
26
25
  await (0, _devEnvironmentCli.validateDependencies)(lando, slug);
27
26
  const trackingInfo = (0, _devEnvironmentCli.getEnvTrackingInfo)(slug);
@@ -54,7 +53,7 @@ cmd.argv(process.argv, async (arg, opt) => {
54
53
  mariadb: currentInstanceData.mariadb,
55
54
  phpmyadmin: currentInstanceData.phpmyadmin,
56
55
  xdebug: currentInstanceData.xdebug,
57
- mailpit: currentInstanceData.mailpit ?? currentInstanceData.mailhog,
56
+ mailpit: currentInstanceData.mailpit,
58
57
  photon: currentInstanceData.photon,
59
58
  mediaRedirectDomain: currentInstanceData.mediaRedirectDomain,
60
59
  multisite: false,
@@ -13,6 +13,7 @@ var _tracker = require("../lib/tracker");
13
13
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
14
14
  // eslint-disable-next-line no-duplicate-imports
15
15
 
16
+ const API_VERSION = 'v2';
16
17
  const appQuery = `
17
18
  id,
18
19
  name,
@@ -116,7 +117,8 @@ Importing Media into your App...
116
117
  environmentId: env.id,
117
118
  archiveUrl: url,
118
119
  overwriteExistingFiles,
119
- importIntermediateImages
120
+ importIntermediateImages,
121
+ apiVersion: API_VERSION
120
122
  }
121
123
  }
122
124
  });
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
 
3
3
  exports.__esModule = true;
4
- exports.isSupportedApp = isSupportedApp;
5
4
  exports.validateCustomDeployKey = validateCustomDeployKey;
6
5
  exports.validateFile = validateFile;
7
6
  var _fs = _interopRequireDefault(require("fs"));
@@ -10,7 +9,6 @@ var _api = _interopRequireDefault(require("../../lib/api"));
10
9
  var exit = _interopRequireWildcard(require("../../lib/cli/exit"));
11
10
  var _clientFileUploader = require("../../lib/client-file-uploader");
12
11
  var _fileSize = require("../../lib/constants/file-size");
13
- var _vipgo = require("../../lib/constants/vipgo");
14
12
  var _tracker = require("../../lib/tracker");
15
13
  var _customDeploy = require("../../lib/validations/custom-deploy");
16
14
  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); }
@@ -18,23 +16,28 @@ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e;
18
16
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
19
17
  const DEPLOY_MAX_FILE_SIZE = 4 * _fileSize.GB_IN_BYTES;
20
18
  const WPVIP_DEPLOY_TOKEN = process.env.WPVIP_DEPLOY_TOKEN;
21
- function isSupportedApp(app) {
22
- return _vipgo.WORDPRESS_SITE_TYPE_IDS.includes(app.typeId);
23
- }
24
- async function validateCustomDeployKey(envId) {
19
+ async function validateCustomDeployKey(app, env) {
25
20
  if (!WPVIP_DEPLOY_TOKEN) {
26
21
  exit.withError('Valid custom deploy key is required.');
27
22
  }
28
23
  const VALIDATE_CUSTOM_DEPLOY_ACCESS_MUTATION = (0, _graphqlTag.default)`
29
24
  mutation ValidateCustomDeployAccess {
30
- validateCustomDeployAccess( input: { environmentIds: ${envId} } ) {
31
- success
25
+ validateCustomDeployAccess( input: { app: "${String(app)}", env: "${String(env)}" } ) {
26
+ success,
27
+ appId,
28
+ envId,
29
+ envType,
30
+ envUniqueLabel,
31
+ primaryDomainName,
32
+ launched
32
33
  }
33
34
  }
34
35
  `;
35
- const api = (0, _api.default)();
36
+ const api = (0, _api.default)({
37
+ exitOnError: true
38
+ });
36
39
  try {
37
- await api.mutate({
40
+ const result = await api.mutate({
38
41
  mutation: VALIDATE_CUSTOM_DEPLOY_ACCESS_MUTATION,
39
42
  context: {
40
43
  headers: {
@@ -42,22 +45,24 @@ async function validateCustomDeployKey(envId) {
42
45
  }
43
46
  }
44
47
  });
48
+ if (!result.data?.validateCustomDeployAccess) {
49
+ throw new Error('Not found');
50
+ }
51
+ return result.data?.validateCustomDeployAccess;
45
52
  } catch (error) {
46
- exit.withError(`Unauthorized: Invalid or non-existent custom deploy key for environment ${envId}.`);
53
+ exit.withError(`Unauthorized: Invalid or non-existent custom deploy key for environment.`);
47
54
  }
48
55
  }
49
56
 
50
57
  /**
51
58
  * @param {FileMeta} fileMeta
52
59
  */
53
- async function validateFile(app, env, fileMeta) {
60
+ async function validateFile(appId, envId, fileMeta) {
54
61
  const {
55
62
  fileName,
56
63
  basename,
57
64
  isCompressed
58
65
  } = fileMeta;
59
- const appId = env.appId;
60
- const envId = env.id;
61
66
  const track = _tracker.trackEventWithEnv.bind(null, appId, envId);
62
67
  if (!_fs.default.existsSync(fileName)) {
63
68
  await track('deploy_app_command_error', {
@@ -9,7 +9,6 @@ exports.getEnvironmentStartCommand = getEnvironmentStartCommand;
9
9
  exports.getOptionsFromAppInfo = getOptionsFromAppInfo;
10
10
  exports.getTagChoices = getTagChoices;
11
11
  exports.handleCLIException = handleCLIException;
12
- exports.handleDeprecatedOptions = handleDeprecatedOptions;
13
12
  exports.postStart = postStart;
14
13
  exports.printTable = printTable;
15
14
  exports.processBooleanOption = processBooleanOption;
@@ -606,7 +605,7 @@ function processVersionOption(value) {
606
605
  }
607
606
  function addDevEnvConfigurationOptions(command) {
608
607
  // We leave the third parameter to undefined on some because the defaults are handled in preProcessInstanceData()
609
- return command.option('wordpress', 'Use a specific WordPress version', undefined, processVersionOption).option(['u', 'mu-plugins'], 'Use a specific mu-plugins changeset or local directory').option('app-code', 'Use the application code from a local directory or use "demo" for VIP skeleton code').option('phpmyadmin', 'Enable PHPMyAdmin component. By default it is disabled', undefined, processBooleanOption).option('xdebug', 'Enable XDebug. By default it is disabled', undefined, processBooleanOption).option('xdebug_config', 'Extra configuration to pass to xdebug via XDEBUG_CONFIG environment variable').option('elasticsearch', 'Enable Elasticsearch (needed by Enterprise Search)', undefined, processBooleanOption).option(['r', 'media-redirect-domain'], 'Domain to redirect for missing media files. This can be used to still have images without the need to import them locally.').option('php', 'Explicitly choose PHP version to use', undefined, processVersionOption).option(['G', 'mailhog'], 'Enable Mailpit. By default it is disabled (deprecated option, please use --mailpit instead)', undefined, processBooleanOption).option(['A', 'mailpit'], 'Enable Mailpit. By default it is disabled', undefined, processBooleanOption).option(['H', 'photon'], 'Enable Photon. By default it is disabled', undefined, processBooleanOption);
608
+ return command.option('wordpress', 'Use a specific WordPress version', undefined, processVersionOption).option(['u', 'mu-plugins'], 'Use a specific mu-plugins changeset or local directory').option('app-code', 'Use the application code from a local directory or use "demo" for VIP skeleton code').option('phpmyadmin', 'Enable PHPMyAdmin component. By default it is disabled', undefined, processBooleanOption).option('xdebug', 'Enable XDebug. By default it is disabled', undefined, processBooleanOption).option('xdebug_config', 'Extra configuration to pass to xdebug via XDEBUG_CONFIG environment variable').option('elasticsearch', 'Enable Elasticsearch (needed by Enterprise Search)', undefined, processBooleanOption).option(['r', 'media-redirect-domain'], 'Domain to redirect for missing media files. This can be used to still have images without the need to import them locally.').option('php', 'Explicitly choose PHP version to use', undefined, processVersionOption).option(['A', 'mailpit'], 'Enable Mailpit. By default it is disabled', undefined, processBooleanOption).option(['H', 'photon'], 'Enable Photon. By default it is disabled', undefined, processBooleanOption);
610
609
  }
611
610
 
612
611
  /**
@@ -704,13 +703,4 @@ const getVSCodeExecutable = () => {
704
703
  debug(`Could not find ${candidate} in path`);
705
704
  }
706
705
  return null;
707
- };
708
- function handleDeprecatedOptions(opts) {
709
- if (opts.mailhog) {
710
- console.warn(_chalk.default.yellow('Warning: --mailhog is deprecated and will be removed in a future release. Please use --mailpit instead.'));
711
- if (opts.mailpit === undefined) {
712
- opts.mailpit = opts.mailhog;
713
- }
714
- delete opts.mailhog;
715
- }
716
- }
706
+ };
@@ -81,7 +81,7 @@ function sanitizeConfiguration(configuration, configurationFilePath) {
81
81
  elasticsearch: stringToBooleanIfDefined(configuration.elasticsearch),
82
82
  phpmyadmin: stringToBooleanIfDefined(configuration.phpmyadmin),
83
83
  xdebug: stringToBooleanIfDefined(configuration.xdebug),
84
- mailpit: stringToBooleanIfDefined(configuration.mailpit ?? configuration.mailhog),
84
+ mailpit: stringToBooleanIfDefined(configuration.mailpit),
85
85
  'media-redirect-domain': configuration['media-redirect-domain']?.toString(),
86
86
  photon: stringToBooleanIfDefined(configuration.photon),
87
87
  meta: configurationMeta
@@ -119,7 +119,7 @@ function mergeConfigurationFileOptions(preselectedOptions, configurationFileOpti
119
119
  phpmyadmin: configurationFileOptions.phpmyadmin,
120
120
  xdebug: configurationFileOptions.xdebug,
121
121
  xdebugConfig: configurationFileOptions['xdebug-config'],
122
- mailpit: configurationFileOptions.mailpit ?? configurationFileOptions.mailhog,
122
+ mailpit: configurationFileOptions.mailpit,
123
123
  mediaRedirectDomain: configurationFileOptions['media-redirect-domain'],
124
124
  photon: configurationFileOptions.photon
125
125
  };
@@ -153,9 +153,7 @@ function preProcessInstanceData(instanceData) {
153
153
  }
154
154
 
155
155
  // Mailpit migration
156
- if (!newInstanceData.mailpit) {
157
- newInstanceData.mailpit = newInstanceData.mailhog ?? false;
158
- }
156
+ newInstanceData.mailpit ??= false;
159
157
 
160
158
  // MariaDB migration
161
159
  if (!newInstanceData.mariadb) {
@@ -308,10 +306,6 @@ function readEnvironmentData(slug) {
308
306
  // clientCode was renamed to appCode
309
307
  instanceData.appCode = instanceData.clientCode;
310
308
  }
311
- if (instanceData.mailhog) {
312
- instanceData.mailpit = instanceData.mailhog;
313
- delete instanceData.mailhog;
314
- }
315
309
  return instanceData;
316
310
  }
317
311
 
@@ -5,6 +5,7 @@ exports.default = void 0;
5
5
  exports.getGlyphForStatus = getGlyphForStatus;
6
6
  exports.mediaImportCheckStatus = mediaImportCheckStatus;
7
7
  var _chalk = _interopRequireDefault(require("chalk"));
8
+ var _enquirer = require("enquirer");
8
9
  var _graphqlTag = _interopRequireDefault(require("graphql-tag"));
9
10
  var _promises = require("node:fs/promises");
10
11
  var _nodePath = require("node:path");
@@ -36,6 +37,7 @@ const IMPORT_MEDIA_PROGRESS_QUERY = (0, _graphqlTag.default)`
36
37
  fileName
37
38
  errors
38
39
  }
40
+ fileErrorsUrl
39
41
  }
40
42
  }
41
43
  }
@@ -216,25 +218,76 @@ ${maybeExitPrompt}
216
218
  // Kick off the check
217
219
  void checkStatus(IMPORT_MEDIA_PROGRESS_POLL_INTERVAL);
218
220
  });
221
+ async function exportFailureDetails(fileErrors) {
222
+ const formattedData = buildFileErrors(fileErrors, exportFileErrorsToJson);
223
+ const errorsFile = `media-import-${app.name ?? ''}-${Date.now()}${exportFileErrorsToJson ? '.json' : '.txt'}`;
224
+ try {
225
+ await (0, _promises.writeFile)(errorsFile, formattedData);
226
+ progressTracker.suffix += `${_chalk.default.yellow(`⚠️ All errors have been exported to ${_chalk.default.bold((0, _nodePath.resolve)(errorsFile))}`)}`;
227
+ } catch (writeFileErr) {
228
+ progressTracker.suffix += `${_chalk.default.red(`Could not export errors to file\n${writeFileErr.message}`)}`;
229
+ }
230
+ }
231
+ async function fetchFailureDetails(fileErrorsUrl) {
232
+ progressTracker.suffix += `
233
+ =============================================================
234
+ Downloading errors details from ${fileErrorsUrl}...
235
+ \n`;
236
+ try {
237
+ const response = await fetch(fileErrorsUrl);
238
+ return await response.json();
239
+ } catch (err) {
240
+ progressTracker.suffix += `${_chalk.default.red(`Could not download file import errors report\n${err.message}`)}`;
241
+ throw err;
242
+ }
243
+ }
244
+ async function promptFailureDetailsDownload(fileErrorsUrl) {
245
+ progressTracker.suffix += `${_chalk.default.yellow(`⚠️ Error details can be found on ${_chalk.default.bold(fileErrorsUrl)}\n${_chalk.default.italic.yellow('(This link will be valid for the next 15 minutes. The report is retained for 7 days from the completion of the import.)')}. `)}\n`;
246
+ progressTracker.print({
247
+ clearAfter: true
248
+ });
249
+ const failureDetails = await (0, _enquirer.prompt)({
250
+ type: 'confirm',
251
+ name: 'download',
252
+ message: 'Download file import errors report now?'
253
+ });
254
+ if (!failureDetails.download) {
255
+ return;
256
+ }
257
+ const failureDetailsErrors = await fetchFailureDetails(fileErrorsUrl);
258
+ await exportFailureDetails(failureDetailsErrors);
259
+ }
260
+ function printFileErrorsReportLinkExpiredError(results) {
261
+ if (results.filesTotal && results.filesProcessed && results.filesTotal !== results.filesProcessed) {
262
+ const errorsFound = results.filesTotal - results.filesProcessed;
263
+ progressTracker.suffix += `${_chalk.default.yellow(`⚠️ ${errorsFound} error(s) were found. File import errors report link expired.`)}`;
264
+ }
265
+ }
266
+ async function printFailureDetails(fileErrors, results) {
267
+ progressTracker.suffix += `${_chalk.default.yellow(`⚠️ ${fileErrors.length} file import error(s) were found`)}`;
268
+ if ((results.filesTotal ?? 0) - (results.filesProcessed ?? 0) !== fileErrors.length) {
269
+ progressTracker.suffix += `. ${_chalk.default.italic.yellow('File import errors report size threshold reached.')}`;
270
+ }
271
+ await exportFailureDetails(fileErrors);
272
+ }
219
273
  try {
220
274
  const results = await getResults();
221
275
  overallStatus = results.status ?? 'unknown';
222
276
  progressTracker.stopPrinting();
223
277
  setProgressTrackerSuffix();
224
278
  progressTracker.print();
225
- const fileErrors = results.failureDetails?.fileErrors ?? [];
226
- if (fileErrors.length > 0) {
227
- progressTracker.suffix += `${_chalk.default.yellow(`⚠️ ${fileErrors.length} file error(s) have been extracted`)}`;
228
- if ((results.filesTotal ?? 0) - (results.filesProcessed ?? 0) !== fileErrors.length) {
229
- progressTracker.suffix += `. ${_chalk.default.italic.yellow('File-errors report size threshold reached.')}`;
230
- }
231
- const formattedData = buildFileErrors(fileErrors, exportFileErrorsToJson);
232
- const errorsFile = `media-import-${app.name ?? ''}-${Date.now()}${exportFileErrorsToJson ? '.json' : '.txt'}`;
233
- try {
234
- await (0, _promises.writeFile)(errorsFile, formattedData);
235
- progressTracker.suffix += `\n\n${_chalk.default.yellow(`All errors have been exported to ${_chalk.default.bold((0, _nodePath.resolve)(errorsFile))}`)}\n\n`;
236
- } catch (writeFileErr) {
237
- progressTracker.suffix += `\n\n${_chalk.default.red(`Could not export errors to file\n${writeFileErr.message}`)}\n\n`;
279
+ if (results.failureDetails?.fileErrorsUrl) {
280
+ await promptFailureDetailsDownload(results.failureDetails.fileErrorsUrl);
281
+ } else {
282
+ const fileErrors = results.failureDetails?.fileErrors ?? [];
283
+ if (fileErrors.length > 0) {
284
+ // Errors were observed and are present in the dto
285
+ // Fall back to exporting errors to local file
286
+ await printFailureDetails(fileErrors, results);
287
+ } else {
288
+ // Errors are not present in the dto
289
+ // And file error details report link is not available
290
+ printFileErrorsReportLinkExpiredError(results);
238
291
  }
239
292
  }
240
293
 
package/docs/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  ## Changelog
2
2
 
3
+ ### 2.39.6
4
+ * Custom Deploys: Update deploy validation to run fully off of the custom deploy token. There are breaking changes associated with these updates, so the VIP CLI must be updated to this latest version in order to continue using the custom deploy functionality.
5
+
6
+ **Full Changelog**: https://github.com/Automattic/vip-cli/compare/2.39.5...2.39.6
7
+
3
8
  ### 2.39.5
4
9
  * Custom Deploys: Pass in deploy token to StartCustomDeploy mutation and allow `getSignedUploadRequestData()` to accept another bearer token
5
10