@automattic/vip 2.31.0 → 2.31.1-dev2

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.
Files changed (38) hide show
  1. package/.editorconfig +11 -0
  2. package/.prettierignore +6 -0
  3. package/CONTRIBUTING.md +17 -13
  4. package/dist/bin/vip-app-list.js +12 -10
  5. package/dist/bin/vip-dev-env-start.js +1 -1
  6. package/dist/bin/vip-dev-env-sync-sql.js +1 -1
  7. package/dist/bin/vip-export-sql.js +7 -3
  8. package/dist/bin/vip-import-media-abort.js +3 -3
  9. package/dist/bin/vip-import-media.js +3 -3
  10. package/dist/bin/vip-import-sql.js +2 -1
  11. package/dist/bin/vip-sync.js +27 -13
  12. package/dist/bin/vip-validate-preflight.js +16 -14
  13. package/dist/bin/vip-wp.js +9 -9
  14. package/dist/commands/backup-db.js +213 -0
  15. package/dist/commands/dev-env-sync-sql.js +3 -1
  16. package/dist/commands/export-sql.js +27 -14
  17. package/dist/lib/api/cache-purge.js +2 -10
  18. package/dist/lib/app-logs/app-logs.js +10 -4
  19. package/dist/lib/cli/format.js +2 -2
  20. package/dist/lib/config/software.js +10 -9
  21. package/dist/lib/constants/dev-environment.js +3 -6
  22. package/dist/lib/dev-environment/dev-environment-cli.js +3 -3
  23. package/dist/lib/dev-environment/dev-environment-core.js +52 -41
  24. package/dist/lib/dev-environment/dev-environment-lando.js +3 -1
  25. package/dist/lib/envvar/api-delete.js +2 -11
  26. package/dist/lib/envvar/api-get-all.js +3 -10
  27. package/dist/lib/envvar/api-list.js +3 -10
  28. package/dist/lib/envvar/api-set.js +2 -12
  29. package/dist/lib/keychain/secure.js +4 -4
  30. package/dist/lib/media-import/status.js +18 -18
  31. package/dist/lib/validations/site-type.js +1 -1
  32. package/dist/lib/validations/sql.js +2 -1
  33. package/dist/lib/vip-import-validate-files.js +1 -0
  34. package/helpers/check-version.js +7 -5
  35. package/helpers/prepublishOnly.js +88 -0
  36. package/npm-shrinkwrap.json +2760 -5146
  37. package/package.json +23 -23
  38. package/tsconfig.json +5 -9
package/.editorconfig ADDED
@@ -0,0 +1,11 @@
1
+ root = true
2
+
3
+ [*]
4
+ charset = utf-8
5
+ end_of_line = lf
6
+ indent_style = tab
7
+ insert_final_newline = true
8
+ trim_trailing_whitespace = true
9
+
10
+ [*.json,*.yaml,*.yml]
11
+ indent_style = space
@@ -0,0 +1,6 @@
1
+ *.generated.d.ts
2
+ /CHANGELOG.md
3
+ /__fixtures__/
4
+ /dist/
5
+ /flow-typed/
6
+ /src/graphqlTypes.d.ts
package/CONTRIBUTING.md CHANGED
@@ -35,35 +35,38 @@ Who doesn't like a good console.log for debugging?
35
35
  Well, sometimes it's insufficient, luckily it's not too complicated to use a debugger.
36
36
 
37
37
  1. First, make sure to run the `npm run build:watch`, this will generate source maps
38
- 2. Run the command you want via `node --inspect`, like so: `node --inspect ./dist/bin/vip-dev-env-import-sql.js`
38
+ 2. Run the command you want via `node --inspect`, like so: `node --inspect ./dist/bin/vip-dev-env-import-sql.js`
39
39
  3. Note the port the debugger is listening on:
