@automattic/vip 2.31.0 → 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/automattic-vip-2.31.1-dev.tgz +0 -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 +3 -1
- package/dist/commands/export-sql.js +25 -12
- package/dist/lib/keychain/secure.js +4 -4
- package/npm-shrinkwrap.json +2604 -1924
- package/package.json +14 -14
|
Binary file
|
|
@@ -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
|
|
|
@@ -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
|
|
@@ -328,23 +335,29 @@ class ExportSQLCommand {
|
|
|
328
335
|
exit.withError(`Cannot write to the specified path: ${err === null || err === void 0 ? void 0 : err.message}`);
|
|
329
336
|
}
|
|
330
337
|
}
|
|
331
|
-
|
|
338
|
+
if (this.generateBackup) {
|
|
339
|
+
await this.runBackupJob();
|
|
340
|
+
}
|
|
332
341
|
const {
|
|
333
342
|
latestBackup
|
|
334
343
|
} = await fetchLatestBackupAndJobStatus(this.app.id, this.env.id);
|
|
335
|
-
if (!
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
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
|
+
}
|
|
341
354
|
} else {
|
|
342
|
-
console.log(`${(0, _format.getGlyphForStatus)('success')}
|
|
355
|
+
console.log(`${(0, _format.getGlyphForStatus)('success')} Backup created with timestamp ${latestBackup.createdAt}`);
|
|
343
356
|
}
|
|
344
357
|
if (await this.getExportJob()) {
|
|
345
358
|
console.log(`Attaching to an existing export for the backup with timestamp ${latestBackup.createdAt}`);
|
|
346
359
|
} else {
|
|
347
|
-
console.log(`
|
|
360
|
+
console.log(`Exporting database backup with timestamp ${latestBackup.createdAt}`);
|
|
348
361
|
try {
|
|
349
362
|
await createExportJob(this.app.id, this.env.id, latestBackup.id);
|
|
350
363
|
} catch (err) {
|
|
@@ -4,7 +4,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.default = void 0;
|
|
7
|
-
var
|
|
7
|
+
var _nodeKeytar = _interopRequireDefault(require("@postman/node-keytar"));
|
|
8
8
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
9
9
|
/**
|
|
10
10
|
* External dependencies
|
|
@@ -16,14 +16,14 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
16
16
|
|
|
17
17
|
class Secure {
|
|
18
18
|
getPassword(service) {
|
|
19
|
-
return
|
|
19
|
+
return _nodeKeytar.default.getPassword(service, service);
|
|
20
20
|
}
|
|
21
21
|
async setPassword(service, password) {
|
|
22
|
-
await
|
|
22
|
+
await _nodeKeytar.default.setPassword(service, service, password);
|
|
23
23
|
return true;
|
|
24
24
|
}
|
|
25
25
|
deletePassword(service) {
|
|
26
|
-
return
|
|
26
|
+
return _nodeKeytar.default.deletePassword(service, service);
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
exports.default = Secure;
|