@automattic/vip 2.26.0 → 2.26.1
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 +11 -1
- package/automattic-vip-2.26.1.tgz +0 -0
- package/dist/bin/vip-dev-env-create.js +2 -1
- package/dist/bin/vip-dev-env-import-sql.js +8 -67
- package/dist/bin/vip-dev-env-info.js +2 -1
- package/dist/bin/vip-dev-env-update.js +3 -3
- package/dist/bin/vip-import-sql.js +3 -3
- package/dist/commands/dev-env-import-sql.js +94 -0
- package/dist/commands/dev-env-sync-sql.js +184 -0
- package/dist/{lib/sql-export.js → commands/export-sql.js} +4 -4
- package/dist/lib/client-file-uploader.js +18 -3
- package/dist/lib/constants/dev-environment.js +0 -1
- package/dist/lib/dev-environment/dev-environment-cli.js +21 -12
- package/dist/lib/dev-environment/dev-environment-core.js +13 -3
- package/dist/lib/dev-environment/dev-environment-lando.js +50 -9
- package/dist/lib/search-and-replace.js +3 -8
- package/dist/lib/utils.js +41 -0
- package/npm-shrinkwrap.json +172 -174
- package/package.json +2 -2
- package/automattic-vip-2.26.0.tgz +0 -0
|
@@ -59,18 +59,28 @@ services:
|
|
|
59
59
|
volume:
|
|
60
60
|
nocopy: true
|
|
61
61
|
<% wpVolumes() %>
|
|
62
|
+
run_as_root:
|
|
63
|
+
- chown www-data:www-data /wp/wp-content/mu-plugins /wp/config /wp/log /wp/wp-content/uploads /wp
|
|
62
64
|
run:
|
|
63
65
|
- sh /dev-tools/setup.sh database root "http://<%= siteSlug %>.vipdev.lndo.site/" "<%= wpTitle %>" <% if ( multisite ) { %> <%= siteSlug %>.vipdev.lndo.site <% } %>
|
|
64
66
|
|
|
65
67
|
database:
|
|
66
68
|
type: compose
|
|
67
69
|
services:
|
|
70
|
+
<% if ( mariadb ) { %>
|
|
68
71
|
image: mariadb:<%= mariadb %>
|
|
69
72
|
command: docker-entrypoint.sh mysqld --sql-mode=ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION --max_allowed_packet=67M
|
|
73
|
+
<% } else { %>
|
|
74
|
+
image: mysql:8
|
|
75
|
+
command: docker-entrypoint.sh mysqld --sql-mode=ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION --max_allowed_packet=67M --default-authentication-plugin=mysql_native_password
|
|
76
|
+
<% } %>
|
|
70
77
|
ports:
|
|
71
78
|
- ":3306"
|
|
72
79
|
environment:
|
|
73
|
-
|
|
80
|
+
MYSQL_ALLOW_EMPTY_PASSWORD: 'true'
|
|
81
|
+
MYSQL_USER: wordpress
|
|
82
|
+
MYSQL_PASSWORD: wordpress
|
|
83
|
+
MYSQL_DATABASE: wordpress
|
|
74
84
|
volumes:
|
|
75
85
|
- database_data:/var/lib/mysql
|
|
76
86
|
volumes:
|
|
Binary file
|
|
@@ -87,7 +87,8 @@ cmd.argv(process.argv, async (arg, opt) => {
|
|
|
87
87
|
try {
|
|
88
88
|
await (0, _devEnvironmentCore.createEnvironment)(instanceData);
|
|
89
89
|
await (0, _devEnvironmentCore.printEnvironmentInfo)(lando, slug, {
|
|
90
|
-
extended: false
|
|
90
|
+
extended: false,
|
|
91
|
+
suppressWarnings: true
|
|
91
92
|
});
|
|
92
93
|
const message = '\n' + _chalk.default.green('✓') + ` environment created.\n\nTo start it please run:\n\n${startCommand}\n`;
|
|
93
94
|
console.log(message);
|
|
@@ -8,22 +8,18 @@
|
|
|
8
8
|
/**
|
|
9
9
|
* External dependencies
|
|
10
10
|
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Internal dependencies
|
|
14
|
+
*/
|
|
11
15
|
"use strict";
|
|
12
16
|
|
|
13
|
-
var _fs = _interopRequireDefault(require("fs"));
|
|
14
|
-
var _chalk = _interopRequireDefault(require("chalk"));
|
|
15
17
|
var _tracker = require("../lib/tracker");
|
|
16
18
|
var _command = _interopRequireDefault(require("../lib/cli/command"));
|
|
17
19
|
var _devEnvironmentCli = require("../lib/dev-environment/dev-environment-cli");
|
|
18
|
-
var _devEnvironmentCore = require("../lib/dev-environment/dev-environment-core");
|
|
19
20
|
var _devEnvironment = require("../lib/constants/dev-environment");
|
|
20
|
-
var
|
|
21
|
-
var _devEnvironmentLando = require("../lib/dev-environment/dev-environment-lando");
|
|
22
|
-
var _userError = _interopRequireDefault(require("../lib/user-error"));
|
|
21
|
+
var _devEnvImportSql = require("../commands/dev-env-import-sql");
|
|
23
22
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
24
|
-
/**
|
|
25
|
-
* Internal dependencies
|
|
26
|
-
*/
|
|
27
23
|
const examples = [{
|
|
28
24
|
usage: `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} import sql some-wp-db-file.sql`,
|
|
29
25
|
description: 'Import the contents of a WordPress database from an SQL file'
|
|
@@ -41,65 +37,10 @@ const examples = [{
|
|
|
41
37
|
requiredArgs: 1
|
|
42
38
|
}).option('slug', 'Custom name of the dev environment').option(['r', 'search-replace'], 'Perform Search and Replace on the specified SQL file').option('in-place', 'Search and Replace explicitly on the given input file').option('skip-validate', 'Do not perform file validation.').examples(examples).argv(process.argv, async (unmatchedArgs, opt) => {
|
|
43
39
|
const [fileName] = unmatchedArgs;
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
inPlace
|
|
47
|
-
} = opt;
|
|
48
|
-
const slug = (0, _devEnvironmentCli.getEnvironmentName)(opt);
|
|
49
|
-
const lando = await (0, _devEnvironmentLando.bootstrapLando)();
|
|
50
|
-
await (0, _devEnvironmentCli.validateDependencies)(lando, slug);
|
|
51
|
-
const trackingInfo = (0, _devEnvironmentCli.getEnvTrackingInfo)(slug);
|
|
52
|
-
await (0, _tracker.trackEvent)('dev_env_import_sql_command_execute', trackingInfo);
|
|
40
|
+
const cmd = new _devEnvImportSql.DevEnvImportSQLCommand(fileName, opt);
|
|
41
|
+
const trackingInfo = (0, _devEnvironmentCli.getEnvTrackingInfo)(cmd.slug);
|
|
53
42
|
try {
|
|
54
|
-
|
|
55
|
-
if (!opt.skipValidate) {
|
|
56
|
-
if (!(0, _devEnvironmentLando.isEnvUp)(lando, (0, _devEnvironmentCore.getEnvironmentPath)(slug))) {
|
|
57
|
-
throw new _userError.default('Environment needs to be started first');
|
|
58
|
-
}
|
|
59
|
-
const expectedDomain = `${slug}.vipdev.lndo.site`;
|
|
60
|
-
await (0, _sql.validate)(resolvedPath, {
|
|
61
|
-
isImport: false,
|
|
62
|
-
skipChecks: [],
|
|
63
|
-
extraCheckParams: {
|
|
64
|
-
siteHomeUrlLando: expectedDomain
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
const fd = await _fs.default.promises.open(resolvedPath, 'r');
|
|
69
|
-
const importArg = ['db', '--disable-auto-rehash'];
|
|
70
|
-
const origIsTTY = process.stdin.isTTY;
|
|
71
|
-
try {
|
|
72
|
-
/**
|
|
73
|
-
* When stdin is a TTY, Lando passes the `--tty` flag to Docker.
|
|
74
|
-
* This breaks our code when we pass the stream as stdin to Docker.
|
|
75
|
-
* exec() then fails with "the input device is not a TTY".
|
|
76
|
-
*
|
|
77
|
-
* Therefore, for the things to work, we have to pretend that stdin is not a TTY :-)
|
|
78
|
-
*/
|
|
79
|
-
process.stdin.isTTY = false;
|
|
80
|
-
await (0, _devEnvironmentCore.exec)(lando, slug, importArg, {
|
|
81
|
-
stdio: [fd, 'pipe', 'pipe']
|
|
82
|
-
});
|
|
83
|
-
console.log(`${_chalk.default.green.bold('Success:')} Database imported.`);
|
|
84
|
-
} finally {
|
|
85
|
-
process.stdin.isTTY = origIsTTY;
|
|
86
|
-
}
|
|
87
|
-
if (searchReplace && searchReplace.length && !inPlace) {
|
|
88
|
-
_fs.default.unlinkSync(resolvedPath);
|
|
89
|
-
}
|
|
90
|
-
const cacheArg = ['wp', 'cache', 'flush'];
|
|
91
|
-
await (0, _devEnvironmentCore.exec)(lando, slug, cacheArg);
|
|
92
|
-
try {
|
|
93
|
-
await (0, _devEnvironmentCore.exec)(lando, slug, ['wp', 'cli', 'has-command', 'vip-search']);
|
|
94
|
-
const doIndex = await (0, _devEnvironmentCli.promptForBoolean)('Do you want to index data in Elasticsearch (used by Enterprise Search)?', true);
|
|
95
|
-
if (doIndex) {
|
|
96
|
-
await (0, _devEnvironmentCore.exec)(lando, slug, ['wp', 'vip-search', 'index', '--setup', '--network-wide', '--skip-confirm']);
|
|
97
|
-
}
|
|
98
|
-
} catch (err) {
|
|
99
|
-
// Exception means they don't have vip-search enabled.
|
|
100
|
-
}
|
|
101
|
-
const addUserArg = ['wp', 'dev-env-add-admin', '--username=vipgo', '--password=password'];
|
|
102
|
-
await (0, _devEnvironmentCore.exec)(lando, slug, addUserArg);
|
|
43
|
+
await cmd.run();
|
|
103
44
|
await (0, _tracker.trackEvent)('dev_env_import_sql_command_success', trackingInfo);
|
|
104
45
|
} catch (error) {
|
|
105
46
|
await (0, _devEnvironmentCli.handleCLIException)(error, 'dev_env_import_sql_command_error', trackingInfo);
|
|
@@ -40,7 +40,8 @@ const examples = [{
|
|
|
40
40
|
debug('Args: ', arg, 'Options: ', opt);
|
|
41
41
|
try {
|
|
42
42
|
const options = {
|
|
43
|
-
extended: !!opt.extended
|
|
43
|
+
extended: !!opt.extended,
|
|
44
|
+
suppressWarnings: true
|
|
44
45
|
};
|
|
45
46
|
if (opt.all) {
|
|
46
47
|
await (0, _devEnvironmentCore.printAllEnvironmentsInfo)(lando, options);
|
|
@@ -52,7 +52,7 @@ cmd.argv(process.argv, async (arg, opt) => {
|
|
|
52
52
|
const defaultOptions = {
|
|
53
53
|
appCode: currentInstanceData.appCode.dir || currentInstanceData.appCode.tag || 'latest',
|
|
54
54
|
muPlugins: currentInstanceData.muPlugins.dir || currentInstanceData.muPlugins.tag || 'latest',
|
|
55
|
-
wordpress: currentInstanceData.wordpress.tag,
|
|
55
|
+
wordpress: currentInstanceData.wordpress.tag || 'trunk',
|
|
56
56
|
elasticsearch: currentInstanceData.elasticsearch,
|
|
57
57
|
php: currentInstanceData.php || _devEnvironment.DEV_ENVIRONMENT_PHP_VERSIONS.default,
|
|
58
58
|
mariadb: currentInstanceData.mariadb,
|
|
@@ -66,8 +66,8 @@ cmd.argv(process.argv, async (arg, opt) => {
|
|
|
66
66
|
const providedOptions = Object.keys(opt).filter(option => option.length > 1) // Filter out single letter aliases
|
|
67
67
|
.filter(option => !['debug', 'help', 'slug'].includes(option)); // Filter out options that are not related to instance configuration
|
|
68
68
|
|
|
69
|
-
const
|
|
70
|
-
const instanceData = await (0, _devEnvironmentCli.promptForArguments)(preselectedOptions, defaultOptions,
|
|
69
|
+
const suppressPrompts = providedOptions.length > 0;
|
|
70
|
+
const instanceData = await (0, _devEnvironmentCli.promptForArguments)(preselectedOptions, defaultOptions, suppressPrompts);
|
|
71
71
|
instanceData.siteSlug = slug;
|
|
72
72
|
await (0, _devEnvironmentCore.updateEnvironment)(instanceData);
|
|
73
73
|
const message = '\n' + _chalk.default.green('✓') + ' environment updated. Restart environment for changes to take an affect.';
|
|
@@ -353,10 +353,10 @@ const displayPlaybook = ({
|
|
|
353
353
|
const domain = env !== null && env !== void 0 && (_env$primaryDomain = env.primaryDomain) !== null && _env$primaryDomain !== void 0 && _env$primaryDomain.name ? env.primaryDomain.name : `#${env.id}`;
|
|
354
354
|
const formattedEnvironment = (0, _format.formatEnvironment)(opts.env.type);
|
|
355
355
|
const launched = opts.env.launched;
|
|
356
|
-
let fileNameToUpload = fileName;
|
|
357
356
|
|
|
358
|
-
//
|
|
359
|
-
validateFilename(
|
|
357
|
+
// Extract base file name and exit if it contains unsafe character
|
|
358
|
+
validateFilename(fileMeta.basename);
|
|
359
|
+
let fileNameToUpload = fileName;
|
|
360
360
|
|
|
361
361
|
// SQL file validations
|
|
362
362
|
const tableNames = await validateAndGetTableNames({
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.DevEnvImportSQLCommand = void 0;
|
|
7
|
+
var _fs = _interopRequireDefault(require("fs"));
|
|
8
|
+
var _chalk = _interopRequireDefault(require("chalk"));
|
|
9
|
+
var _devEnvironmentCli = require("../lib/dev-environment/dev-environment-cli");
|
|
10
|
+
var _devEnvironmentCore = require("../lib/dev-environment/dev-environment-core");
|
|
11
|
+
var _devEnvironmentLando = require("../lib/dev-environment/dev-environment-lando");
|
|
12
|
+
var _userError = _interopRequireDefault(require("../lib/user-error"));
|
|
13
|
+
var _sql = require("../lib/validations/sql");
|
|
14
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
15
|
+
/**
|
|
16
|
+
*
|
|
17
|
+
* @format
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* External dependencies
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Internal dependencies
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
class DevEnvImportSQLCommand {
|
|
29
|
+
constructor(fileName, options) {
|
|
30
|
+
this.fileName = fileName;
|
|
31
|
+
this.options = options;
|
|
32
|
+
this.slug = (0, _devEnvironmentCli.getEnvironmentName)(options);
|
|
33
|
+
}
|
|
34
|
+
async run(silent = false) {
|
|
35
|
+
const lando = await (0, _devEnvironmentLando.bootstrapLando)();
|
|
36
|
+
await (0, _devEnvironmentCli.validateDependencies)(lando, this.slug, silent);
|
|
37
|
+
const {
|
|
38
|
+
searchReplace,
|
|
39
|
+
inPlace
|
|
40
|
+
} = this.options;
|
|
41
|
+
const resolvedPath = await (0, _devEnvironmentCore.resolveImportPath)(this.slug, this.fileName, searchReplace, inPlace);
|
|
42
|
+
if (!this.options.skipValidate) {
|
|
43
|
+
if (!(await (0, _devEnvironmentLando.isEnvUp)(lando, (0, _devEnvironmentCore.getEnvironmentPath)(this.slug)))) {
|
|
44
|
+
throw new _userError.default('Environment needs to be started first');
|
|
45
|
+
}
|
|
46
|
+
const expectedDomain = `${this.slug}.vipdev.lndo.site`;
|
|
47
|
+
await (0, _sql.validate)(resolvedPath, {
|
|
48
|
+
isImport: false,
|
|
49
|
+
skipChecks: [],
|
|
50
|
+
extraCheckParams: {
|
|
51
|
+
siteHomeUrlLando: expectedDomain
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
const fd = await _fs.default.promises.open(resolvedPath, 'r');
|
|
56
|
+
const importArg = ['db', '--disable-auto-rehash'];
|
|
57
|
+
const origIsTTY = process.stdin.isTTY;
|
|
58
|
+
try {
|
|
59
|
+
/**
|
|
60
|
+
* When stdin is a TTY, Lando passes the `--tty` flag to Docker.
|
|
61
|
+
* This breaks our code when we pass the stream as stdin to Docker.
|
|
62
|
+
* exec() then fails with "the input device is not a TTY".
|
|
63
|
+
*
|
|
64
|
+
* Therefore, for the things to work, we have to pretend that stdin is not a TTY :-)
|
|
65
|
+
*/
|
|
66
|
+
process.stdin.isTTY = false;
|
|
67
|
+
await (0, _devEnvironmentCore.exec)(lando, this.slug, importArg, {
|
|
68
|
+
stdio: [fd, 'pipe', 'pipe']
|
|
69
|
+
});
|
|
70
|
+
if (!silent) {
|
|
71
|
+
console.log(`${_chalk.default.green.bold('Success:')} Database imported.`);
|
|
72
|
+
}
|
|
73
|
+
} finally {
|
|
74
|
+
process.stdin.isTTY = origIsTTY;
|
|
75
|
+
}
|
|
76
|
+
if (searchReplace && searchReplace.length && !inPlace) {
|
|
77
|
+
_fs.default.unlinkSync(resolvedPath);
|
|
78
|
+
}
|
|
79
|
+
const cacheArg = ['wp', 'cache', 'flush'];
|
|
80
|
+
await (0, _devEnvironmentCore.exec)(lando, this.slug, cacheArg);
|
|
81
|
+
try {
|
|
82
|
+
await (0, _devEnvironmentCore.exec)(lando, this.slug, ['wp', 'cli', 'has-command', 'vip-search']);
|
|
83
|
+
const doIndex = await (0, _devEnvironmentCli.promptForBoolean)('Do you want to index data in Elasticsearch (used by Enterprise Search)?', true);
|
|
84
|
+
if (doIndex) {
|
|
85
|
+
await (0, _devEnvironmentCore.exec)(lando, this.slug, ['wp', 'vip-search', 'index', '--setup', '--network-wide', '--skip-confirm']);
|
|
86
|
+
}
|
|
87
|
+
} catch (err) {
|
|
88
|
+
// Exception means they don't have vip-search enabled.
|
|
89
|
+
}
|
|
90
|
+
const addUserArg = ['wp', 'dev-env-add-admin', '--username=vipgo', '--password=password'];
|
|
91
|
+
await (0, _devEnvironmentCore.exec)(lando, this.slug, addUserArg);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
exports.DevEnvImportSQLCommand = DevEnvImportSQLCommand;
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
* @format
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* External dependencies
|
|
10
|
+
*/
|
|
11
|
+
"use strict";
|
|
12
|
+
|
|
13
|
+
Object.defineProperty(exports, "__esModule", {
|
|
14
|
+
value: true
|
|
15
|
+
});
|
|
16
|
+
exports.DevEnvSyncSQLCommand = void 0;
|
|
17
|
+
var _fs = _interopRequireDefault(require("fs"));
|
|
18
|
+
var _chalk = _interopRequireDefault(require("chalk"));
|
|
19
|
+
var _vipSearchReplace = require("@automattic/vip-search-replace");
|
|
20
|
+
var _clientFileUploader = require("../lib/client-file-uploader");
|
|
21
|
+
var _exportSql = require("./export-sql");
|
|
22
|
+
var _utils = require("../lib/utils");
|
|
23
|
+
var _lineByLine = require("../lib/validations/line-by-line");
|
|
24
|
+
var exit = _interopRequireWildcard(require("../lib/cli/exit"));
|
|
25
|
+
var _devEnvImportSql = require("./dev-env-import-sql");
|
|
26
|
+
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
27
|
+
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
28
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
29
|
+
/**
|
|
30
|
+
* Internal dependencies
|
|
31
|
+
*/
|
|
32
|
+
/**
|
|
33
|
+
* Finds the site home url from the SQL line
|
|
34
|
+
*
|
|
35
|
+
* @param {string} sql A line in a SQL file
|
|
36
|
+
* @return {string} Site home url. null if not found
|
|
37
|
+
*/
|
|
38
|
+
function findSiteHomeUrl(sql) {
|
|
39
|
+
const regex = "'(siteurl|home)',\\s?'(.*?)'";
|
|
40
|
+
const results = sql.match(regex);
|
|
41
|
+
if (results) {
|
|
42
|
+
const domain = results[2].replace(/https?:\/\//, '');
|
|
43
|
+
return domain;
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Extracts a list of site urls from the SQL file
|
|
50
|
+
*
|
|
51
|
+
* @param {string} sqlFile Path to the SQL file
|
|
52
|
+
* @return {Promise<string[]>} List of site urls
|
|
53
|
+
* @throws {Error} If there is an error reading the file
|
|
54
|
+
*/
|
|
55
|
+
async function extractSiteUrls(sqlFile) {
|
|
56
|
+
const readInterface = await (0, _lineByLine.getReadInterface)(sqlFile);
|
|
57
|
+
return new Promise((resolve, reject) => {
|
|
58
|
+
const domains = [];
|
|
59
|
+
readInterface.on('line', line => {
|
|
60
|
+
const domain = findSiteHomeUrl(line);
|
|
61
|
+
if (domain) {
|
|
62
|
+
domains.push(domain);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
readInterface.on('close', () => {
|
|
66
|
+
resolve(domains);
|
|
67
|
+
});
|
|
68
|
+
readInterface.on('error', reject);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
class DevEnvSyncSQLCommand {
|
|
72
|
+
/**
|
|
73
|
+
* Creates a new instance of the command
|
|
74
|
+
*
|
|
75
|
+
* @param {string} app The app object
|
|
76
|
+
* @param {string} env The environment object
|
|
77
|
+
* @param {string} slug The site slug
|
|
78
|
+
*/
|
|
79
|
+
constructor(app, env, slug) {
|
|
80
|
+
this.app = app;
|
|
81
|
+
this.env = env;
|
|
82
|
+
this.slug = slug;
|
|
83
|
+
this.tmpDir = (0, _utils.makeTempDir)();
|
|
84
|
+
}
|
|
85
|
+
get landoDomain() {
|
|
86
|
+
return `${this.slug}.vipdev.lndo.site`;
|
|
87
|
+
}
|
|
88
|
+
get sqlFile() {
|
|
89
|
+
return `${this.tmpDir}/sql-export.sql`;
|
|
90
|
+
}
|
|
91
|
+
get gzFile() {
|
|
92
|
+
return `${this.tmpDir}/sql-export.sql.gz`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Runs the SQL export command to generate the SQL export from
|
|
97
|
+
* the latest backup
|
|
98
|
+
*
|
|
99
|
+
* @return {Promise<void>} Promise that resolves when the export is complete
|
|
100
|
+
*/
|
|
101
|
+
async generateExport() {
|
|
102
|
+
const exportCommand = new _exportSql.ExportSQLCommand(this.app, this.env, this.gzFile);
|
|
103
|
+
await exportCommand.run();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Runs the search-replace operation on the SQL file
|
|
108
|
+
* to replace the site urls with the lando domain
|
|
109
|
+
*
|
|
110
|
+
* @return {Promise<void>} Promise that resolves when the search-replace is complete
|
|
111
|
+
* @throws {Error} If there is an error reading the file
|
|
112
|
+
*/
|
|
113
|
+
async runSearchReplace() {
|
|
114
|
+
const replacements = this.siteUrls.reduce((acc, url) => [...acc, url, this.landoDomain], []);
|
|
115
|
+
const readStream = _fs.default.createReadStream(this.sqlFile);
|
|
116
|
+
const replacedStream = await (0, _vipSearchReplace.replace)(readStream, replacements);
|
|
117
|
+
replacedStream.pipe(_fs.default.createWriteStream(this.sqlFile));
|
|
118
|
+
return new Promise((resolve, reject) => {
|
|
119
|
+
replacedStream.on('finish', resolve);
|
|
120
|
+
replacedStream.on('error', reject);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Runs the SQL import command to import the SQL file
|
|
126
|
+
*
|
|
127
|
+
* @return {Promise<void>} Promise that resolves when the import is complete
|
|
128
|
+
* @throws {Error} If there is an error importing the file
|
|
129
|
+
*/
|
|
130
|
+
async runImport() {
|
|
131
|
+
const importOptions = {
|
|
132
|
+
slug: this.slug,
|
|
133
|
+
inPlace: true,
|
|
134
|
+
skipValidate: true
|
|
135
|
+
};
|
|
136
|
+
const importCommand = new _devEnvImportSql.DevEnvImportSQLCommand(this.sqlFile, importOptions);
|
|
137
|
+
await importCommand.run(true);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Sequentially runs the commands to export, search-replace, and import the SQL file
|
|
142
|
+
* to the local environment
|
|
143
|
+
*
|
|
144
|
+
* @return {Promise<void>} Promise that resolves when the commands are complete
|
|
145
|
+
*/
|
|
146
|
+
async run() {
|
|
147
|
+
try {
|
|
148
|
+
await this.generateExport();
|
|
149
|
+
} catch (err) {
|
|
150
|
+
exit.withError(`Error exporting SQL backup: ${err === null || err === void 0 ? void 0 : err.message}`);
|
|
151
|
+
}
|
|
152
|
+
try {
|
|
153
|
+
console.log(`Extracting the exported file ${this.gzFile}...`);
|
|
154
|
+
await (0, _clientFileUploader.unzipFile)(this.gzFile, this.sqlFile);
|
|
155
|
+
console.log(`${_chalk.default.green('✓')} Extracted to ${this.sqlFile}`);
|
|
156
|
+
} catch (err) {
|
|
157
|
+
exit.withError(`Error extracting the SQL export: ${err === null || err === void 0 ? void 0 : err.message}`);
|
|
158
|
+
}
|
|
159
|
+
try {
|
|
160
|
+
console.log('Extracting site urls from the SQL file...');
|
|
161
|
+
this.siteUrls = await extractSiteUrls(this.sqlFile);
|
|
162
|
+
} catch (err) {
|
|
163
|
+
exit.withError(`Error extracting site URLs: ${err === null || err === void 0 ? void 0 : err.message}`);
|
|
164
|
+
}
|
|
165
|
+
try {
|
|
166
|
+
console.log('Running the following search-replace operations on the SQL file:');
|
|
167
|
+
this.siteUrls.forEach(domain => {
|
|
168
|
+
console.log(` ${domain} -> ${this.landoDomain}`);
|
|
169
|
+
});
|
|
170
|
+
await this.runSearchReplace();
|
|
171
|
+
console.log(`${_chalk.default.green('✓')} Search-replace operation is complete`);
|
|
172
|
+
} catch (err) {
|
|
173
|
+
exit.withError(`Error replacing domains: ${err === null || err === void 0 ? void 0 : err.message}`);
|
|
174
|
+
}
|
|
175
|
+
try {
|
|
176
|
+
console.log('Importing the SQL file...');
|
|
177
|
+
await this.runImport();
|
|
178
|
+
console.log(`${_chalk.default.green('✓')} SQL file imported`);
|
|
179
|
+
} catch (err) {
|
|
180
|
+
exit.withError(`Error importing SQL file: ${err === null || err === void 0 ? void 0 : err.message}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
exports.DevEnvSyncSQLCommand = DevEnvSyncSQLCommand;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.
|
|
6
|
+
exports.GENERATE_DOWNLOAD_LINK_MUTATION = exports.ExportSQLCommand = exports.CREATE_EXPORT_JOB_MUTATION = void 0;
|
|
7
7
|
var _graphqlTag = _interopRequireDefault(require("graphql-tag"));
|
|
8
8
|
var _fs = _interopRequireDefault(require("fs"));
|
|
9
9
|
var _https = _interopRequireDefault(require("https"));
|
|
@@ -188,7 +188,7 @@ async function createExportJob(appId, envId, backupId) {
|
|
|
188
188
|
/**
|
|
189
189
|
* Class representing an export command workflow
|
|
190
190
|
*/
|
|
191
|
-
class
|
|
191
|
+
class ExportSQLCommand {
|
|
192
192
|
steps = {
|
|
193
193
|
PREPARE: 'prepare',
|
|
194
194
|
CREATE: 'create',
|
|
@@ -313,7 +313,7 @@ class SQLExportCommand {
|
|
|
313
313
|
*
|
|
314
314
|
* @return {Promise} A promise which resolves to void
|
|
315
315
|
*/
|
|
316
|
-
async
|
|
316
|
+
async run() {
|
|
317
317
|
console.log(`Fetching the latest backup for ${this.app.name}`);
|
|
318
318
|
const {
|
|
319
319
|
latestBackup
|
|
@@ -357,4 +357,4 @@ class SQLExportCommand {
|
|
|
357
357
|
}
|
|
358
358
|
}
|
|
359
359
|
}
|
|
360
|
-
exports.
|
|
360
|
+
exports.ExportSQLCommand = ExportSQLCommand;
|
|
@@ -15,6 +15,7 @@ exports.getPartBoundaries = getPartBoundaries;
|
|
|
15
15
|
exports.getSignedUploadRequestData = getSignedUploadRequestData;
|
|
16
16
|
exports.gzipFile = exports.getWorkingTempDir = void 0;
|
|
17
17
|
exports.isFile = isFile;
|
|
18
|
+
exports.unzipFile = void 0;
|
|
18
19
|
exports.uploadImportSqlFileToS3 = uploadImportSqlFileToS3;
|
|
19
20
|
exports.uploadPart = uploadPart;
|
|
20
21
|
exports.uploadParts = uploadParts;
|
|
@@ -27,7 +28,7 @@ var _nodeFetch = _interopRequireDefault(require("node-fetch"));
|
|
|
27
28
|
var _chalk = _interopRequireDefault(require("chalk"));
|
|
28
29
|
var _zlib = require("zlib");
|
|
29
30
|
var _crypto = require("crypto");
|
|
30
|
-
var
|
|
31
|
+
var _promises = require("node:stream/promises");
|
|
31
32
|
var _xml2js = require("xml2js");
|
|
32
33
|
var _debug = _interopRequireDefault(require("debug"));
|
|
33
34
|
var _http = _interopRequireDefault(require("../lib/api/http"));
|
|
@@ -77,7 +78,21 @@ const getFileMD5Hash = async fileName => new Promise((resolve, reject) => _fs.de
|
|
|
77
78
|
}).on('error', error => reject(`could not generate file hash: ${error}`)));
|
|
78
79
|
exports.getFileMD5Hash = getFileMD5Hash;
|
|
79
80
|
const gzipFile = async (uncompressedFileName, compressedFileName) => new Promise((resolve, reject) => _fs.default.createReadStream(uncompressedFileName).pipe((0, _zlib.createGzip)()).pipe(_fs.default.createWriteStream(compressedFileName)).on('finish', resolve).on('error', error => reject(`could not compress file: ${error}`)));
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Extract a .gz file and save it to a specified location
|
|
84
|
+
*
|
|
85
|
+
* @param {string} inputFilename The file to unzip
|
|
86
|
+
* @param {string} outputFilename The file where the unzipped data will be written
|
|
87
|
+
* @return {Promise} A promise that resolves when the file is unzipped
|
|
88
|
+
*/
|
|
80
89
|
exports.gzipFile = gzipFile;
|
|
90
|
+
const unzipFile = async (inputFilename, outputFilename) => {
|
|
91
|
+
const source = _fs.default.createReadStream(inputFilename);
|
|
92
|
+
const destination = _fs.default.createWriteStream(outputFilename);
|
|
93
|
+
await (0, _promises.pipeline)(source, (0, _zlib.createGunzip)(), destination);
|
|
94
|
+
};
|
|
95
|
+
exports.unzipFile = unzipFile;
|
|
81
96
|
async function getFileMeta(fileName) {
|
|
82
97
|
const fileSize = await getFileSize(fileName);
|
|
83
98
|
const basename = _path.default.basename(fileName);
|
|
@@ -171,7 +186,7 @@ async function uploadUsingPutObject({
|
|
|
171
186
|
};
|
|
172
187
|
|
|
173
188
|
let readBytes = 0;
|
|
174
|
-
const progressPassThrough = new
|
|
189
|
+
const progressPassThrough = new _promises.PassThrough();
|
|
175
190
|
progressPassThrough.on('data', data => {
|
|
176
191
|
readBytes += data.length;
|
|
177
192
|
const percentage = Math.floor(100 * readBytes / fileSize) + '%';
|
|
@@ -394,7 +409,7 @@ async function uploadParts({
|
|
|
394
409
|
index,
|
|
395
410
|
partSize
|
|
396
411
|
} = part;
|
|
397
|
-
const progressPassThrough = new
|
|
412
|
+
const progressPassThrough = new _promises.PassThrough();
|
|
398
413
|
let partBytesRead = 0;
|
|
399
414
|
progressPassThrough.on('data', data => {
|
|
400
415
|
totalBytesRead += data.length;
|
|
@@ -11,7 +11,6 @@ exports.DEV_ENVIRONMENT_FULL_COMMAND = DEV_ENVIRONMENT_FULL_COMMAND;
|
|
|
11
11
|
const DEV_ENVIRONMENT_DEFAULTS = {
|
|
12
12
|
title: 'VIP Dev',
|
|
13
13
|
multisite: false,
|
|
14
|
-
mariadbVersion: '10.3',
|
|
15
14
|
phpVersion: '8.0'
|
|
16
15
|
};
|
|
17
16
|
exports.DEV_ENVIRONMENT_DEFAULTS = DEV_ENVIRONMENT_DEFAULTS;
|
|
@@ -14,6 +14,7 @@ exports.handleCLIException = handleCLIException;
|
|
|
14
14
|
exports.printTable = printTable;
|
|
15
15
|
exports.processBooleanOption = processBooleanOption;
|
|
16
16
|
exports.processComponentOptionInput = processComponentOptionInput;
|
|
17
|
+
exports.processVersionOption = processVersionOption;
|
|
17
18
|
exports.promptForArguments = promptForArguments;
|
|
18
19
|
exports.promptForBoolean = promptForBoolean;
|
|
19
20
|
exports.promptForComponent = promptForComponent;
|
|
@@ -274,7 +275,7 @@ async function promptForArguments(preselectedOptions, defaultOptions, suppressPr
|
|
|
274
275
|
multisite: 'multisite' in preselectedOptions ? preselectedOptions.multisite : await promptForBoolean(multisiteText, !!multisiteDefault),
|
|
275
276
|
elasticsearch: false,
|
|
276
277
|
php: preselectedOptions.php ? resolvePhpVersion(preselectedOptions.php) : await promptForPhpVersion(resolvePhpVersion(defaultOptions.php || _devEnvironment.DEV_ENVIRONMENT_DEFAULTS.phpVersion)),
|
|
277
|
-
mariadb: preselectedOptions.mariadb || defaultOptions.mariadb
|
|
278
|
+
mariadb: preselectedOptions.mariadb || defaultOptions.mariadb,
|
|
278
279
|
mediaRedirectDomain: preselectedOptions.mediaRedirectDomain || '',
|
|
279
280
|
wordpress: {
|
|
280
281
|
mode: 'image',
|
|
@@ -343,7 +344,9 @@ async function processComponent(component, preselectedValue, defaultValue) {
|
|
|
343
344
|
const defaultObject = defaultValue ? processComponentOptionInput(defaultValue, allowLocal) : null;
|
|
344
345
|
if (preselectedValue) {
|
|
345
346
|
result = processComponentOptionInput(preselectedValue, allowLocal);
|
|
346
|
-
|
|
347
|
+
if (allowLocal) {
|
|
348
|
+
console.log(`${_chalk.default.green('✓')} Path to your local ${componentDisplayNames[component]}: ${preselectedValue}`);
|
|
349
|
+
}
|
|
347
350
|
} else {
|
|
348
351
|
result = await promptForComponent(component, allowLocal, defaultObject);
|
|
349
352
|
}
|
|
@@ -449,9 +452,7 @@ function resolvePhpVersion(version) {
|
|
|
449
452
|
}
|
|
450
453
|
const versions = Object.keys(_devEnvironment.DEV_ENVIRONMENT_PHP_VERSIONS);
|
|
451
454
|
const images = Object.values(_devEnvironment.DEV_ENVIRONMENT_PHP_VERSIONS);
|
|
452
|
-
|
|
453
|
-
// eslint-disable-next-line eqeqeq -- use loose comparison because commander resolves '8.0' to '8'
|
|
454
|
-
const index = versions.findIndex(value => value == version);
|
|
455
|
+
const index = versions.findIndex(value => value === version);
|
|
455
456
|
if (index === -1) {
|
|
456
457
|
const image = images.find(value => value === version);
|
|
457
458
|
return image !== null && image !== void 0 ? image : images[0];
|
|
@@ -551,8 +552,16 @@ function processBooleanOption(value) {
|
|
|
551
552
|
}
|
|
552
553
|
return !FALSE_OPTIONS.includes((_value$toLowerCase = value.toLowerCase) === null || _value$toLowerCase === void 0 ? void 0 : _value$toLowerCase.call(value));
|
|
553
554
|
}
|
|
555
|
+
function processVersionOption(value) {
|
|
556
|
+
if (!isNaN(value) && value % 1 === 0) {
|
|
557
|
+
// If it's an Integer passed in, let's ensure that it has a decimal in it to match the version tags e.g. 6 => 6.0
|
|
558
|
+
return parseFloat(value).toFixed(1);
|
|
559
|
+
}
|
|
560
|
+
return value;
|
|
561
|
+
}
|
|
554
562
|
function addDevEnvConfigurationOptions(command) {
|
|
555
|
-
|
|
563
|
+
// We leave the third parameter to undefined on some because the defaults are handled in preProcessInstanceData()
|
|
564
|
+
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', 'mailhog'], 'Enable MailHog. By default it is disabled', undefined, processBooleanOption);
|
|
556
565
|
}
|
|
557
566
|
|
|
558
567
|
/**
|
|
@@ -562,20 +571,20 @@ async function getTagChoices() {
|
|
|
562
571
|
let versions = await (0, _devEnvironmentCore.getVersionList)();
|
|
563
572
|
if (versions.length < 1) {
|
|
564
573
|
versions = [{
|
|
565
|
-
ref: '
|
|
566
|
-
tag: '
|
|
574
|
+
ref: '6.1.1',
|
|
575
|
+
tag: '6.1',
|
|
567
576
|
cacheable: true,
|
|
568
577
|
locked: true,
|
|
569
578
|
prerelease: false
|
|
570
579
|
}, {
|
|
571
|
-
ref: '
|
|
572
|
-
tag: '
|
|
580
|
+
ref: '6.0.3',
|
|
581
|
+
tag: '6.0',
|
|
573
582
|
cacheable: true,
|
|
574
583
|
locked: true,
|
|
575
584
|
prerelease: false
|
|
576
585
|
}, {
|
|
577
|
-
ref: '5.
|
|
578
|
-
tag: '5.
|
|
586
|
+
ref: '5.9.5',
|
|
587
|
+
tag: '5.9',
|
|
579
588
|
cacheable: true,
|
|
580
589
|
locked: true,
|
|
581
590
|
prerelease: false
|