40
+
40
41
  ```
41
42
  Debugger listening on ws://127.0.0.1:9229/db6c03e9-2585-4a08-a1c6-1fee0295c9ff
42
43
  For help, see: https://nodejs.org/en/docs/inspector
43
44
  ```
45
+
44
46
  4. In your editor of choice attach to the debugger. For VSCode: Hit 'Run and Debug' panel, hit the "gear" icon (open launch.json), make your `Attach` configuration entry to look like so:
45
- Make sure the `port` matches the port from step 3, and the `runtimeExecutable` matches the exact `node` executable you ran. If you use a version manager like `nvm`, its especially important to check this.
47
+ Make sure the `port` matches the port from step 3, and the `runtimeExecutable` matches the exact `node` executable you ran. If you use a version manager like `nvm`, its especially important to check this.
46
48
 
47
49
  ```json
48
- {
49
- "name": "Attach",
50
- "port": 9229,
51
- "request": "attach",
52
- "skipFiles": ["<node_internals>/**"],
53
- "type": "node",
54
- "runtimeExecutable": "/Users/user/.nvm/versions/node/v14.18.2/bin/node"
55
- }
50
+ {
51
+ "name": "Attach",
52
+ "port": 9229,
53
+ "request": "attach",
54
+ "skipFiles": [ "<node_internals>/**" ],
55
+ "type": "node",
56
+ "runtimeExecutable": "/Users/user/.nvm/versions/node/v14.18.2/bin/node"
57
+ }
56
58
  ```
57
59
 
58
60
  5. Set your breakpoints and whatnot, hit the play button.
59
61
  6. Confirm that you attached the debugger to continue command execution
60
62
  7. Squash them bugs 🐛🔨.
61
63
  8. [Optional but recommended] Pay it forward and implement a similar approach to other internal/external tooling.
64
+
62
65
  ### Adding commands
63
66
 
64
- * New command names should use the singular form (e.g. site vs sites).
65
- * Add new commands to `package.json#bin`.
66
- * Run `npm link` so that `arg` knows how to spawn the command locally. (Skipping this step will result in `Error: spawn vip-command ENOENT`.)
67
+ - New command names should use the singular form (e.g. site vs sites).
68
+ - Add new commands to `package.json#bin`.
69
+ - Run `npm link` so that `arg` knows how to spawn the command locally. (Skipping this step will result in `Error: spawn vip-command ENOENT`.)
67
70
 
68
71
  ### Adding libraries
69
72
 
@@ -83,6 +86,7 @@ Our release flow for VIP CLI follows this pattern:
83
86
  - Finally, release your changes as a new minor or major NPM version. Ping in the #vip-platform channel to notify folks of a new release, but please feel free to release your changes without any blockers from the team. Any team member that is part of the Automattic NPM organization can release a new version; if you aren't a member, generic credentials are available in the Secret Store.
84
87
 
85
88
  ### Changelogs
