@automattic/vip 2.31.0-dev1 → 2.31.1-dev
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/CHANGELOG.md +25 -0
- package/assets/dev-env.lando.template.yml.ejs +14 -0
- package/assets/dev-env.nginx.template.conf.ejs +23 -2
- package/automattic-vip-2.31.1-dev.tgz +0 -0
- package/dist/bin/vip-dev-env-shell.js +2 -1
- package/dist/bin/vip-dev-env-sync-sql.js +4 -1
- package/dist/bin/vip-dev-env-sync.js +0 -0
- package/dist/bin/vip-dev-env-update.js +1 -0
- package/dist/bin/vip-export-sql.js +7 -3
- package/dist/commands/backup-db.js +213 -0
- package/dist/commands/dev-env-sync-sql.js +29 -8
- package/dist/commands/export-sql.js +37 -15
- package/dist/lib/dev-environment/dev-environment-cli.js +6 -4
- package/dist/lib/dev-environment/dev-environment-configuration-file.js +4 -2
- package/dist/lib/dev-environment/dev-environment-core.js +3 -0
- package/dist/lib/keychain/secure.js +4 -4
- package/npm-shrinkwrap.json +2691 -2079
- package/package.json +15 -18
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
## Changelog
|
|
2
2
|
|
|
3
|
+
### 2.31.0
|
|
4
|
+
|
|
5
|
+
- #1397 feature(dev-env): Add ability to sync multisites
|
|
6
|
+
- #1399 chore(dev-deps): Remove stub type definitions
|
|
7
|
+
- #1394 FORNO-1609: Combine all error events into one error event
|
|
8
|
+
- #1345 feat(dev-env): Add Photon
|
|
9
|
+
- #1398 Fix coverage generation
|
|
10
|
+
- #1395 Add types for the other ways of using enquirer
|
|
11
|
+
- #1388 Typescript: refactor `vip-whoami`
|
|
12
|
+
- #1393 refactor: Convert `lib/{app-logs,envvar}` to TypeScript
|
|
13
|
+
- #1392 refactor: Convert `lib/config` to TypeScript
|
|
14
|
+
- #1391 refactor: Convert lib to TypeScript
|
|
15
|
+
- #1390 chore(deps): Update @automattic/vip-search-replace to 1.1.1
|
|
16
|
+
- #1389 Replace 'site' by 'environment' in error msg
|
|
17
|
+
- #1385 refactor: Convert lib/api to TypeScript
|
|
18
|
+
- #1387 chore(deps): Update @automattic/vip-search-replace to 1.1.0
|
|
19
|
+
- #1386 fix: Fix `getAbsolutePath()` and convert `utils.js` to TypeScript
|
|
20
|
+
- #1383 fix(dev-env): Pull uniqueLabel field from backend
|
|
21
|
+
- #1382 test: Reduce noise from tests
|
|
22
|
+
- #1384 fix: Add return type to parseEnvAliasFromArgv()
|
|
23
|
+
- #1381 refactor: Convert lib/cli and dependencies to TypeScript
|
|
24
|
+
- #1377 chore: Configure linting for TS files
|
|
25
|
+
- #1380 chore(dev-deps): Remove jest-environment-jsdom
|
|
26
|
+
- #1378 refactor: Convert analytics and dependencies to TypeScript
|
|
27
|
+
|
|
3
28
|
### 2.30.0
|
|
4
29
|
|
|
5
30
|
- #1264 feature(dev-env): Add dev-env-sync-sql command
|
|
@@ -222,6 +222,20 @@ services:
|
|
|
222
222
|
LANDO_NEEDS_EXEC: 1
|
|
223
223
|
<% } %>
|
|
224
224
|
|
|
225
|
+
<% if ( photon ) { %>
|
|
226
|
+
photon:
|
|
227
|
+
type: compose
|
|
228
|
+
services:
|
|
229
|
+
image: ghcr.io/automattic/vip-container-images/photon:latest
|
|
230
|
+
command: /usr/sbin/php-fpm
|
|
231
|
+
environment:
|
|
232
|
+
LANDO_NO_USER_PERMS: 1
|
|
233
|
+
LANDO_NO_SCRIPTS: 1
|
|
234
|
+
LANDO_NEEDS_EXEC: 1
|
|
235
|
+
volumes:
|
|
236
|
+
- ./uploads:/usr/share/webapps/photon/uploads:ro
|
|
237
|
+
<% } %>
|
|
238
|
+
|
|
225
239
|
tooling:
|
|
226
240
|
wp:
|
|
227
241
|
service: php
|
|
@@ -1,6 +1,27 @@
|
|
|
1
|
+
<% if ( photon ) { %>
|
|
2
|
+
|
|
3
|
+
location ^~ /wp-content/uploads/ {
|
|
4
|
+
expires max;
|
|
5
|
+
log_not_found off;
|
|
1
6
|
<% if ( mediaRedirectDomain ) { %>
|
|
7
|
+
if (!-f $request_filename) {
|
|
8
|
+
rewrite ^/(.*)$ <%= mediaRedirectDomain %>/$1 redirect;
|
|
9
|
+
}
|
|
10
|
+
<% } %>
|
|
11
|
+
|
|
12
|
+
include fastcgi_params;
|
|
13
|
+
fastcgi_param DOCUMENT_ROOT /usr/share/webapps/photon;
|
|
14
|
+
fastcgi_param SCRIPT_FILENAME /usr/share/webapps/photon/index.php;
|
|
15
|
+
fastcgi_param SCRIPT_NAME /index.php;
|
|
16
|
+
|
|
17
|
+
if ($request_uri ~* \.(gif|jpe?g|png)\?) {
|
|
18
|
+
fastcgi_pass photon:9000;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
<% } else if ( mediaRedirectDomain ) { %>
|
|
2
23
|
|
|
3
|
-
location
|
|
24
|
+
location ^~ /wp-content/uploads {
|
|
4
25
|
expires max;
|
|
5
26
|
log_not_found off;
|
|
6
27
|
try_files $uri @prod_site;
|
|
@@ -10,4 +31,4 @@ location @prod_site {
|
|
|
10
31
|
rewrite ^/(.*)$ <%= mediaRedirectDomain %>/$1 redirect;
|
|
11
32
|
}
|
|
12
33
|
|
|
13
|
-
<% } %>
|
|
34
|
+
<% } %>
|
|
Binary file
|
|
@@ -62,7 +62,8 @@ const appQuery = `
|
|
|
62
62
|
const trackerFn = (0, _tracker.makeCommandTracker)('dev_env_sync_sql', {
|
|
63
63
|
app: app.id,
|
|
64
64
|
env: env.uniqueLabel,
|
|
65
|
-
slug
|
|
65
|
+
slug,
|
|
66
|
+
multisite: env.isMultisite
|
|
66
67
|
});
|
|
67
68
|
await trackerFn('execute');
|
|
68
69
|
const lando = await (0, _devEnvironmentLando.bootstrapLando)();
|
|
@@ -74,6 +75,8 @@ const appQuery = `
|
|
|
74
75
|
throw new _userError.default('Environment needs to be started first');
|
|
75
76
|
}
|
|
76
77
|
const cmd = new _devEnvSyncSql.DevEnvSyncSQLCommand(app, env, slug, trackerFn);
|
|
78
|
+
// TODO: There's a function called handleCLIException for dev-env that handles exceptions but DevEnvSyncSQLCommand has its own implementation.
|
|
79
|
+
// We should probably use handleCLIException instead?
|
|
77
80
|
await cmd.run();
|
|
78
81
|
await trackerFn('success');
|
|
79
82
|
});
|
|
File without changes
|
|
@@ -64,6 +64,7 @@ cmd.argv(process.argv, async (arg, opt) => {
|
|
|
64
64
|
phpmyadmin: currentInstanceData.phpmyadmin,
|
|
65
65
|
xdebug: currentInstanceData.xdebug,
|
|
66
66
|
mailpit: (_currentInstanceData$ = currentInstanceData.mailpit) !== null && _currentInstanceData$ !== void 0 ? _currentInstanceData$ : currentInstanceData.mailhog,
|
|
67
|
+
photon: currentInstanceData.photon,
|
|
67
68
|
mediaRedirectDomain: currentInstanceData.mediaRedirectDomain,
|
|
68
69
|
multisite: false,
|
|
69
70
|
title: ''
|
|
@@ -46,17 +46,21 @@ const appQuery = `
|
|
|
46
46
|
module: 'export-sql',
|
|
47
47
|
requiredArgs: 0,
|
|
48
48
|
usage: 'vip export sql'
|
|
49
|
-
}).option('output', 'Specify the location where you want to save the export file').examples(examples).argv(process.argv, async (arg, {
|
|
49
|
+
}).option('output', 'Specify the location where you want to save the export file').option('generate-backup', 'Generate a new backup instead of using the available ones').examples(examples).argv(process.argv, async (arg, {
|
|
50
50
|
app,
|
|
51
51
|
env,
|
|
52
|
-
output
|
|
52
|
+
output,
|
|
53
|
+
generateBackup
|
|
53
54
|
}) => {
|
|
54
55
|
const trackerFn = (0, _tracker.makeCommandTracker)('export_sql', {
|
|
55
56
|
app: app.id,
|
|
56
57
|
env: env.uniqueLabel
|
|
57
58
|
});
|
|
58
59
|
await trackerFn('execute');
|
|
59
|
-
const exportCommand = new _exportSql.ExportSQLCommand(app, env,
|
|
60
|
+
const exportCommand = new _exportSql.ExportSQLCommand(app, env, {
|
|
61
|
+
outputFile: output,
|
|
62
|
+
generateBackup
|
|
63
|
+
}, trackerFn);
|
|
60
64
|
await exportCommand.run();
|
|
61
65
|
await trackerFn('success');
|
|
62
66
|
});
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.DB_BACKUP_JOB_STATUS_QUERY = exports.CREATE_DB_BACKUP_JOB_MUTATION = exports.BackupDBCommand = void 0;
|
|
7
|
+
var _chalk = _interopRequireDefault(require("chalk"));
|
|
8
|
+
var _graphqlTag = _interopRequireDefault(require("graphql-tag"));
|
|
9
|
+
var _api = _interopRequireWildcard(require("../lib/api"));
|
|
10
|
+
var exit = _interopRequireWildcard(require("../lib/cli/exit"));
|
|
11
|
+
var _utils = require("../lib/utils");
|
|
12
|
+
var _progress = require("../lib/cli/progress");
|
|
13
|
+
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); }
|
|
14
|
+
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; }
|
|
15
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
16
|
+
/**
|
|
17
|
+
*
|
|
18
|
+
* @format
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* External dependencies
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Internal dependencies
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
const DB_BACKUP_PROGRESS_POLL_INTERVAL = 1000;
|
|
30
|
+
const CREATE_DB_BACKUP_JOB_MUTATION = (0, _graphqlTag.default)`
|
|
31
|
+
mutation TriggerDatabaseBackup($input: AppEnvironmentTriggerDBBackupInput) {
|
|
32
|
+
triggerDatabaseBackup(input: $input) {
|
|
33
|
+
success
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
`;
|
|
37
|
+
exports.CREATE_DB_BACKUP_JOB_MUTATION = CREATE_DB_BACKUP_JOB_MUTATION;
|
|
38
|
+
const DB_BACKUP_JOB_STATUS_QUERY = (0, _graphqlTag.default)`
|
|
39
|
+
query AppBackupJobStatus($appId: Int!, $envId: Int!) {
|
|
40
|
+
app(id: $appId) {
|
|
41
|
+
id
|
|
42
|
+
environments(id: $envId) {
|
|
43
|
+
id
|
|
44
|
+
jobs(jobTypes: [db_backup]) {
|
|
45
|
+
id
|
|
46
|
+
type
|
|
47
|
+
completedAt
|
|
48
|
+
createdAt
|
|
49
|
+
inProgressLock
|
|
50
|
+
metadata {
|
|
51
|
+
name
|
|
52
|
+
value
|
|
53
|
+
}
|
|
54
|
+
progress {
|
|
55
|
+
status
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
`;
|
|
62
|
+
exports.DB_BACKUP_JOB_STATUS_QUERY = DB_BACKUP_JOB_STATUS_QUERY;
|
|
63
|
+
async function getBackupJob(appId, envId) {
|
|
64
|
+
const api = await (0, _api.default)();
|
|
65
|
+
const response = await api.query({
|
|
66
|
+
query: DB_BACKUP_JOB_STATUS_QUERY,
|
|
67
|
+
variables: {
|
|
68
|
+
appId,
|
|
69
|
+
envId
|
|
70
|
+
},
|
|
71
|
+
fetchPolicy: 'network-only'
|
|
72
|
+
});
|
|
73
|
+
const {
|
|
74
|
+
data: {
|
|
75
|
+
app: {
|
|
76
|
+
environments
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
} = response;
|
|
80
|
+
const job = environments[0].jobs[0];
|
|
81
|
+
return job || null;
|
|
82
|
+
}
|
|
83
|
+
async function createBackupJob(appId, envId) {
|
|
84
|
+
// Disable global error handling so that we can handle errors ourselves
|
|
85
|
+
(0, _api.disableGlobalGraphQLErrorHandling)();
|
|
86
|
+
const api = await (0, _api.default)();
|
|
87
|
+
await api.mutate({
|
|
88
|
+
mutation: CREATE_DB_BACKUP_JOB_MUTATION,
|
|
89
|
+
variables: {
|
|
90
|
+
input: {
|
|
91
|
+
id: appId,
|
|
92
|
+
environmentId: envId
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Re-enable global error handling
|
|
98
|
+
(0, _api.enableGlobalGraphQLErrorHandling)();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Library for a possible command in the future: vip backup db @app.env
|
|
102
|
+
class BackupDBCommand {
|
|
103
|
+
steps = {
|
|
104
|
+
PREPARE: 'prepare',
|
|
105
|
+
GENERATE: 'generate'
|
|
106
|
+
};
|
|
107
|
+
constructor(app, env, trackerFn = () => {}) {
|
|
108
|
+
this.app = app;
|
|
109
|
+
this.env = env;
|
|
110
|
+
this.progressTracker = new _progress.ProgressTracker([{
|
|
111
|
+
id: this.steps.PREPARE,
|
|
112
|
+
name: 'Preparing'
|
|
113
|
+
}, {
|
|
114
|
+
id: this.steps.GENERATE,
|
|
115
|
+
name: 'Generating backup'
|
|
116
|
+
}]);
|
|
117
|
+
this.track = trackerFn;
|
|
118
|
+
}
|
|
119
|
+
log(msg) {
|
|
120
|
+
if (this.silent) return;
|
|
121
|
+
console.log(msg);
|
|
122
|
+
}
|
|
123
|
+
isDone(job) {
|
|
124
|
+
return !job.inProgressLock;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Stops the progress tracker
|
|
129
|
+
*
|
|
130
|
+
* @return {void}
|
|
131
|
+
*/
|
|
132
|
+
stopProgressTracker() {
|
|
133
|
+
this.progressTracker.print();
|
|
134
|
+
this.progressTracker.stopPrinting();
|
|
135
|
+
}
|
|
136
|
+
async loadBackupJob() {
|
|
137
|
+
var _this$job, _this$job$metadata$fi, _this$job2, _this$job2$progress, _this$job3;
|
|
138
|
+
this.job = await getBackupJob(this.app.id, this.env.id);
|
|
139
|
+
this.backupName = ((_this$job = this.job) === null || _this$job === void 0 ? void 0 : (_this$job$metadata$fi = _this$job.metadata.find(meta => meta.name === 'backupName')) === null || _this$job$metadata$fi === void 0 ? void 0 : _this$job$metadata$fi.value) || 'Unknown';
|
|
140
|
+
this.jobStatus = (_this$job2 = this.job) === null || _this$job2 === void 0 ? void 0 : (_this$job2$progress = _this$job2.progress) === null || _this$job2$progress === void 0 ? void 0 : _this$job2$progress.status;
|
|
141
|
+
if ((_this$job3 = this.job) !== null && _this$job3 !== void 0 && _this$job3.completedAt) {
|
|
142
|
+
this.jobAge = (new Date() - new Date(this.job.completedAt)) / 1000 / 60;
|
|
143
|
+
} else {
|
|
144
|
+
this.jobAge = undefined;
|
|
145
|
+
}
|
|
146
|
+
return this.job;
|
|
147
|
+
}
|
|
148
|
+
async run(silent = false) {
|
|
149
|
+
var _this$job4;
|
|
150
|
+
this.silent = silent;
|
|
151
|
+
const readMoreMessage = '\nRead more about the limitations around database backups & exports here: https://docs.wpvip.com/technical-references/vip-dashboard/backups/ \n';
|
|
152
|
+
let noticeMessage = `\n${_chalk.default.yellow('NOTICE: ')}`;
|
|
153
|
+
noticeMessage += 'A fresh database backup will be generated only if there hasn\'t one already been created recently, either by our automated system or by a user on your site';
|
|
154
|
+
noticeMessage += readMoreMessage;
|
|
155
|
+
this.log(noticeMessage);
|
|
156
|
+
await this.loadBackupJob();
|
|
157
|
+
if ((_this$job4 = this.job) !== null && _this$job4 !== void 0 && _this$job4.inProgressLock) {
|
|
158
|
+
this.log('Database backup already in progress...');
|
|
159
|
+
} else {
|
|
160
|
+
try {
|
|
161
|
+
this.log('Creating a new database backup...');
|
|
162
|
+
this.progressTracker.stepRunning(this.steps.PREPARE);
|
|
163
|
+
this.progressTracker.startPrinting();
|
|
164
|
+
await createBackupJob(this.app.id, this.env.id);
|
|
165
|
+
} catch (err) {
|
|
166
|
+
var _err$message;
|
|
167
|
+
this.progressTracker.stepFailed(this.steps.PREPARE);
|
|
168
|
+
this.stopProgressTracker();
|
|
169
|
+
if ((_err$message = err.message) !== null && _err$message !== void 0 && _err$message.includes('Database backups limit reached')) {
|
|
170
|
+
await this.track('error', {
|
|
171
|
+
error_type: 'rate_limit_exceeded',
|
|
172
|
+
error_message: `Couldn't create a new database backup job: ${err === null || err === void 0 ? void 0 : err.message}`,
|
|
173
|
+
stack: err === null || err === void 0 ? void 0 : err.stack
|
|
174
|
+
});
|
|
175
|
+
let errMessage = err.message.replace('Database backups limit reached', 'New database backup generation failed because there was already one created recently, either by our automated system or by a user on your site');
|
|
176
|
+
errMessage = errMessage.replace('Retry after', '\nTo create a new backup, you can wait until:');
|
|
177
|
+
errMessage += `\n\nYou can also export the latest backup using the ${_chalk.default.green('vip @app.env export sql')} command`;
|
|
178
|
+
errMessage += readMoreMessage;
|
|
179
|
+
exit.withError(errMessage);
|
|
180
|
+
}
|
|
181
|
+
await this.track('error', {
|
|
182
|
+
error_type: 'db_backup_job_creation_failed',
|
|
183
|
+
error_message: `Database Backup job creation failed: ${err === null || err === void 0 ? void 0 : err.message}`,
|
|
184
|
+
stack: err === null || err === void 0 ? void 0 : err.stack
|
|
185
|
+
});
|
|
186
|
+
exit.withError(`Couldn't create a new database backup job: ${err === null || err === void 0 ? void 0 : err.message}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
this.progressTracker.stepSuccess(this.steps.PREPARE);
|
|
190
|
+
this.progressTracker.stepRunning(this.steps.GENERATE);
|
|
191
|
+
try {
|
|
192
|
+
await (0, _utils.pollUntil)(this.loadBackupJob.bind(this), DB_BACKUP_PROGRESS_POLL_INTERVAL, this.isDone.bind(this));
|
|
193
|
+
} catch (err) {
|
|
194
|
+
this.progressTracker.stepFailed(this.steps.GENERATE);
|
|
195
|
+
this.stopProgressTracker();
|
|
196
|
+
await this.track('error', {
|
|
197
|
+
error_type: 'db_backup_job_failed',
|
|
198
|
+
error_message: `Database Backup job failed: ${err === null || err === void 0 ? void 0 : err.message}`,
|
|
199
|
+
stack: err === null || err === void 0 ? void 0 : err.stack
|
|
200
|
+
});
|
|
201
|
+
exit.withError(`Failed to create new database backup: ${err === null || err === void 0 ? void 0 : err.message}`);
|
|
202
|
+
}
|
|
203
|
+
this.progressTracker.stepSuccess(this.steps.GENERATE);
|
|
204
|
+
this.stopProgressTracker();
|
|
205
|
+
await this.loadBackupJob();
|
|
206
|
+
if (this.jobStatus !== 'success') {
|
|
207
|
+
exit.withError('Failed to create a new database backup');
|
|
208
|
+
} else {
|
|
209
|
+
this.log(`New database backup created at ${this.backupName}`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
exports.BackupDBCommand = BackupDBCommand;
|
|
@@ -100,7 +100,9 @@ class DevEnvSyncSQLCommand {
|
|
|
100
100
|
* @return {Promise<void>} Promise that resolves when the export is complete
|
|
101
101
|
*/
|
|
102
102
|
async generateExport() {
|
|
103
|
-
const exportCommand = new _exportSql.ExportSQLCommand(this.app, this.env,
|
|
103
|
+
const exportCommand = new _exportSql.ExportSQLCommand(this.app, this.env, {
|
|
104
|
+
outputFile: this.gzFile
|
|
105
|
+
}, this.track);
|
|
104
106
|
await exportCommand.run();
|
|
105
107
|
}
|
|
106
108
|
|
|
@@ -135,7 +137,7 @@ class DevEnvSyncSQLCommand {
|
|
|
135
137
|
for (const site of networkSites) {
|
|
136
138
|
if (!site.blogId || site.blogId === 1) continue;
|
|
137
139
|
const url = site.homeUrl.replace(/https?:\/\//, '');
|
|
138
|
-
if (!this.searchReplaceMap[url])
|
|
140
|
+
if (!this.searchReplaceMap[url]) continue;
|
|
139
141
|
this.searchReplaceMap[url] = `${site.blogId}.${this.landoDomain}`;
|
|
140
142
|
}
|
|
141
143
|
}
|
|
@@ -165,6 +167,14 @@ class DevEnvSyncSQLCommand {
|
|
|
165
167
|
try {
|
|
166
168
|
await this.generateExport();
|
|
167
169
|
} catch (err) {
|
|
170
|
+
// this.generateExport probably catches all exceptions, track the event and runs exit.withError() but if things go really wrong
|
|
171
|
+
// and we have no tracking data, we would at least have it logged here.
|
|
172
|
+
// the following will not get executed if this.generateExport() calls exit.withError() on all exception
|
|
173
|
+
await this.track('error', {
|
|
174
|
+
error_type: 'export_sql_backup',
|
|
175
|
+
error_message: err === null || err === void 0 ? void 0 : err.message,
|
|
176
|
+
stack: err === null || err === void 0 ? void 0 : err.stack
|
|
177
|
+
});
|
|
168
178
|
exit.withError(`Error exporting SQL backup: ${err === null || err === void 0 ? void 0 : err.message}`);
|
|
169
179
|
}
|
|
170
180
|
try {
|
|
@@ -172,8 +182,10 @@ class DevEnvSyncSQLCommand {
|
|
|
172
182
|
await (0, _clientFileUploader.unzipFile)(this.gzFile, this.sqlFile);
|
|
173
183
|
console.log(`${_chalk.default.green('✓')} Extracted to ${this.sqlFile}`);
|
|
174
184
|
} catch (err) {
|
|
175
|
-
await this.track('
|
|
176
|
-
|
|
185
|
+
await this.track('error', {
|
|
186
|
+
error_type: 'archive_extraction',
|
|
187
|
+
error_message: err === null || err === void 0 ? void 0 : err.message,
|
|
188
|
+
stack: err === null || err === void 0 ? void 0 : err.stack
|
|
177
189
|
});
|
|
178
190
|
exit.withError(`Error extracting the SQL export: ${err.message}`);
|
|
179
191
|
}
|
|
@@ -181,6 +193,11 @@ class DevEnvSyncSQLCommand {
|
|
|
181
193
|
console.log('Extracting site urls from the SQL file...');
|
|
182
194
|
this.siteUrls = await extractSiteUrls(this.sqlFile);
|
|
183
195
|
} catch (err) {
|
|
196
|
+
await this.track('error', {
|
|
197
|
+
error_type: 'extract_site_urls',
|
|
198
|
+
error_message: err === null || err === void 0 ? void 0 : err.message,
|
|
199
|
+
stack: err === null || err === void 0 ? void 0 : err.stack
|
|
200
|
+
});
|
|
184
201
|
exit.withError(`Error extracting site URLs: ${err === null || err === void 0 ? void 0 : err.message}`);
|
|
185
202
|
}
|
|
186
203
|
console.log('Generating search-replace configuration...');
|
|
@@ -193,8 +210,10 @@ class DevEnvSyncSQLCommand {
|
|
|
193
210
|
await this.runSearchReplace();
|
|
194
211
|
console.log(`${_chalk.default.green('✓')} Search-replace operation is complete`);
|
|
195
212
|
} catch (err) {
|
|
196
|
-
await this.track('
|
|
197
|
-
|
|
213
|
+
await this.track('error', {
|
|
214
|
+
error_type: 'search_replace',
|
|
215
|
+
error_message: err === null || err === void 0 ? void 0 : err.message,
|
|
216
|
+
stack: err === null || err === void 0 ? void 0 : err.stack
|
|
198
217
|
});
|
|
199
218
|
exit.withError(`Error replacing domains: ${err === null || err === void 0 ? void 0 : err.message}`);
|
|
200
219
|
}
|
|
@@ -203,8 +222,10 @@ class DevEnvSyncSQLCommand {
|
|
|
203
222
|
await this.runImport();
|
|
204
223
|
console.log(`${_chalk.default.green('✓')} SQL file imported`);
|
|
205
224
|
} catch (err) {
|
|
206
|
-
await this.track('
|
|
207
|
-
|
|
225
|
+
await this.track('error', {
|
|
226
|
+
error_type: 'import_sql_file',
|
|
227
|
+
error_message: err === null || err === void 0 ? void 0 : err.message,
|
|
228
|
+
stack: err === null || err === void 0 ? void 0 : err.stack
|
|
208
229
|
});
|
|
209
230
|
exit.withError(`Error importing SQL file: ${err === null || err === void 0 ? void 0 : err.message}`);
|
|
210
231
|
}
|
|
@@ -13,6 +13,7 @@ var _format = require("../lib/cli/format");
|
|
|
13
13
|
var _progress = require("../lib/cli/progress");
|
|
14
14
|
var exit = _interopRequireWildcard(require("../lib/cli/exit"));
|
|
15
15
|
var _utils = require("../lib/utils");
|
|
16
|
+
var _backupDb = require("./backup-db");
|
|
16
17
|
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); }
|
|
17
18
|
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; }
|
|
18
19
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
@@ -185,6 +186,7 @@ async function createExportJob(appId, envId, backupId) {
|
|
|
185
186
|
*/
|
|
186
187
|
class ExportSQLCommand {
|
|
187
188
|
steps = {
|
|
189
|
+
GENERATE: 'generate',
|
|
188
190
|
PREPARE: 'prepare',
|
|
189
191
|
CREATE: 'create',
|
|
190
192
|
DOWNLOAD_LINK: 'downloadLink',
|
|
@@ -195,13 +197,14 @@ class ExportSQLCommand {
|
|
|
195
197
|
*
|
|
196
198
|
* @param {any} app The application object
|
|
197
199
|
* @param {any} env The environment object
|
|
198
|
-
* @param {
|
|
200
|
+
* @param {object} options The optional parameters
|
|
199
201
|
* @param {Function} trackerFn The progress tracker function
|
|
200
202
|
*/
|
|
201
|
-
constructor(app, env,
|
|
203
|
+
constructor(app, env, options = {}, trackerFn = () => {}) {
|
|
202
204
|
this.app = app;
|
|
203
205
|
this.env = env;
|
|
204
|
-
this.outputFile = typeof outputFile === 'string' ? (0, _utils.getAbsolutePath)(outputFile) : null;
|
|
206
|
+
this.outputFile = typeof options.outputFile === 'string' ? (0, _utils.getAbsolutePath)(options.outputFile) : null;
|
|
207
|
+
this.generateBackup = options.generateBackup || false;
|
|
205
208
|
this.progressTracker = new _progress.ProgressTracker([{
|
|
206
209
|
id: this.steps.PREPARE,
|
|
207
210
|
name: 'Preparing'
|
|
@@ -309,6 +312,10 @@ class ExportSQLCommand {
|
|
|
309
312
|
this.progressTracker.print();
|
|
310
313
|
this.progressTracker.stopPrinting();
|
|
311
314
|
}
|
|
315
|
+
async runBackupJob() {
|
|
316
|
+
const cmd = new _backupDb.BackupDBCommand(this.app, this.env);
|
|
317
|
+
await cmd.run(false);
|
|
318
|
+
}
|
|
312
319
|
|
|
313
320
|
/**
|
|
314
321
|
* Sequentially runs the steps of the export workflow
|
|
@@ -322,28 +329,35 @@ class ExportSQLCommand {
|
|
|
322
329
|
} catch (err) {
|
|
323
330
|
await this.track('error', {
|
|
324
331
|
error_type: 'cannot_write_to_path',
|
|
325
|
-
error_message: `Cannot write to the specified path: ${err === null || err === void 0 ? void 0 : err.message}
|
|
332
|
+
error_message: `Cannot write to the specified path: ${err === null || err === void 0 ? void 0 : err.message}`,
|
|
333
|
+
stack: err === null || err === void 0 ? void 0 : err.stack
|
|
326
334
|
});
|
|
327
335
|
exit.withError(`Cannot write to the specified path: ${err === null || err === void 0 ? void 0 : err.message}`);
|
|
328
336
|
}
|
|
329
337
|
}
|
|
330
|
-
|
|
338
|
+
if (this.generateBackup) {
|
|
339
|
+
await this.runBackupJob();
|
|
340
|
+
}
|
|
331
341
|
const {
|
|
332
342
|
latestBackup
|
|
333
343
|
} = await fetchLatestBackupAndJobStatus(this.app.id, this.env.id);
|
|
334
|
-
if (!
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
344
|
+
if (!this.generateBackup) {
|
|
345
|
+
if (!latestBackup) {
|
|
346
|
+
await this.track('error', {
|
|
347
|
+
error_type: 'no_backup_found',
|
|
348
|
+
error_message: 'No backup found for the site'
|
|
349
|
+
});
|
|
350
|
+
exit.withError(`No backup found for site ${this.app.name}`);
|
|
351
|
+
} else {
|
|
352
|
+
console.log(`${(0, _format.getGlyphForStatus)('success')} Latest backup found with timestamp ${latestBackup.createdAt}`);
|
|
353
|
+
}
|
|
340
354
|
} else {
|
|
341
|
-
console.log(`${(0, _format.getGlyphForStatus)('success')}
|
|
355
|
+
console.log(`${(0, _format.getGlyphForStatus)('success')} Backup created with timestamp ${latestBackup.createdAt}`);
|
|
342
356
|
}
|
|
343
357
|
if (await this.getExportJob()) {
|
|
344
358
|
console.log(`Attaching to an existing export for the backup with timestamp ${latestBackup.createdAt}`);
|
|
345
359
|
} else {
|
|
346
|
-
console.log(`
|
|
360
|
+
console.log(`Exporting database backup with timestamp ${latestBackup.createdAt}`);
|
|
347
361
|
try {
|
|
348
362
|
await createExportJob(this.app.id, this.env.id, latestBackup.id);
|
|
349
363
|
} catch (err) {
|
|
@@ -351,9 +365,16 @@ class ExportSQLCommand {
|
|
|
351
365
|
if (err !== null && err !== void 0 && err.message.includes('Backup Copy already in progress')) {
|
|
352
366
|
await this.track('error', {
|
|
353
367
|
error_type: 'job_already_running',
|
|
354
|
-
error_message: err === null || err === void 0 ? void 0 : err.message
|
|
368
|
+
error_message: err === null || err === void 0 ? void 0 : err.message,
|
|
369
|
+
stack: err === null || err === void 0 ? void 0 : err.stack
|
|
355
370
|
});
|
|
356
371
|
exit.withError('There is an export job already running for this environment: ' + `https://dashboard.wpvip.com/apps/${this.app.id}/${this.env.uniqueLabel}/data/database/backups\n` + 'Currently, we allow only one export job at a time, per site. Please try again later.');
|
|
372
|
+
} else {
|
|
373
|
+
await this.track('error', {
|
|
374
|
+
error_type: 'create_export_job',
|
|
375
|
+
error_message: err === null || err === void 0 ? void 0 : err.message,
|
|
376
|
+
stack: err === null || err === void 0 ? void 0 : err.stack
|
|
377
|
+
});
|
|
357
378
|
}
|
|
358
379
|
exit.withError(`Error creating export job: ${err === null || err === void 0 ? void 0 : err.message}`);
|
|
359
380
|
}
|
|
@@ -378,7 +399,8 @@ class ExportSQLCommand {
|
|
|
378
399
|
this.stopProgressTracker();
|
|
379
400
|
await this.track('error', {
|
|
380
401
|
error_type: 'download_failed',
|
|
381
|
-
error_message: err === null || err === void 0 ? void 0 : err.message
|
|
402
|
+
error_message: err === null || err === void 0 ? void 0 : err.message,
|
|
403
|
+
stack: err === null || err === void 0 ? void 0 : err.stack
|
|
382
404
|
});
|
|
383
405
|
exit.withError(`Error downloading exported file: ${err === null || err === void 0 ? void 0 : err.message}`);
|
|
384
406
|
}
|
|
@@ -307,12 +307,14 @@ async function promptForArguments(preselectedOptions, defaultOptions, suppressPr
|
|
|
307
307
|
xdebug: false,
|
|
308
308
|
xdebugConfig: preselectedOptions.xdebugConfig,
|
|
309
309
|
siteSlug: '',
|
|
310
|
-
mailpit: false
|
|
310
|
+
mailpit: false,
|
|
311
|
+
photon: false
|
|
311
312
|
};
|
|
312
313
|
const promptLabels = {
|
|
313
314
|
xdebug: 'XDebug',
|
|
314
315
|
phpmyadmin: 'phpMyAdmin',
|
|
315
|
-
mailpit: 'Mailpit'
|
|
316
|
+
mailpit: 'Mailpit',
|
|
317
|
+
photon: 'Photon'
|
|
316
318
|
};
|
|
317
319
|
if (!instanceData.mediaRedirectDomain && defaultOptions.mediaRedirectDomain) {
|
|
318
320
|
const mediaRedirectPromptText = `Would you like to redirect to ${defaultOptions.mediaRedirectDomain} for missing media files?`;
|
|
@@ -340,7 +342,7 @@ async function promptForArguments(preselectedOptions, defaultOptions, suppressPr
|
|
|
340
342
|
} else {
|
|
341
343
|
instanceData.elasticsearch = await promptForBoolean('Enable Elasticsearch (needed by Enterprise Search)?', !!defaultOptions.elasticsearch);
|
|
342
344
|
}
|
|
343
|
-
for (const service of ['phpmyadmin', 'xdebug', 'mailpit']) {
|
|
345
|
+
for (const service of ['phpmyadmin', 'xdebug', 'mailpit', 'photon']) {
|
|
344
346
|
if (service in instanceData) {
|
|
345
347
|
if (service in preselectedOptions) {
|
|
346
348
|
instanceData[service] = preselectedOptions[service];
|
|
@@ -636,7 +638,7 @@ function processVersionOption(value) {
|
|
|
636
638
|
}
|
|
637
639
|
function addDevEnvConfigurationOptions(command) {
|
|
638
640
|
// We leave the third parameter to undefined on some because the defaults are handled in preProcessInstanceData()
|
|
639
|
-
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);
|
|
641
|
+
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);
|
|
640
642
|
}
|
|
641
643
|
|
|
642
644
|
/**
|
|
@@ -94,7 +94,8 @@ async function sanitizeConfiguration(configuration) {
|
|
|
94
94
|
phpmyadmin: stringToBooleanIfDefined(configuration.phpmyadmin),
|
|
95
95
|
xdebug: stringToBooleanIfDefined(configuration.xdebug),
|
|
96
96
|
mailpit: stringToBooleanIfDefined((_configuration$mailpi = configuration.mailpit) !== null && _configuration$mailpi !== void 0 ? _configuration$mailpi : configuration.mailhog),
|
|
97
|
-
'media-redirect-domain': configuration['media-redirect-domain']
|
|
97
|
+
'media-redirect-domain': configuration['media-redirect-domain'],
|
|
98
|
+
photon: stringToBooleanIfDefined(configuration.photon)
|
|
98
99
|
};
|
|
99
100
|
|
|
100
101
|
// Remove undefined values
|
|
@@ -119,7 +120,8 @@ function mergeConfigurationFileOptions(preselectedOptions, configurationFileOpti
|
|
|
119
120
|
xdebug: configurationFileOptions.xdebug,
|
|
120
121
|
xdebugConfig: configurationFileOptions['xdebug-config'],
|
|
121
122
|
mailpit: (_configurationFileOpt = configurationFileOptions.mailpit) !== null && _configurationFileOpt !== void 0 ? _configurationFileOpt : configurationFileOptions.mailhog,
|
|
122
|
-
mediaRedirectDomain: configurationFileOptions['media-redirect-domain']
|
|
123
|
+
mediaRedirectDomain: configurationFileOptions['media-redirect-domain'],
|
|
124
|
+
photon: configurationFileOptions.photon
|
|
123
125
|
};
|
|
124
126
|
const mergedOptions = {};
|
|
125
127
|
Object.keys(configurationFileInstanceOptions).forEach(key => {
|
|
@@ -146,6 +146,9 @@ function preProcessInstanceData(instanceData) {
|
|
|
146
146
|
if (!newInstanceData.phpmyadmin) {
|
|
147
147
|
newInstanceData.phpmyadmin = false;
|
|
148
148
|
}
|
|
149
|
+
if (!newInstanceData.photon) {
|
|
150
|
+
newInstanceData.photon = false;
|
|
151
|
+
}
|
|
149
152
|
|
|
150
153
|
// Mailpit migration
|
|
151
154
|
if (!newInstanceData.mailpit) {
|