@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.
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, output, trackerFn);
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, this.gzFile, this.track);
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 {string} outputFile The output file path
200
+ * @param {object} options The optional parameters
199
201
  * @param {Function} trackerFn The progress tracker function
200
202
  */
201
- constructor(app, env, outputFile, trackerFn = () => {}) {
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
- console.log(`Fetching the latest backup for ${this.app.name}`);
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 (!latestBackup) {
336
- await this.track('error', {
337
- error_type: 'no_backup_found',
338
- error_message: 'No backup found for the site'
339
- });
340
- exit.withError(`No backup found for site ${this.app.name}`);
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')} Latest backup found with timestamp ${latestBackup.createdAt}`);
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(`Creating a new export for the backup with timestamp ${latestBackup.createdAt}`);
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 _keytar = _interopRequireDefault(require("keytar"));
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 _keytar.default.getPassword(service, service);
19
+ return _nodeKeytar.default.getPassword(service, service);
20
20
  }
21
21
  async setPassword(service, password) {
22
- await _keytar.default.setPassword(service, service, password);
22
+ await _nodeKeytar.default.setPassword(service, service, password);
23
23
  return true;
24
24
  }
25
25
  deletePassword(service) {
26
- return _keytar.default.deletePassword(service, service);
26
+ return _nodeKeytar.default.deletePassword(service, service);
27
27
  }
28
28
  }
29
29
  exports.default = Secure;