89
+
86
90
  Changelogs allow customers to keep up with all the changes happening across our VIP Platform. Changelogs for VIP CLI are posted to the [VIP Cloud Changelog P2](https://wpvipchangelog.wordpress.com/), along with the repository’s `README.md`.
87
91
 
88
92
  ## Releasing / Publishing
@@ -21,17 +21,19 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
21
21
  try {
22
22
  response = await api.query({
23
23
  // $FlowFixMe: gql template is not supported by flow
24
- query: (0, _graphqlTag.default)`query Apps( $first: Int, $after: String ) {
25
- apps( first: $first, after: $after ) {
26
- total,
27
- nextCursor
28
- edges {
29
- id,
30
- name,
31
- repo
32
- }
24
+ query: (0, _graphqlTag.default)`
25
+ query Apps($first: Int, $after: String) {
26
+ apps(first: $first, after: $after) {
27
+ total
28
+ nextCursor
29
+ edges {
30
+ id
31
+ name
32
+ repo
33
33
  }
34
- }`,
34
+ }
35
+ }
36
+ `,
35
37
  variables: {
36
38
  first: 100,
37
39
  after: null // TODO make dynamic
@@ -31,7 +31,7 @@ const examples = [{
31
31
  usage: `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} start`,
32
32
  description: 'Starts a local dev environment'
33
33
  }];
34
- (0, _command.default)().option('slug', 'Custom name of the dev environment').option('skip-rebuild', 'Only start stopped services').option(['w', 'skip-wp-versions-check'], 'Skip propting for wordpress update if non latest').option('vscode', 'Open environment workspace in VSCode').examples(examples).argv(process.argv, async (arg, opt) => {
34
+ (0, _command.default)().option('slug', 'Custom name of the dev environment').option('skip-rebuild', 'Only start stopped services').option(['w', 'skip-wp-versions-check'], 'Skip prompt to update WordPress version if not on latest').option('vscode', 'Generate a Visual Studio Code Workspace file and open it').examples(examples).argv(process.argv, async (arg, opt) => {
35
35
  const slug = await (0, _devEnvironmentCli.getEnvironmentName)(opt);
36
36
  const lando = await (0, _devEnvironmentLando.bootstrapLando)();
37
37
  await (0, _devEnvironmentCli.validateDependencies)(lando, slug);
@@ -24,7 +24,7 @@ var _tracker = require("../lib/tracker");
24
24
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
25
25
  const examples = [{
26
26
  usage: `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} sync sql @my-test.develop --slug=my_site`,
27
- description: 'Syncs with the `my-test` site\'s `develop` environment database into `my_site`'
27
+ description: "Syncs with the `my-test` site's `develop` environment database into `my_site`"
28
28
  }];
29
29
  const appQuery = `
30
30
  id,
@@ -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
  });
@@ -41,8 +41,8 @@ const appQuery = `
41
41
  }
42
42
  `;
43
43
  const ABORT_IMPORT_MUTATION = (0, _graphqlTag.default)`
44
- mutation AbortMediaImport( $input: AppEnvironmentAbortMediaImportInput ) {
45
- abortMediaImport( input: $input ) {
44
+ mutation AbortMediaImport($input: AppEnvironmentAbortMediaImportInput) {
45
+ abortMediaImport(input: $input) {
46
46
  applicationId
47
47
  environmentId
48
48
  mediaImportStatusChange {
@@ -60,7 +60,7 @@ const ABORT_IMPORT_MUTATION = (0, _graphqlTag.default)`
60
60
  envContext: true,
61
61
  requiredArgs: 0,
62
62
  requireConfirm: `
63
- ${_chalk.default.red.bold('By running this command, the Media Import running on your App will stop and can\'t be resumed.')}
63
+ ${_chalk.default.red.bold("By running this command, the Media Import running on your App will stop and can't be resumed.")}
64
64
  ${_chalk.default.red.bold('Are you sure you want to abort this Media Import?')}
65
65
  `
66
66
  }).argv(process.argv, async (arg, {
@@ -38,8 +38,8 @@ const appQuery = `
38
38
  }
39
39
  `;
40
40
  const START_IMPORT_MUTATION = (0, _graphqlTag.default)`
41
- mutation StartMediaImport( $input: AppEnvironmentStartMediaImportInput ) {
42
- startMediaImport( input: $input ) {
41
+ mutation StartMediaImport($input: AppEnvironmentStartMediaImportInput) {
42
+ startMediaImport(input: $input) {
43
43
  applicationId
44
44
  environmentId
45
45
  mediaImportStatus {
@@ -83,7 +83,7 @@ function isSupportedUrl(urlToTest) {
83
83
  module: 'import-media',
84
84
  requiredArgs: 1,
85
85
  requireConfirm: `
86
- ${_chalk.default.red.bold('NOTE: If the provided archive\'s directory structure contains an `uploads/` directory,')}
86
+ ${_chalk.default.red.bold("NOTE: If the provided archive's directory structure contains an `uploads/` directory,")}
87
87
  ${_chalk.default.red.bold('only the files present inside that directory will be imported and the rest will be ignored.')}
88
88
  ${_chalk.default.red.bold('If no `uploads/` directory is found, all files will be imported, as is.')}
89
89
 
@@ -296,7 +296,8 @@ const displayPlaybook = ({
296
296
  if (wpSite.id === 1) {
297
297
  siteRegex = /^wp_[a-z]+/i;
298
298
  } else {
299
- siteRegex = new RegExp(`^wp_${wpSite.id}_[a-z]+`, 'i');
299
+ // eslint-disable-next-line security/detect-non-literal-regexp
300
+ siteRegex = new RegExp(`^wp_${parseInt(wpSite.id, 10)}_[a-z]+`, 'i');
300
301
  }
301
302
  const tableNamesInGroup = tableNames.filter(name => siteRegex.test(name));
302
303
  return {
@@ -35,14 +35,14 @@ const appQuery = `id,name,environments{
35
35
  await api.mutate({
36
36
  // $FlowFixMe: gql template is not supported by flow
37
37
  mutation: (0, _graphqlTag.default)`
38
- mutation SyncEnvironmentMutation($input: AppEnvironmentSyncInput){
39
- syncEnvironment(input: $input){
40
- environment{
41
- id
42
- }
43
- }
38
+ mutation SyncEnvironmentMutation($input: AppEnvironmentSyncInput) {
39
+ syncEnvironment(input: $input) {
40
+ environment {
41
+ id
44
42
  }
45
- `,
43
+ }
44
+ }
45
+ `,
46
46
  variables: {
47
47
  input: {
48
48
  id: opts.app.id,
@@ -106,15 +106,29 @@ const appQuery = `id,name,environments{
106
106
  // The rest of the iterations are just for moving the spinner
107
107
  api.query({
108
108
  // $FlowFixMe: gql template is not supported by flow
109
- query: (0, _graphqlTag.default)`query App( $id: Int, $sync: Int ) {
110
- app( id: $id ){
111
- id,name,environments{
112
- id,name,defaultDomain,branch,datacenter,syncProgress( sync: $sync ){
113
- status,sync,steps{name,status}
109
+ query: (0, _graphqlTag.default)`
110
+ query App($id: Int, $sync: Int) {
111
+ app(id: $id) {
112
+ id
113
+ name
114
+ environments {
115
+ id
116
+ name
117
+ defaultDomain
118
+ branch
119
+ datacenter
120
+ syncProgress(sync: $sync) {
121
+ status
122
+ sync
123
+ steps {
124
+ name
125
+ status
126
+ }
114
127
  }
115
128
  }
116
129
  }
117
- }`,
130
+ }
131
+ `,
118
132
  fetchPolicy: 'network-only',
119
133
  variables: {
120
134
  id: opts.app.id,
@@ -68,19 +68,20 @@ async function getBuildConfiguration(application, environment) {
68
68
  // Disable the global GraphQL error handling, so we can catch Unauthorized errors and recommend next steps.
69
69
  (0, _api.disableGlobalGraphQLErrorHandling)();
70
70
  const buildConfigQuery = (0, _graphqlTag.default)`
71
- query BuildConfig( $appId: Int, $envId: Int ) {
72
- app(id: $appId) {
73
- environments(id: $envId) {
74
- id,
75
- buildConfiguration {
76
- buildType
77
- nodeBuildDockerEnv,
78
- nodeJSVersion,
79
- npmToken,
80
- }
81
- }
82
- }
83
- }`;
71
+ query BuildConfig($appId: Int, $envId: Int) {
72
+ app(id: $appId) {
73
+ environments(id: $envId) {
74
+ id
75
+ buildConfiguration {
76
+ buildType
77
+ nodeBuildDockerEnv
78
+ nodeJSVersion
79
+ npmToken
80
+ }
81
+ }
82
+ }
83
+ }
84
+ `;
84
85
  try {
85
86
  const result = await api.query({
86
87
  query: buildConfigQuery,
@@ -122,7 +123,7 @@ async function vipValidatePreflightCommand(arg, opt) {
122
123
  };
123
124
  await (0, _tracker.trackEvent)('validate_preflight_command_execute', baseTrackingParams);
124
125
  logToConsole(' /\\ /\\__ _ _ __ _ __ ___ ___ _ __ (_) __ _ ');
125
- logToConsole(' / /_/ / _` | \'__| \'_ ` _ \\ / _ \\| \'_ \\| |/ _` |');
126
+ logToConsole(" / /_/ / _` | '__| '_ ` _ \\ / _ \\| '_ \\| |/ _` |");
126
127
  logToConsole('/ __ / (_| | | | | | | | | (_) | | | | | (_| |');
127
128
  logToConsole('\\/ /_/ \\__,_|_| |_| |_| |_|\\___/|_| |_|_|\\__,_|');
128
129
  logToConsole('VIP Harmonia - Application testing made easy\n');
@@ -155,6 +156,7 @@ async function vipValidatePreflightCommand(arg, opt) {
155
156
  const packageJSONfile = _path.default.resolve(opt.path, 'package.json');
156
157
  let packageJSON;
157
158
  try {
159
+ // eslint-disable-next-line security/detect-non-literal-require
158
160
  packageJSON = require(packageJSONfile);
159
161
  siteOptions.setPackageJSON(packageJSON);
160
162
  } catch (error) {
@@ -100,15 +100,15 @@ const getTokenForCommand = async (appId, envId, command) => {
100
100
  return api.mutate({
101
101
  // $FlowFixMe: gql template is not supported by flow
102
102
  mutation: (0, _graphqlTag.default)`
103
- mutation TriggerWPCLICommandMutation($input: AppEnvironmentTriggerWPCLICommandInput ){
104
- triggerWPCLICommandOnAppEnvironment( input: $input ) {
105
- inputToken
106
- command {
107
- guid
108
- }
103
+ mutation TriggerWPCLICommandMutation($input: AppEnvironmentTriggerWPCLICommandInput) {
104
+ triggerWPCLICommandOnAppEnvironment(input: $input) {
105
+ inputToken
106
+ command {
107
+ guid
109
108
  }
110
109
  }
111
- `,
110
+ }
111
+ `,
112
112
  variables: {
113
113
  input: {
114
114
  id: appId,
@@ -125,8 +125,8 @@ const cancelCommand = async guid => {
125
125
  return api.mutate({
126
126
  // $FlowFixMe: gql template is not supported by flow
127
127
  mutation: (0, _graphqlTag.default)`
128
- mutation cancelWPCLICommand($input: CancelWPCLICommandInput ){
129
- cancelWPCLICommand( input: $input ) {
128
+ mutation cancelWPCLICommand($input: CancelWPCLICommandInput) {
129
+ cancelWPCLICommand(input: $input) {
130
130
  command {
131
131
  id
132
132
  }
@@ -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 for backup generation'
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 = '\nLearn more about the database backup limitations here: https://docs.wpvip.com/technical-references/vip-dashboard/backups/ \n';
152
+ let noticeMessage = `\n${_chalk.default.yellow('NOTICE: ')}`;
153
+ noticeMessage += 'A new database backup will be created only if no recent backup is available, created by our automated system or manually by a user.';
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', 'Unable to create a new database backup as a recent backup already exists. This backup was created by our automated system or a user.');
176
+ errMessage = errMessage.replace('Retry after', '\nThe next possible backup attempt can be made on:');
177
+ errMessage += `\n\nYou can export the (existing) latest database backup with the command: ${_chalk.default.green('vip @app.env export sql')}`;
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