@automattic/vip 3.4.0 → 3.4.2
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/assets/dev-env.lando.template.yml.ejs +2 -0
- package/dist/bin/vip-app-deploy-validate.js +7 -5
- package/dist/bin/vip-app-deploy.js +10 -8
- package/dist/bin/vip-app-list.js +17 -12
- package/dist/bin/vip-app.js +1 -1
- package/dist/bin/vip-cache-purge-url.js +8 -6
- package/dist/bin/vip-cache.js +12 -2
- package/dist/bin/vip-config-envvar-delete.js +1 -1
- package/dist/bin/vip-config-envvar-set.js +1 -1
- package/dist/bin/vip.js +1 -1
- package/dist/commands/export-sql.js +9 -1
- package/dist/lib/dev-environment/dev-environment-cli.js +4 -1
- package/dist/lib/dev-environment/dev-environment-core.js +2 -2
- package/dist/lib/dev-environment/dev-environment-lando.js +2 -1
- package/dist/lib/retry.js +153 -0
- package/dist/lib/validations/custom-deploy.js +2 -2
- package/dist/lib/validations/sql.js +8 -2
- package/docs/CHANGELOG.md +10 -0
- package/npm-shrinkwrap.json +21 -21
- package/package.json +2 -2
|
@@ -20,6 +20,7 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
|
|
|
20
20
|
* Internal dependencies
|
|
21
21
|
*/
|
|
22
22
|
const debug = (0, _debug.default)('@automattic/vip:bin:vip-app-deploy-validate');
|
|
23
|
+
const baseUsage = 'vip app deploy validate';
|
|
23
24
|
async function appDeployValidateCmd(arg = [], opts = {}) {
|
|
24
25
|
const app = opts.app;
|
|
25
26
|
const env = opts.env;
|
|
@@ -37,14 +38,15 @@ async function appDeployValidateCmd(arg = [], opts = {}) {
|
|
|
37
38
|
} else {
|
|
38
39
|
await (0, _customDeploy2.validateTarFile)(fileName);
|
|
39
40
|
}
|
|
40
|
-
console.log(_chalk.default.green('✓ Compressed file has been successfully validated with no errors
|
|
41
|
+
console.log(_chalk.default.green('✓ Compressed file has been successfully validated with no errors.'));
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
// Command examples for the `vip app deploy validate` help prompt
|
|
44
45
|
const examples = [{
|
|
45
|
-
usage: 'vip app
|
|
46
|
-
description: 'Validate the
|
|
46
|
+
usage: 'vip app deploy validate file.tar.gz',
|
|
47
|
+
description: 'Validate the directory structure of the local archived file named "file.tar.gz".'
|
|
47
48
|
}];
|
|
48
49
|
void (0, _command.default)({
|
|
49
|
-
requiredArgs: 1
|
|
50
|
-
|
|
50
|
+
requiredArgs: 1,
|
|
51
|
+
usage: baseUsage
|
|
52
|
+
}).examples(examples).argv(process.argv, appDeployValidateCmd);
|
|
@@ -35,6 +35,7 @@ const START_DEPLOY_MUTATION = (0, _graphqlTag.default)`
|
|
|
35
35
|
}
|
|
36
36
|
`;
|
|
37
37
|
const debug = (0, _debug.default)('@automattic/vip:bin:vip-app-deploy');
|
|
38
|
+
const baseUsage = 'vip app deploy';
|
|
38
39
|
const DEPLOY_PREFLIGHT_PROGRESS_STEPS = [{
|
|
39
40
|
id: 'upload',
|
|
40
41
|
name: 'Uploading file'
|
|
@@ -221,15 +222,16 @@ Processing the file for deployment to your environment...
|
|
|
221
222
|
const examples = [
|
|
222
223
|
// `app` subcommand
|
|
223
224
|
{
|
|
224
|
-
usage: 'vip app
|
|
225
|
-
description: 'Deploy
|
|
225
|
+
usage: 'WPVIP_DEPLOY_TOKEN=1234 vip @example-app.develop app deploy file.zip',
|
|
226
|
+
description: 'Deploy a local archived file named "file.zip" that contains application code to a VIP Platform environment that has Custom Deployment enabled.'
|
|
226
227
|
}, {
|
|
227
|
-
usage: 'vip app
|
|
228
|
-
description: '
|
|
228
|
+
usage: 'WPVIP_DEPLOY_TOKEN=1234 vip @example-app.develop app deploy file.tgz --message="A description for this deploy"',
|
|
229
|
+
description: 'Add a descriptive message for the Custom Deployment of the archived file named "file.tgz".'
|
|
229
230
|
}, {
|
|
230
|
-
usage: 'vip app
|
|
231
|
-
description: '
|
|
231
|
+
usage: 'WPVIP_DEPLOY_TOKEN=1234 vip @example-app.develop app deploy file.tar.gz --skip-confirmation',
|
|
232
|
+
description: 'Skip the confirmation prompt for the Custom Deployment of the archived file named "file.tar.gz" to the environment.'
|
|
232
233
|
}];
|
|
233
234
|
void (0, _command.default)({
|
|
234
|
-
requiredArgs: 1
|
|
235
|
-
|
|
235
|
+
requiredArgs: 1,
|
|
236
|
+
usage: baseUsage
|
|
237
|
+
}).command('validate', 'Validate the directory structure of an archived file.').examples(examples).option('message', 'Add a description of a deployment.').option('skip-confirmation', 'Skip the confirmation prompt.').option('force', 'Skip confirmation prompt (deprecated)').option('app', 'Target an application. Accepts a string value for the application name or an integer for the application ID.').option('env', 'Target an environment. Accepts a string value for the environment type.').argv(process.argv, appDeployCmd);
|
package/dist/bin/vip-app-list.js
CHANGED
|
@@ -6,27 +6,32 @@ var _api = _interopRequireDefault(require("../lib/api"));
|
|
|
6
6
|
var _command = _interopRequireDefault(require("../lib/cli/command"));
|
|
7
7
|
var _tracker = require("../lib/tracker");
|
|
8
8
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
|
+
const baseUsage = 'vip app list';
|
|
9
10
|
(0, _command.default)({
|
|
10
|
-
format: true
|
|
11
|
-
|
|
11
|
+
format: true,
|
|
12
|
+
usage: baseUsage
|
|
13
|
+
}).examples([{
|
|
14
|
+
usage: baseUsage + '\n' + ' - ┌──────┬─────────────────┬─────────────────────────────────┐\n' + ' - │ id │ name │ repo │\n' + ' - ┌──────┬─────────────────┬─────────────────────────────────┐\n' + ' - │ 8886 │ example-app │ wpcomvip/my-org-example-app │\n' + ' - ┌──────┬─────────────────┬─────────────────────────────────┐\n' + ' - │ 4325 │ mytestmultisite │ wpcomvip/my-org-mytestmultisite │\n' + ' - └──────┴─────────────────┴─────────────────────────────────┘\n',
|
|
15
|
+
description: 'Retrieve a list of applications that can be accessed by the current authenticated VIP-CLI user.'
|
|
16
|
+
}]).argv(process.argv, async () => {
|
|
12
17
|
const api = (0, _api.default)();
|
|
13
18
|
await (0, _tracker.trackEvent)('app_list_command_execute');
|
|
14
19
|
let response;
|
|
15
20
|
try {
|
|
16
21
|
response = await api.query({
|
|
17
22
|
query: (0, _graphqlTag.default)`
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
query Apps($first: Int, $after: String) {
|
|
24
|
+
apps(first: $first, after: $after) {
|
|
25
|
+
total
|
|
26
|
+
nextCursor
|
|
27
|
+
edges {
|
|
28
|
+
id
|
|
29
|
+
name
|
|
30
|
+
repo
|
|
31
|
+
}
|
|
26
32
|
}
|
|
27
33
|
}
|
|
28
|
-
|
|
29
|
-
`,
|
|
34
|
+
`,
|
|
30
35
|
variables: {
|
|
31
36
|
first: 100,
|
|
32
37
|
after: null // TODO make dynamic
|
package/dist/bin/vip-app.js
CHANGED
|
@@ -11,7 +11,7 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
|
|
|
11
11
|
(0, _command.default)({
|
|
12
12
|
requiredArgs: 1,
|
|
13
13
|
format: true
|
|
14
|
-
}).example('vip app
|
|
14
|
+
}).example('vip app list', 'Retrieve a list of applications that can be accessed by the current authenticated VIP-CLI user.').example('vip app example-app', 'Retrieve information about the application named "example-app" and its environments.').example('WPVIP_DEPLOY_TOKEN=1234 vip @example-app.develop app deploy file.zip', 'Deploy a local archived file named "file.zip" that contains application code to a VIP Platform environment that has Custom Deployment enabled.').command('list', 'Retrieve a list of applications that can be accessed by the current authenticated VIP-CLI user.').command('deploy', 'Deploy an archived file of application code to an environment that has Custom Deployment enabled.').argv(process.argv, async arg => {
|
|
15
15
|
await (0, _tracker.trackEvent)('app_command_execute');
|
|
16
16
|
let res;
|
|
17
17
|
try {
|
|
@@ -11,12 +11,14 @@ var _tracker = require("../lib/tracker");
|
|
|
11
11
|
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); }
|
|
12
12
|
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; }
|
|
13
13
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
14
|
+
const usage = 'vip cache purge-url';
|
|
15
|
+
const exampleUsage = 'vip @example-app.develop cache purge-url';
|
|
14
16
|
const examples = [{
|
|
15
|
-
usage:
|
|
16
|
-
description: 'Purge
|
|
17
|
+
usage: `${exampleUsage} https://example-app-develop.go-vip.co/sample-page/` + '\n - Purged URL: https://example-app.develop.go-vip.co/sample-page/',
|
|
18
|
+
description: 'Purge the page cache for a single URL.'
|
|
17
19
|
}, {
|
|
18
|
-
usage:
|
|
19
|
-
description: 'Purge multiple URLs
|
|
20
|
+
usage: `${exampleUsage} --from-file=./urls.txt`,
|
|
21
|
+
description: 'Purge the page cache for multiple URLs, each listed on a single line in a local file.'
|
|
20
22
|
}];
|
|
21
23
|
async function cachePurgeCommand(urls = [], opt = {}) {
|
|
22
24
|
const trackingParams = {
|
|
@@ -59,5 +61,5 @@ async function cachePurgeCommand(urls = [], opt = {}) {
|
|
|
59
61
|
appQuery: _cachePurge.appQuery,
|
|
60
62
|
envContext: true,
|
|
61
63
|
wildcardCommand: true,
|
|
62
|
-
usage
|
|
63
|
-
}).option('from-file', 'Read URLs from file
|
|
64
|
+
usage
|
|
65
|
+
}).option('from-file', 'Read one or more URLs from a file, each listed on a single line.').examples(examples).argv(process.argv, cachePurgeCommand);
|
package/dist/bin/vip-cache.js
CHANGED
|
@@ -3,6 +3,16 @@
|
|
|
3
3
|
|
|
4
4
|
var _command = _interopRequireDefault(require("../lib/cli/command"));
|
|
5
5
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
6
|
+
const usage = 'vip cache';
|
|
7
|
+
const exampleUsage = 'vip @example-app.develop cache';
|
|
8
|
+
const examples = [{
|
|
9
|
+
usage: `${exampleUsage} purge-url https://example-app-develop.go-vip.co/sample-page/` + '\n - Purged URL: https://example-app.develop.go-vip.co/sample-page/',
|
|
10
|
+
description: 'Purge the page cache for a single URL.'
|
|
11
|
+
}, {
|
|
12
|
+
usage: `${exampleUsage} purge-url --from-file=./urls.txt`,
|
|
13
|
+
description: 'Purge the page cache for multiple URLs, each listed on a single line in a local file.'
|
|
14
|
+
}];
|
|
6
15
|
(0, _command.default)({
|
|
7
|
-
requiredArgs: 1
|
|
8
|
-
|
|
16
|
+
requiredArgs: 1,
|
|
17
|
+
usage
|
|
18
|
+
}).command('purge-url', 'Purge page cache for one or more URLs.').examples(examples).argv(process.argv);
|
|
@@ -46,7 +46,7 @@ async function deleteEnvVarCommand(arg, opt) {
|
|
|
46
46
|
await (0, _tracker.trackEvent)('envvar_delete_user_cancelled_input', trackingParams);
|
|
47
47
|
(0, _input.cancel)();
|
|
48
48
|
});
|
|
49
|
-
if (!(await (0, _input.confirm)(`Are you sure? ${_chalk.default.bold.red('Deletion is permanent')}
|
|
49
|
+
if (!(await (0, _input.confirm)(`Are you sure? ${_chalk.default.bold.red('Deletion is permanent')}`))) {
|
|
50
50
|
await (0, _tracker.trackEvent)('envvar_delete_user_cancelled_confirmation', trackingParams);
|
|
51
51
|
(0, _input.cancel)();
|
|
52
52
|
}
|
|
@@ -65,7 +65,7 @@ async function setEnvVarCommand(arg, opt) {
|
|
|
65
65
|
console.log('===== Received value printed above =====');
|
|
66
66
|
console.log();
|
|
67
67
|
}
|
|
68
|
-
if (!(await (0, _input.confirm)(`Please ${_chalk.default.bold('confirm')} the input value above
|
|
68
|
+
if (!(await (0, _input.confirm)(`Please ${_chalk.default.bold('confirm')} the input value above`))) {
|
|
69
69
|
await (0, _tracker.trackEvent)('envvar_set_user_cancelled_confirmation', trackingParams);
|
|
70
70
|
(0, _input.cancel)();
|
|
71
71
|
}
|
package/dist/bin/vip.js
CHANGED
|
@@ -22,7 +22,7 @@ const tokenURL = 'https://dashboard.wpvip.com/me/cli/token';
|
|
|
22
22
|
const customDeployToken = process.env.WPVIP_DEPLOY_TOKEN;
|
|
23
23
|
const runCmd = async function () {
|
|
24
24
|
const cmd = (0, _command.default)();
|
|
25
|
-
cmd.command('logout', 'Log out the current authenticated VIP-CLI user.').command('app', '
|
|
25
|
+
cmd.command('logout', 'Log out the current authenticated VIP-CLI user.').command('app', 'Interact with applications that the current authenticated VIP-CLI user has permission to access.').command('backup', 'Generate a backup of an environment.').command('cache', 'Manage page cache for an environment.').command('config', 'Manage environment configurations.').command('dev-env', 'Create and manage VIP Local Development Environments.').command('export', 'Export a copy of data associated with an 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('slowlogs', 'Retrieve MySQL slow query logs from an environment.').command('db', "Access an environment's database.").command('sync', 'Sync production to a development environment').command('whoami', 'Retrieve details about the current authenticated VIP-CLI user.').command('validate', 'Validate your VIP application and environment').command('wp', 'Run WP CLI commands against an environment');
|
|
26
26
|
cmd.argv(process.argv);
|
|
27
27
|
};
|
|
28
28
|
|
|
@@ -13,6 +13,7 @@ var _backupStorageAvailability = require("../lib/backup-storage-availability/bac
|
|
|
13
13
|
var exit = _interopRequireWildcard(require("../lib/cli/exit"));
|
|
14
14
|
var _format = require("../lib/cli/format");
|
|
15
15
|
var _progress = require("../lib/cli/progress");
|
|
16
|
+
var _retry = require("../lib/retry");
|
|
16
17
|
var _utils = require("../lib/utils");
|
|
17
18
|
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
19
|
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; }
|
|
@@ -83,7 +84,7 @@ const CREATE_EXPORT_JOB_MUTATION = exports.CREATE_EXPORT_JOB_MUTATION = (0, _gra
|
|
|
83
84
|
* @param {number} envId Environment ID
|
|
84
85
|
* @return {Promise} A promise which resolves to the latest backup and job status
|
|
85
86
|
*/
|
|
86
|
-
async function
|
|
87
|
+
async function fetchLatestBackupAndJobStatusBase(appId, envId) {
|
|
87
88
|
const api = (0, _api.default)();
|
|
88
89
|
const response = await api.query({
|
|
89
90
|
query: BACKUP_AND_JOB_STATUS_QUERY,
|
|
@@ -107,6 +108,13 @@ async function fetchLatestBackupAndJobStatus(appId, envId) {
|
|
|
107
108
|
jobs
|
|
108
109
|
};
|
|
109
110
|
}
|
|
111
|
+
async function fetchLatestBackupAndJobStatus(appId, envId) {
|
|
112
|
+
return await (0, _retry.retry)({
|
|
113
|
+
retryOnlyIf: options => {
|
|
114
|
+
return (options.error.message || '').indexOf('Unexpected token < in JSON at position 0') !== -1;
|
|
115
|
+
}
|
|
116
|
+
}, () => fetchLatestBackupAndJobStatusBase(appId, envId));
|
|
117
|
+
}
|
|
110
118
|
|
|
111
119
|
/**
|
|
112
120
|
* Generates a download link for a backup
|
|
@@ -512,7 +512,10 @@ async function promptForWordPress(defaultObject) {
|
|
|
512
512
|
|
|
513
513
|
// image with selection
|
|
514
514
|
const tagChoices = await getTagChoices();
|
|
515
|
-
|
|
515
|
+
if (typeof defaultObject === 'string' && !tagChoices.find(choice => choice.value === defaultObject)) {
|
|
516
|
+
throw new Error(`Unknown or unsupported WordPress version: ${defaultObject}.`);
|
|
517
|
+
}
|
|
518
|
+
let option = typeof defaultObject === 'string' ? defaultObject : defaultObject?.tag ?? tagChoices[0].value;
|
|
516
519
|
if (isStdinTTY) {
|
|
517
520
|
const message = `${messagePrefix}Which version would you like`;
|
|
518
521
|
const selectTag = new _enquirer.Select({
|
|
@@ -577,7 +577,7 @@ async function maybeUpdateWordPressImage(lando, slug) {
|
|
|
577
577
|
type: 'select',
|
|
578
578
|
name: 'upgrade',
|
|
579
579
|
message: 'Would you like to upgrade WordPress? ',
|
|
580
|
-
choices: ['
|
|
580
|
+
choices: ['yes', 'no', "no (don't ask anymore)"]
|
|
581
581
|
});
|
|
582
582
|
|
|
583
583
|
// If the user takes the new WP version path
|
|
@@ -585,7 +585,7 @@ async function maybeUpdateWordPressImage(lando, slug) {
|
|
|
585
585
|
console.log('Upgrading from: ' + _chalk.default.yellow(currentWordPressTag) + ' to:');
|
|
586
586
|
|
|
587
587
|
// Select a new image
|
|
588
|
-
const choice = await (0, _devEnvironmentCli.promptForWordPress)(null);
|
|
588
|
+
const choice = await (0, _devEnvironmentCli.promptForWordPress)(newestWordPressImage?.tag ?? null);
|
|
589
589
|
const version = versions.find(({
|
|
590
590
|
tag
|
|
591
591
|
}) => tag.trim() === choice.tag.trim());
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
exports.__esModule = true;
|
|
4
|
+
exports.EXPONENTIAL_BACKOFF_STARTING_IN_50_MS = exports.EXPONENTIAL_BACKOFF_STARTING_IN_200_MS = exports.EXPONENTIAL_BACKOFF_STARTING_IN_100_MS = void 0;
|
|
5
|
+
exports.exponentialBackoff = exponentialBackoff;
|
|
6
|
+
exports.retry = retry;
|
|
7
|
+
// copied over from our internal lib
|
|
8
|
+
|
|
9
|
+
const EXPONENTIAL_BACKOFF_STARTING_IN_50_MS = exports.EXPONENTIAL_BACKOFF_STARTING_IN_50_MS = exponentialBackoff(50);
|
|
10
|
+
const EXPONENTIAL_BACKOFF_STARTING_IN_100_MS = exports.EXPONENTIAL_BACKOFF_STARTING_IN_100_MS = exponentialBackoff(100);
|
|
11
|
+
const EXPONENTIAL_BACKOFF_STARTING_IN_200_MS = exports.EXPONENTIAL_BACKOFF_STARTING_IN_200_MS = exponentialBackoff(200);
|
|
12
|
+
const MAX_INTERVAL_BETWEEN_ATTEMPTS = 600000; // 10 minutes
|
|
13
|
+
|
|
14
|
+
const NOOP = async () => {};
|
|
15
|
+
const DEFAULT_OPTIONS = {
|
|
16
|
+
maxRetries: 4,
|
|
17
|
+
interval: EXPONENTIAL_BACKOFF_STARTING_IN_100_MS,
|
|
18
|
+
retryOnlyIf: () => Promise.resolve(true),
|
|
19
|
+
onRetry: NOOP,
|
|
20
|
+
onFailedAttempt: NOOP
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* We don't need a strict Error instance, which can help in cases (probably
|
|
24
|
+
* just tests) where code throws an object instead of an Error instance. We
|
|
25
|
+
* really just need to check for an object with a message property.
|
|
26
|
+
*/
|
|
27
|
+
function isErrorLike(value) {
|
|
28
|
+
return value instanceof Error || null !== value && 'object' === typeof value && 'message' in value;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Execute a <task> and retries <options.maxRetries> times while it fails.
|
|
33
|
+
* @param {Object} [options] - The options.
|
|
34
|
+
* @param {number} [options.maxRetries] - The max number of retry attempts to be executed.
|
|
35
|
+
* @param {function|number} [options.interval] - The time to wait between retries in milliseconds.
|
|
36
|
+
* It can be either a number or a function.
|
|
37
|
+
* Function arguments: retryAttemptNumber.
|
|
38
|
+
* Expected return: a number representing the time in milliseconds.
|
|
39
|
+
* @param {function} [options.retryOnlyIf] - A function used to determine if a retry should be attempted.
|
|
40
|
+
* Arguments: Object<{ error, attemptNumber }>.
|
|
41
|
+
* Expected return: boolean|Promise<boolean>.
|
|
42
|
+
* @param {function} [options.onRetry] - A callback function executed right before a retry attempt.
|
|
43
|
+
* Arguments: Object<{ retryNumber: attemptNumber }>.
|
|
44
|
+
* Expected return: void|Promise<void>.
|
|
45
|
+
* @param {function} [options.onFailedAttempt] - A callback function executed right after a failed attempt.
|
|
46
|
+
* Arguments: Object<{ error, attemptNumber, attemptDuration }>.
|
|
47
|
+
* Expected return: void|Promise<void>.
|
|
48
|
+
* @param {function} task - The task to be executed.
|
|
49
|
+
* @return {any} - The value returned by the provided <task> function when it is successfully executed
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
async function retry(optionsOrTask, task) {
|
|
53
|
+
let options = optionsOrTask;
|
|
54
|
+
if (arguments.length < 3 && typeof optionsOrTask === 'function') {
|
|
55
|
+
task = optionsOrTask;
|
|
56
|
+
options = {};
|
|
57
|
+
}
|
|
58
|
+
const finalOptions = {
|
|
59
|
+
...DEFAULT_OPTIONS,
|
|
60
|
+
...options
|
|
61
|
+
};
|
|
62
|
+
const {
|
|
63
|
+
maxRetries,
|
|
64
|
+
interval,
|
|
65
|
+
retryOnlyIf,
|
|
66
|
+
onRetry,
|
|
67
|
+
onFailedAttempt
|
|
68
|
+
} = finalOptions;
|
|
69
|
+
validateTask(task);
|
|
70
|
+
validateOptions(finalOptions);
|
|
71
|
+
const maxAttempts = maxRetries + 1;
|
|
72
|
+
for (let attemptNumber = 1; attemptNumber <= maxAttempts; attemptNumber++) {
|
|
73
|
+
/* eslint-disable no-await-in-loop */
|
|
74
|
+
const attemptStart = Date.now();
|
|
75
|
+
try {
|
|
76
|
+
return await task();
|
|
77
|
+
} catch (caughtError) {
|
|
78
|
+
const attemptEnd = Date.now();
|
|
79
|
+
const attemptDuration = attemptEnd - attemptStart;
|
|
80
|
+
const attemptStartTime = new Date(attemptStart);
|
|
81
|
+
const attemptEndTime = new Date(attemptEnd);
|
|
82
|
+
const error = isErrorLike(caughtError) ? caughtError : new Error(String(caughtError));
|
|
83
|
+
await onFailedAttempt({
|
|
84
|
+
error,
|
|
85
|
+
attemptNumber,
|
|
86
|
+
attemptDuration,
|
|
87
|
+
attemptStartTime,
|
|
88
|
+
attemptEndTime
|
|
89
|
+
});
|
|
90
|
+
const shouldRetry = await retryOnlyIf({
|
|
91
|
+
error,
|
|
92
|
+
attemptNumber
|
|
93
|
+
});
|
|
94
|
+
if (attemptNumber === maxAttempts || shouldRetry !== true) {
|
|
95
|
+
throw error;
|
|
96
|
+
}
|
|
97
|
+
await awaitInterval(interval, attemptNumber);
|
|
98
|
+
await onRetry({
|
|
99
|
+
retryNumber: attemptNumber
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
throw new Error('Error, wrong retry options set');
|
|
104
|
+
}
|
|
105
|
+
function isValidInterval(interval) {
|
|
106
|
+
if ('function' === typeof interval) {
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
return typeof interval === 'number' && Number.isInteger(interval) && interval >= 0 && interval <= MAX_INTERVAL_BETWEEN_ATTEMPTS;
|
|
110
|
+
}
|
|
111
|
+
async function awaitInterval(interval, attemptNumber) {
|
|
112
|
+
let newInterval;
|
|
113
|
+
if (typeof interval === 'function') {
|
|
114
|
+
newInterval = interval(attemptNumber);
|
|
115
|
+
if (!isValidInterval(newInterval)) {
|
|
116
|
+
throw new Error(`Invalid calculated interval for retry attempt ${attemptNumber}: "${newInterval}" (type: ${typeof newInterval})`);
|
|
117
|
+
}
|
|
118
|
+
} else {
|
|
119
|
+
newInterval = interval;
|
|
120
|
+
}
|
|
121
|
+
return new Promise(resolve => setTimeout(resolve, newInterval));
|
|
122
|
+
}
|
|
123
|
+
function validateTask(task) {
|
|
124
|
+
if (typeof task !== 'function') {
|
|
125
|
+
throw new Error('Invalid task: it should be a function');
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
function validateOptions({
|
|
129
|
+
maxRetries,
|
|
130
|
+
interval,
|
|
131
|
+
onFailedAttempt,
|
|
132
|
+
retryOnlyIf
|
|
133
|
+
}) {
|
|
134
|
+
if (!Number.isInteger(maxRetries) || maxRetries < 0) {
|
|
135
|
+
throw new Error('Invalid option "maxRetries": it should be an integer number >= 0');
|
|
136
|
+
}
|
|
137
|
+
if (!isValidInterval(interval)) {
|
|
138
|
+
const definition = `an integer number between 0 and ${MAX_INTERVAL_BETWEEN_ATTEMPTS}`;
|
|
139
|
+
throw new Error(`Invalid option "interval": it should be either ${definition}, or a synchronous function which returns ${definition}`);
|
|
140
|
+
}
|
|
141
|
+
if (!isValidInterval(interval)) {
|
|
142
|
+
throw new Error('Invalid option "interval": it should be either an integer number >= 0, or a synchronous function which returns an integer number >= 0');
|
|
143
|
+
}
|
|
144
|
+
if (typeof onFailedAttempt !== 'function') {
|
|
145
|
+
throw new Error('Invalid option "onFailedAttempt": it should be a function');
|
|
146
|
+
}
|
|
147
|
+
if (typeof retryOnlyIf !== 'function') {
|
|
148
|
+
throw new Error('Invalid option "retryOnlyIf": it should be a function');
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
function exponentialBackoff(startMilliseconds) {
|
|
152
|
+
return retryAttemptNumber => Math.pow(2, retryAttemptNumber - 1) * startMilliseconds;
|
|
153
|
+
}
|
|
@@ -15,9 +15,9 @@ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return
|
|
|
15
15
|
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; }
|
|
16
16
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
17
17
|
const errorMessages = {
|
|
18
|
-
missingThemes: 'Missing `themes` directory from root folder
|
|
18
|
+
missingThemes: 'Missing `themes` directory from root folder.',
|
|
19
19
|
symlink: 'Symlink detected: ',
|
|
20
|
-
singleRootDir: 'The compressed file must contain a single root directory
|
|
20
|
+
singleRootDir: 'The compressed file must contain a single root directory.',
|
|
21
21
|
invalidExt: 'Invalid file extension. Please provide a .zip, .tar.gz, or a .tgz file.',
|
|
22
22
|
invalidChars: (filename, invalidChars) => `Filename ${filename} contains disallowed characters: ${invalidChars}`
|
|
23
23
|
};
|
|
@@ -271,9 +271,15 @@ const checks = {
|
|
|
271
271
|
recommendation: ''
|
|
272
272
|
},
|
|
273
273
|
siteHomeUrlLando: {
|
|
274
|
-
matcher: "'(siteurl|home)',\\s?'(
|
|
274
|
+
matcher: "'(siteurl|home)',\\s?'([^']+)'",
|
|
275
275
|
matchHandler: (lineNumber, results, expectedDomain) => {
|
|
276
|
-
|
|
276
|
+
let foundDomain = results[2];
|
|
277
|
+
if (!/^https?:\/\//i.test(foundDomain)) {
|
|
278
|
+
return {
|
|
279
|
+
falsePositive: true
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
foundDomain = foundDomain.replace(/^https?:\/\//, '');
|
|
277
283
|
if (!foundDomain.trim()) {
|
|
278
284
|
return {
|
|
279
285
|
falsePositive: true
|
package/docs/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
## Changelog
|
|
2
2
|
|
|
3
|
+
### 3.4.1
|
|
4
|
+
|
|
5
|
+
* Updating vip cache commands descriptions and examples to follow the VIP-CLI style guide
|
|
6
|
+
* Dev-env: Upgrade WordPress upgrade prompt to "yes" and pre-select latest version not trunk
|
|
7
|
+
* build(deps-dev): bump @types/node from 18.19.36 to 18.19.37
|
|
8
|
+
* fix(dev-env): retry healthcheck only on 502 and 504 status codes
|
|
9
|
+
* fix(dev-env): set the user/group for `nginx` container to `nginx:nginx`
|
|
10
|
+
|
|
11
|
+
**Full Changelog**: https://github.com/Automattic/vip-cli/compare/3.4.0...3.4.1
|
|
12
|
+
|
|
3
13
|
### 3.4.0
|
|
4
14
|
|
|
5
15
|
* Updating command option descriptions to follow the VIP-CLI style guide
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automattic/vip",
|
|
3
|
-
"version": "3.4.
|
|
3
|
+
"version": "3.4.2",
|
|
4
4
|
"lockfileVersion": 2,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@automattic/vip",
|
|
9
|
-
"version": "3.4.
|
|
9
|
+
"version": "3.4.2",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "MIT",
|
|
12
12
|
"dependencies": {
|
|
@@ -132,7 +132,7 @@
|
|
|
132
132
|
"@types/single-line-log": "1.1.2",
|
|
133
133
|
"@types/tar": "^6.1.13",
|
|
134
134
|
"@types/update-notifier": "^6.0.8",
|
|
135
|
-
"@types/uuid": "^
|
|
135
|
+
"@types/uuid": "^10.0.0",
|
|
136
136
|
"@types/xml2js": "^0.4.14",
|
|
137
137
|
"dockerode": "^4.0.0",
|
|
138
138
|
"eslint": "^8.35.0",
|
|
@@ -3884,9 +3884,9 @@
|
|
|
3884
3884
|
"dev": true
|
|
3885
3885
|
},
|
|
3886
3886
|
"node_modules/@types/node": {
|
|
3887
|
-
"version": "18.19.
|
|
3888
|
-
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.
|
|
3889
|
-
"integrity": "sha512-
|
|
3887
|
+
"version": "18.19.39",
|
|
3888
|
+
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.39.tgz",
|
|
3889
|
+
"integrity": "sha512-nPwTRDKUctxw3di5b4TfT3I0sWDiWoPQCZjXhvdkINntwr8lcoVCKsTgnXeRubKIlfnV+eN/HYk6Jb40tbcEAQ==",
|
|
3890
3890
|
"dependencies": {
|
|
3891
3891
|
"undici-types": "~5.26.4"
|
|
3892
3892
|
}
|
|
@@ -3992,9 +3992,9 @@
|
|
|
3992
3992
|
}
|
|
3993
3993
|
},
|
|
3994
3994
|
"node_modules/@types/uuid": {
|
|
3995
|
-
"version": "
|
|
3996
|
-
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-
|
|
3997
|
-
"integrity": "sha512-
|
|
3995
|
+
"version": "10.0.0",
|
|
3996
|
+
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz",
|
|
3997
|
+
"integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==",
|
|
3998
3998
|
"dev": true
|
|
3999
3999
|
},
|
|
4000
4000
|
"node_modules/@types/xml2js": {
|
|
@@ -12643,9 +12643,9 @@
|
|
|
12643
12643
|
}
|
|
12644
12644
|
},
|
|
12645
12645
|
"node_modules/typescript": {
|
|
12646
|
-
"version": "5.
|
|
12647
|
-
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.
|
|
12648
|
-
"integrity": "sha512-
|
|
12646
|
+
"version": "5.5.2",
|
|
12647
|
+
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.2.tgz",
|
|
12648
|
+
"integrity": "sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==",
|
|
12649
12649
|
"dev": true,
|
|
12650
12650
|
"bin": {
|
|
12651
12651
|
"tsc": "bin/tsc",
|
|
@@ -16306,9 +16306,9 @@
|
|
|
16306
16306
|
"dev": true
|
|
16307
16307
|
},
|
|
16308
16308
|
"@types/node": {
|
|
16309
|
-
"version": "18.19.
|
|
16310
|
-
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.
|
|
16311
|
-
"integrity": "sha512-
|
|
16309
|
+
"version": "18.19.39",
|
|
16310
|
+
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.39.tgz",
|
|
16311
|
+
"integrity": "sha512-nPwTRDKUctxw3di5b4TfT3I0sWDiWoPQCZjXhvdkINntwr8lcoVCKsTgnXeRubKIlfnV+eN/HYk6Jb40tbcEAQ==",
|
|
16312
16312
|
"requires": {
|
|
16313
16313
|
"undici-types": "~5.26.4"
|
|
16314
16314
|
}
|
|
@@ -16415,9 +16415,9 @@
|
|
|
16415
16415
|
}
|
|
16416
16416
|
},
|
|
16417
16417
|
"@types/uuid": {
|
|
16418
|
-
"version": "
|
|
16419
|
-
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-
|
|
16420
|
-
"integrity": "sha512-
|
|
16418
|
+
"version": "10.0.0",
|
|
16419
|
+
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz",
|
|
16420
|
+
"integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==",
|
|
16421
16421
|
"dev": true
|
|
16422
16422
|
},
|
|
16423
16423
|
"@types/xml2js": {
|
|
@@ -22653,9 +22653,9 @@
|
|
|
22653
22653
|
}
|
|
22654
22654
|
},
|
|
22655
22655
|
"typescript": {
|
|
22656
|
-
"version": "5.
|
|
22657
|
-
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.
|
|
22658
|
-
"integrity": "sha512-
|
|
22656
|
+
"version": "5.5.2",
|
|
22657
|
+
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.2.tgz",
|
|
22658
|
+
"integrity": "sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==",
|
|
22659
22659
|
"dev": true
|
|
22660
22660
|
},
|
|
22661
22661
|
"typical": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automattic/vip",
|
|
3
|
-
"version": "3.4.
|
|
3
|
+
"version": "3.4.2",
|
|
4
4
|
"description": "The VIP Javascript library & CLI",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -131,7 +131,7 @@
|
|
|
131
131
|
"@types/single-line-log": "1.1.2",
|
|
132
132
|
"@types/tar": "^6.1.13",
|
|
133
133
|
"@types/update-notifier": "^6.0.8",
|
|
134
|
-
"@types/uuid": "^
|
|
134
|
+
"@types/uuid": "^10.0.0",
|
|
135
135
|
"@types/xml2js": "^0.4.14",
|
|
136
136
|
"dockerode": "^4.0.0",
|
|
137
137
|
"eslint": "^8.35.0",
|