@automattic/vip 2.28.0-dev7 → 2.28.0

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 CHANGED
@@ -1,5 +1,53 @@
1
1
  ## Changelog
2
2
 
3
+ ### 2.28.0
4
+
5
+ - #1343 fix(dev-env): Force image pulling after recovering Landofile
6
+ - #1342 fix(dev-env): Do not fail when lando_bridge_network does not exist
7
+ - #1329 feat(dev-env)!: Replace MailHog with Mailpit
8
+ - #1340 feat(dev-env): Kill lando proxy when last env stops
9
+ - #1324 feat(dev-env): Better detection of a Docker socket
10
+ - #1332 Update inline search-replace help for import sql command
11
+ - #1338 chore(dev-deps): Update @automattic/eslint-plugin-wpvip from 0.4.0 to 0.5.2
12
+ - #1337 chore(dev-deps): Update flow-bin from 0.203.1 to 0.204.0
13
+ - #1336 chore(dev-deps): Update eslint from 8.37.0 to 8.38.0
14
+ - #1325 tests: Fix timing out tests on Windows
15
+ - #1333 [dev-env] Readds xdebug_config to vip dev-env update
16
+ - #1328 feat(dev-env): Profile Lando-related functions
17
+ - #1326 tests(e2e): Force Jest to exit after all tests have completed running
18
+ - #1327 ci: Set `DO_NOT_TRACK=1` for E2E tests
19
+ - #1323 Revert "feat(dev-env): Better detection of a Docker socket"
20
+ - #1316 feat(dev-env): Better detection of a Docker socket
21
+ - #1321 chore(dev-deps): Update Babel-related packages
22
+ - #1320 chore(dev-deps): Update flow-bin from 0.202.1 to 0.203.1
23
+ - #1319 chore(dev-deps): Update eslint from 8.36.0 to 8.37.0
24
+ - #1318 chore(deps): Update lando to d75a4e8
25
+ - #1315 Fix preselected options on update
26
+ - #1314 chore(deps): Update socket.io-client to 4.6.1
27
+ - #1311 chore(dev-deps): Update eslint to 8.36.0
28
+ - #1310 chore(deps): Update ejs to 3.1.9
29
+ - #1312 chore(dev-deps): Update prettier to 2.8.7
30
+ - #1309 chore(dev-deps): Update flow-bin to 0.202.1
31
+ - #1308 chore(dev-deps): Update dockerode to 3.3.5
32
+ - #1307 chore(dev-deps): Update nock to 13.3.0
33
+ - #1306 chore(dev-deps): Forcefully update lodash for publish-please to 4.17.21
34
+ - #1305 chore(deps): Update Lando
35
+ - #1303 chore(deps): Replace socket.io-stream with @wearemothership/socket.io-stream
36
+ - #1301 chore(deps): Update lando-cli
37
+ - #1302 fix(ci): Do not prompt for WP update
38
+ - #1297 chore(dev-deps): Update Jest-related packages to 29.5.0
39
+ - #1299 chore(dev-deps): Update eslint-related stuff
40
+ - #1298 chore(dev-deps): Update flow-bin from 0.196.3 to 0.201.0
41
+ - #1293 chore(dev-deps): Update Babel-related packages
42
+ - #1300 chore(deps): Update Lando
43
+ - #1294 chore(dev-deps): Update Jest-related packages to 29.4.3
44
+ - #1296 Add trackers to capture execute, success and error events
45
+ - #1292 Fix requiredArgs for other vip commands
46
+ - #1290 Fail with a msg if an export job with a different backup is running
47
+ - #1254 refactor(dev-env): Remove healthcheck hook
48
+ - #1291 Dev-env: Fix silent failing when passing invalid subcommand
49
+ - #1282 Prefix urls with // to fix the search-replace problem
50
+
3
51
  ### 2.27.0
4
52
 
5
53
  - #1287 fix(dev-env): Pull fresh images for new environments
@@ -9,6 +9,8 @@ proxy:
9
9
  <% } %>
10
10
  phpmyadmin:
11
11
  - <%= siteSlug %>-pma.vipdev.lndo.site
12
+ mailpit:
13
+ - <%= siteSlug %>-mailpit.vipdev.lndo.site:8025
12
14
 
13
15
  services:
14
16
  devtools:
@@ -197,9 +199,19 @@ services:
197
199
  clientcode_vipconfig: {}
198
200
  <% } %>
199
201
 
200
- <% if ( mailhog ) { %>
201
- mailhog:
202
- type: mailhog
202
+ <% if ( mailpit ) { %>
203
+ mailpit:
204
+ type: compose
205
+ services:
206
+ image: axllent/mailpit:latest
207
+ command: /mailpit
208
+ ports:
209
+ - ":1025"
210
+ - ":8025"
211
+ environment:
212
+ LANDO_NO_USER_PERMS: 1
213
+ LANDO_NO_SCRIPTS: 1
214
+ LANDO_NEEDS_EXEC: 1
203
215
  <% } %>
204
216
 
205
217
  tooling:
@@ -62,6 +62,7 @@ cmd.argv(process.argv, async (arg, opt) => {
62
62
  const lando = await (0, _devEnvironmentLando.bootstrapLando)();
63
63
  await (0, _devEnvironmentCli.validateDependencies)(lando, slug);
64
64
  debug('Args: ', arg, 'Options: ', opt);
65
+ (0, _devEnvironmentCli.handleDeprecatedOptions)(opt);
65
66
  const trackingInfo = {
66
67
  slug,
67
68
  app: opt.app,
@@ -28,7 +28,8 @@ const userMap = {
28
28
  'demo-app-code': 'www-data',
29
29
  elasticsearch: 'elasticsearch',
30
30
  phpmyadmin: 'www-data',
31
- mailhog: 'mailhog'
31
+ mailhog: 'mailhog',
32
+ mailpit: 'root'
32
33
  };
33
34
  const examples = [{
34
35
  usage: `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} shell`,
@@ -33,33 +33,27 @@ const cmd = (0, _command.default)().option('slug', 'Custom name of the dev envir
33
33
  cmd.examples(examples);
34
34
  cmd.argv(process.argv, async (arg, opt) => {
35
35
  const slug = await (0, _devEnvironmentCli.getEnvironmentName)(opt);
36
+ (0, _devEnvironmentCli.handleDeprecatedOptions)(opt);
36
37
  const lando = await (0, _devEnvironmentLando.bootstrapLando)();
37
38
  await (0, _devEnvironmentCli.validateDependencies)(lando, slug);
38
39
  const trackingInfo = (0, _devEnvironmentCli.getEnvTrackingInfo)(slug);
39
40
  await (0, _tracker.trackEvent)('dev_env_update_command_execute', trackingInfo);
40
41
  try {
42
+ var _currentInstanceData$;
41
43
  const environmentAlreadyExists = await (0, _devEnvironmentCore.doesEnvironmentExist)((0, _devEnvironmentCore.getEnvironmentPath)(slug));
42
44
  if (!environmentAlreadyExists) {
43
45
  throw new Error(_devEnvironment.DEV_ENVIRONMENT_NOT_FOUND);
44
46
  }
45
47
  const currentInstanceData = (0, _devEnvironmentCore.readEnvironmentData)(slug);
46
48
  debug('Read instance data', currentInstanceData);
47
- const configurationFileOptions = await (0, _devEnvironmentConfigurationFile.getConfigurationFileOptions)();
48
- let preselectedOptions = Object.assign({}, opt);
49
- if (Object.keys(configurationFileOptions).length > 0) {
50
- preselectedOptions = (0, _devEnvironmentConfigurationFile.mergeConfigurationFileOptions)(opt, configurationFileOptions);
51
- }
52
-
53
- // Title and multisite can't be changed during update
54
- const selectedOptions = {
49
+ const preselectedOptions = {
55
50
  title: currentInstanceData.wpTitle,
56
- multisite: currentInstanceData.multisite
51
+ multisite: currentInstanceData.multisite,
52
+ ...opt
57
53
  };
58
- Object.keys(preselectedOptions).forEach(key => {
59
- if (!(key in selectedOptions)) {
60
- selectedOptions[key] = preselectedOptions[key];
61
- }
62
- });
54
+ const configurationFileOptions = await (0, _devEnvironmentConfigurationFile.getConfigurationFileOptions)();
55
+ const thereAreOptionsFromConfigFile = Object.keys(configurationFileOptions).length > 0;
56
+ const finalPreselectedOptions = (0, _devEnvironmentConfigurationFile.mergeConfigurationFileOptions)(preselectedOptions, configurationFileOptions);
63
57
  const defaultOptions = {
64
58
  appCode: currentInstanceData.appCode.dir || currentInstanceData.appCode.tag || 'latest',
65
59
  muPlugins: currentInstanceData.muPlugins.dir || currentInstanceData.muPlugins.tag || 'latest',
@@ -69,7 +63,7 @@ cmd.argv(process.argv, async (arg, opt) => {
69
63
  mariadb: currentInstanceData.mariadb,
70
64
  phpmyadmin: currentInstanceData.phpmyadmin,
71
65
  xdebug: currentInstanceData.xdebug,
72
- mailhog: currentInstanceData.mailhog,
66
+ mailpit: (_currentInstanceData$ = currentInstanceData.mailpit) !== null && _currentInstanceData$ !== void 0 ? _currentInstanceData$ : currentInstanceData.mailhog,
73
67
  mediaRedirectDomain: currentInstanceData.mediaRedirectDomain,
74
68
  multisite: false,
75
69
  title: ''
@@ -77,8 +71,8 @@ cmd.argv(process.argv, async (arg, opt) => {
77
71
  const providedOptions = Object.keys(opt).filter(option => option.length > 1) // Filter out single letter aliases
78
72
  .filter(option => !['debug', 'help', 'slug'].includes(option)); // Filter out options that are not related to instance configuration
79
73
 
80
- const suppressPrompts = providedOptions.length > 0 || Object.keys(configurationFileOptions).length > 0;
81
- const instanceData = await (0, _devEnvironmentCli.promptForArguments)(preselectedOptions, defaultOptions, suppressPrompts);
74
+ const suppressPrompts = providedOptions.length > 0 || thereAreOptionsFromConfigFile;
75
+ const instanceData = await (0, _devEnvironmentCli.promptForArguments)(finalPreselectedOptions, defaultOptions, suppressPrompts);
82
76
  instanceData.siteSlug = slug;
83
77
  await (0, _devEnvironmentCore.updateEnvironment)(instanceData);
84
78
  const message = '\n' + _chalk.default.green('✓') + ' environment updated. Restart environment for changes to take an affect.';
@@ -18,4 +18,4 @@ var _command = _interopRequireDefault(require("../lib/cli/command"));
18
18
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
19
19
  (0, _command.default)({
20
20
  requiredArgs: 0
21
- }).command('create', 'Create a new local dev environment').command('update', 'Update an already created local dev environment').command('start', 'Start a local dev environment').command('stop', 'Stop a local dev environment').command('destroy', 'Remove containers, networks, volumes and configuration files of a local dev environment').command('info', 'Provides basic info about one or multiple local dev environments').command('list', 'Provides basic info about all local dev environments').command('exec', 'Execute a WP-CLI command in local dev environment').command('import', 'Import data into a local WordPress environment').command('shell', 'Spawns a shell in a dev environment').command('logs', 'View logs from a local WordPress environment').command('sync', 'Pull data from production to local development environment').argv(process.argv);
21
+ }).command('create', 'Create a new local dev environment').command('update', 'Update an already created local dev environment').command('start', 'Start a local dev environment').command('stop', 'Stop a local dev environment').command('destroy', 'Remove containers, networks, volumes and configuration files of a local dev environment').command('info', 'Provides basic info about one or multiple local dev environments').command('list', 'Provides basic info about all local dev environments').command('exec', 'Execute a WP-CLI command in local dev environment').command('import', 'Import data into a local WordPress environment').command('shell', 'Spawns a shell in a dev environment').command('logs', 'View logs from a local WordPress environment').argv(process.argv);
@@ -168,22 +168,27 @@ async function gates(app, env, fileName) {
168
168
  const examples = [
169
169
  // `sql` subcommand
170
170
  {
171
- usage: 'vip import sql @mysite.develop <file.sql>',
171
+ usage: 'vip import sql @mysite.develop file.sql',
172
172
  description: 'Import the given SQL file to your site'
173
173
  },
174
174
  // `search-replace` flag
175
175
  {
176
- usage: 'vip import sql @mysite.develop <file.sql> --search-replace="from,to"',
176
+ usage: 'vip import sql @mysite.develop file.sql --search-replace="https://from.example.com,https://to.example.com"',
177
177
  description: 'Perform a Search and Replace, then import the replaced file to your site.\n' + ' * Ensure there are no spaces between your search-replace parameters'
178
178
  },
179
+ // `search-replace` flag
180
+ {
181
+ usage: 'vip import sql @mysite.develop file.sql --search-replace="https://from.example.com,https://to.example.com" --search-replace="example.com/from,example.com/to"',
182
+ description: 'Perform multiple search and replace tasks, then import the updated file to your site.'
183
+ },
179
184
  // `in-place` flag
180
185
  {
181
- usage: 'vip import sql @mysite.develop <file.sql> --search-replace="from,to" --in-place',
182
- description: 'Search and Replace on the input <file.sql>, then import the replaced file to your site'
186
+ usage: 'vip import sql @mysite.develop file.sql --search-replace="https://from.example.com,https://to.example.com" --in-place',
187
+ description: 'Search and Replace on the input `file.sql`, then import the updated file to your site'
183
188
  },
184
189
  // `output` flag
185
190
  {
186
- usage: 'vip import sql @mysite.develop <file.sql> --search-replace="from,to" --output="<output.sql>"',
191
+ usage: 'vip import sql @mysite.develop file.sql --search-replace="https://from.example.com,https://to.example.com" --output="output.sql"',
187
192
  description: 'Output the performed Search and Replace to the specified output file, then import the replaced file to your site\n' + ' * Has no effect when the `in-place` flag is used'
188
193
  },
189
194
  // `sql status` subcommand
@@ -319,7 +324,7 @@ const displayPlaybook = ({
319
324
  envContext: true,
320
325
  requiredArgs: 1,
321
326
  module: 'import-sql'
322
- }).command('status', 'Check the status of the current running import').option('skip-validate', 'Do not perform pre-upload file validation. If unsupported entries are present, the import is likely to fail').option('search-replace', 'Perform Search and Replace on the specified SQL file').option('in-place', 'Search and Replace explicitly on the given input file').option('output', 'Specify the replacement output file for Search and Replace', 'process.stdout').examples(examples).argv(process.argv, async (arg, opts) => {
327
+ }).command('status', 'Check the status of the current running import').option('skip-validate', 'Do not perform pre-upload file validation. If unsupported entries are present, the import is likely to fail').option('search-replace', 'Search for a given URL in the SQL file and replace it with another. The format for the value is `<from-url>,<to-url>` (e.g. --search-replace="https://from.com,https://to.com"').option('in-place', 'Search and Replace explicitly on the given input file').option('output', 'Specify the replacement output file for Search and Replace', 'process.stdout').examples(examples).argv(process.argv, async (arg, opts) => {
323
328
  var _env$primaryDomain;
324
329
  const {
325
330
  app,
@@ -16,7 +16,6 @@ Object.defineProperty(exports, "__esModule", {
16
16
  exports.DevEnvSyncSQLCommand = void 0;
17
17
  var _fs = _interopRequireDefault(require("fs"));
18
18
  var _chalk = _interopRequireDefault(require("chalk"));
19
- var _url = _interopRequireDefault(require("url"));
20
19
  var _vipSearchReplace = require("@automattic/vip-search-replace");
21
20
  var _clientFileUploader = require("../lib/client-file-uploader");
22
21
  var _exportSql = require("./export-sql");
@@ -40,8 +39,8 @@ function findSiteHomeUrl(sql) {
40
39
  const regex = "'(siteurl|home)',\\s?'(.*?)'";
41
40
  const results = sql.match(regex);
42
41
  if (results) {
43
- const url = results[2];
44
- return _url.default.parse(url).hostname;
42
+ const domain = results[2].replace(/https?:\/\//, '');
43
+ return domain;
45
44
  }
46
45
  return null;
47
46
  }
@@ -56,15 +55,15 @@ function findSiteHomeUrl(sql) {
56
55
  async function extractSiteUrls(sqlFile) {
57
56
  const readInterface = await (0, _lineByLine.getReadInterface)(sqlFile);
58
57
  return new Promise((resolve, reject) => {
59
- const domains = new Set();
58
+ const domains = [];
60
59
  readInterface.on('line', line => {
61
60
  const domain = findSiteHomeUrl(line);
62
61
  if (domain) {
63
- domains.add('//' + domain);
62
+ domains.push(domain);
64
63
  }
65
64
  });
66
65
  readInterface.on('close', () => {
67
- resolve(Array.from(domains));
66
+ resolve(domains);
68
67
  });
69
68
  readInterface.on('error', reject);
70
69
  });
@@ -73,20 +72,18 @@ class DevEnvSyncSQLCommand {
73
72
  /**
74
73
  * Creates a new instance of the command
75
74
  *
76
- * @param {string} app The app object
77
- * @param {string} env The environment object
78
- * @param {string} slug The site slug
79
- * @param {Function} trackerFn Function to call for tracking
75
+ * @param {string} app The app object
76
+ * @param {string} env The environment object
77
+ * @param {string} slug The site slug
80
78
  */
81
- constructor(app, env, slug, trackerFn = () => {}) {
79
+ constructor(app, env, slug) {
82
80
  this.app = app;
83
81
  this.env = env;
84
82
  this.slug = slug;
85
- this.track = trackerFn;
86
83
  this.tmpDir = (0, _utils.makeTempDir)();
87
84
  }
88
85
  get landoDomain() {
89
- return `//${this.slug}.vipdev.lndo.site`;
86
+ return `${this.slug}.vipdev.lndo.site`;
90
87
  }
91
88
  get sqlFile() {
92
89
  return `${this.tmpDir}/sql-export.sql`;
@@ -102,7 +99,7 @@ class DevEnvSyncSQLCommand {
102
99
  * @return {Promise<void>} Promise that resolves when the export is complete
103
100
  */
104
101
  async generateExport() {
105
- const exportCommand = new _exportSql.ExportSQLCommand(this.app, this.env, this.gzFile, this.track);
102
+ const exportCommand = new _exportSql.ExportSQLCommand(this.app, this.env, this.gzFile);
106
103
  await exportCommand.run();
107
104
  }
108
105
 
@@ -117,13 +114,9 @@ class DevEnvSyncSQLCommand {
117
114
  const replacements = this.siteUrls.reduce((acc, url) => [...acc, url, this.landoDomain], []);
118
115
  const readStream = _fs.default.createReadStream(this.sqlFile);
119
116
  const replacedStream = await (0, _vipSearchReplace.replace)(readStream, replacements);
120
- const outputFile = `${this.tmpDir}/sql-export-sr.sql`;
121
- replacedStream.pipe(_fs.default.createWriteStream(outputFile));
117
+ replacedStream.pipe(_fs.default.createWriteStream(this.sqlFile));
122
118
  return new Promise((resolve, reject) => {
123
- replacedStream.on('finish', () => {
124
- _fs.default.renameSync(outputFile, this.sqlFile);
125
- resolve();
126
- });
119
+ replacedStream.on('finish', resolve);
127
120
  replacedStream.on('error', reject);
128
121
  });
129
122
  }
@@ -136,10 +129,11 @@ class DevEnvSyncSQLCommand {
136
129
  */
137
130
  async runImport() {
138
131
  const importOptions = {
132
+ slug: this.slug,
139
133
  inPlace: true,
140
134
  skipValidate: true
141
135
  };
142
- const importCommand = new _devEnvImportSql.DevEnvImportSQLCommand(this.sqlFile, importOptions, this.slug);
136
+ const importCommand = new _devEnvImportSql.DevEnvImportSQLCommand(this.sqlFile, importOptions);
143
137
  await importCommand.run(true);
144
138
  }
145
139
 
@@ -160,9 +154,6 @@ class DevEnvSyncSQLCommand {
160
154
  await (0, _clientFileUploader.unzipFile)(this.gzFile, this.sqlFile);
161
155
  console.log(`${_chalk.default.green('✓')} Extracted to ${this.sqlFile}`);
162
156
  } catch (err) {
163
- await this.track('archive_extraction_error', {
164
- errorMessage: err === null || err === void 0 ? void 0 : err.message
165
- });
166
157
  exit.withError(`Error extracting the SQL export: ${err === null || err === void 0 ? void 0 : err.message}`);
167
158
  }
168
159
  try {
@@ -179,9 +170,6 @@ class DevEnvSyncSQLCommand {
179
170
  await this.runSearchReplace();
180
171
  console.log(`${_chalk.default.green('✓')} Search-replace operation is complete`);
181
172
  } catch (err) {
182
- await this.track('search_replace_error', {
183
- errorMessage: err === null || err === void 0 ? void 0 : err.message
184
- });
185
173
  exit.withError(`Error replacing domains: ${err === null || err === void 0 ? void 0 : err.message}`);
186
174
  }
187
175
  try {
@@ -189,9 +177,6 @@ class DevEnvSyncSQLCommand {
189
177
  await this.runImport();
190
178
  console.log(`${_chalk.default.green('✓')} SQL file imported`);
191
179
  } catch (err) {
192
- await this.track('import_error', {
193
- errorMessage: err === null || err === void 0 ? void 0 : err.message
194
- });
195
180
  exit.withError(`Error importing SQL file: ${err === null || err === void 0 ? void 0 : err.message}`);
196
181
  }
197
182
  }
@@ -8,7 +8,7 @@ var _graphqlTag = _interopRequireDefault(require("graphql-tag"));
8
8
  var _fs = _interopRequireDefault(require("fs"));
9
9
  var _https = _interopRequireDefault(require("https"));
10
10
  var _path = _interopRequireDefault(require("path"));
11
- var _api = _interopRequireWildcard(require("../lib/api"));
11
+ var _api = _interopRequireDefault(require("../lib/api"));
12
12
  var _format = require("../lib/cli/format");
13
13
  var _progress = require("../lib/cli/progress");
14
14
  var exit = _interopRequireWildcard(require("../lib/cli/exit"));
@@ -162,10 +162,8 @@ async function generateDownloadLink(appId, envId, backupId) {
162
162
  * @throws {Error} Throws an error if the job creation fails
163
163
  */
164
164
  async function createExportJob(appId, envId, backupId) {
165
- // Disable global error handling so that we can handle errors ourselves
166
- (0, _api.disableGlobalGraphQLErrorHandling)();
167
165
  const api = await (0, _api.default)();
168
- await api.mutate({
166
+ const response = await api.mutate({
169
167
  mutation: CREATE_EXPORT_JOB_MUTATION,
170
168
  variables: {
171
169
  input: {
@@ -175,9 +173,16 @@ async function createExportJob(appId, envId, backupId) {
175
173
  }
176
174
  }
177
175
  });
178
-
179
- // Re-enable global error handling
180
- (0, _api.enableGlobalGraphQLErrorHandling)();
176
+ const {
177
+ data: {
178
+ startDBBackupCopy: {
179
+ success
180
+ }
181
+ }
182
+ } = response;
183
+ if (!success) {
184
+ throw new Error();
185
+ }
181
186
  }
182
187
 
183
188
  /**
@@ -190,15 +195,15 @@ class ExportSQLCommand {
190
195
  DOWNLOAD_LINK: 'downloadLink',
191
196
  DOWNLOAD: 'download'
192
197
  };
198
+
193
199
  /**
194
200
  * Creates an instance of SQLExportCommand
195
201
  *
196
- * @param {any} app The application object
197
- * @param {any} env The environment object
198
- * @param {string} outputFile The output file path
199
- * @param {Function} trackerFn The progress tracker function
202
+ * @param {any} app The application object
203
+ * @param {any} env The environment object
204
+ * @param {string} outputFile The output file path
200
205
  */
201
- constructor(app, env, outputFile, trackerFn = () => {}) {
206
+ constructor(app, env, outputFile) {
202
207
  this.app = app;
203
208
  this.env = env;
204
209
  this.outputFile = outputFile;
@@ -215,7 +220,6 @@ class ExportSQLCommand {
215
220
  id: this.steps.DOWNLOAD,
216
221
  name: 'Downloading file'
217
222
  }]);
218
- this.track = trackerFn;
219
223
  }
220
224
 
221
225
  /**
@@ -315,29 +319,21 @@ class ExportSQLCommand {
315
319
  latestBackup
316
320
  } = await fetchLatestBackupAndJobStatus(this.app.id, this.env.id);
317
321
  if (!latestBackup) {
318
- await this.track('no_backup_found_error', {
319
- errorMessage: 'No backup found for the site'
320
- });
321
322
  exit.withError(`No backup found for site ${this.app.name}`);
322
323
  } else {
323
324
  console.log(`${(0, _format.getGlyphForStatus)('success')} Latest backup found with timestamp ${latestBackup.createdAt}`);
324
325
  }
325
- if (await this.getExportJob()) {
326
- console.log(`Attaching to an existing export for the backup with timestamp ${latestBackup.createdAt}`);
327
- } else {
326
+
327
+ // See if there is an existing export job for the latest backup
328
+ if (!(await this.getExportJob())) {
328
329
  console.log(`Creating a new export for the backup with timestamp ${latestBackup.createdAt}`);
329
330
  try {
330
331
  await createExportJob(this.app.id, this.env.id, latestBackup.id);
331
332
  } catch (err) {
332
- // Todo: match error code instead of message substring
333
- if (err !== null && err !== void 0 && err.message.includes('Backup Copy already in progress')) {
334
- await this.track('export_job_already_running_error', {
335
- errorMessage: err === null || err === void 0 ? void 0 : err.message
336
- });
337
- exit.withError('There is an export job already running for this site: ' + `https://dashboard.wpvip.com/apps/${this.app.id}/${this.env.uniqueLabel}/data/database/backups\n` + 'Currently, we allow only one export job per site. Please try again later.');
338
- }
339
333
  exit.withError(`Error creating export job: ${err === null || err === void 0 ? void 0 : err.message}`);
340
334
  }
335
+ } else {
336
+ console.log(`Attaching to an existing export for the backup with timestamp ${latestBackup.createdAt}`);
341
337
  }
342
338
  this.progressTracker.stepRunning(this.steps.PREPARE);
343
339
  this.progressTracker.startPrinting();
@@ -357,9 +353,6 @@ class ExportSQLCommand {
357
353
  } catch (err) {
358
354
  this.progressTracker.stepFailed(this.steps.DOWNLOAD);
359
355
  this.stopProgressTracker();
360
- await this.track('download_failed_error', {
361
- errorMessage: err === null || err === void 0 ? void 0 : err.message
362
- });
363
356
  exit.withError(`Error downloading exported file: ${err === null || err === void 0 ? void 0 : err.message}`);
364
357
  }
365
358
  }
@@ -11,6 +11,7 @@ exports.getEnvironmentStartCommand = getEnvironmentStartCommand;
11
11
  exports.getOptionsFromAppInfo = getOptionsFromAppInfo;
12
12
  exports.getTagChoices = getTagChoices;
13
13
  exports.handleCLIException = handleCLIException;
14
+ exports.handleDeprecatedOptions = handleDeprecatedOptions;
14
15
  exports.printTable = printTable;
15
16
  exports.processBooleanOption = processBooleanOption;
16
17
  exports.processComponentOptionInput = processComponentOptionInput;
@@ -77,6 +78,8 @@ const componentDemoNames = {
77
78
 
78
79
  // Forward declaration to avoid no-use-before-define
79
80
 
81
+ // eslint-disable-next-line no-redeclare
82
+
80
83
  async function handleCLIException(exception, trackKey, trackBaseInfo = {}) {
81
84
  const errorPrefix = _chalk.default.red('Error:');
82
85
  if (_devEnvironment.DEV_ENVIRONMENT_NOT_FOUND === exception.message) {
@@ -299,12 +302,12 @@ async function promptForArguments(preselectedOptions, defaultOptions, suppressPr
299
302
  xdebug: false,
300
303
  xdebugConfig: preselectedOptions.xdebugConfig,
301
304
  siteSlug: '',
302
- mailhog: false
305
+ mailpit: false
303
306
  };
304
307
  const promptLabels = {
305
308
  xdebug: 'XDebug',
306
309
  phpmyadmin: 'phpMyAdmin',
307
- mailhog: 'MailHog'
310
+ mailpit: 'Mailpit'
308
311
  };
309
312
  if (!instanceData.mediaRedirectDomain && defaultOptions.mediaRedirectDomain) {
310
313
  const mediaRedirectPromptText = `Would you like to redirect to ${defaultOptions.mediaRedirectDomain} for missing media files?`;
@@ -332,7 +335,7 @@ async function promptForArguments(preselectedOptions, defaultOptions, suppressPr
332
335
  } else {
333
336
  instanceData.elasticsearch = await promptForBoolean('Enable Elasticsearch (needed by Enterprise Search)?', !!defaultOptions.elasticsearch);
334
337
  }
335
- for (const service of ['phpmyadmin', 'xdebug', 'mailhog']) {
338
+ for (const service of ['phpmyadmin', 'xdebug', 'mailpit']) {
336
339
  if (service in instanceData) {
337
340
  if (service in preselectedOptions) {
338
341
  instanceData[service] = preselectedOptions[service];
@@ -569,7 +572,7 @@ function processVersionOption(value) {
569
572
  }
570
573
  function addDevEnvConfigurationOptions(command) {
571
574
  // We leave the third parameter to undefined on some because the defaults are handled in preProcessInstanceData()
572
- return command.option('wordpress', 'Use a specific WordPress version', undefined, processVersionOption).option(['u', 'mu-plugins'], 'Use a specific mu-plugins changeset or local directory').option('app-code', 'Use the application code from a local directory or use "demo" for VIP skeleton code').option('phpmyadmin', 'Enable PHPMyAdmin component. By default it is disabled', undefined, processBooleanOption).option('xdebug', 'Enable XDebug. By default it is disabled', undefined, processBooleanOption).option('xdebug_config', 'Extra configuration to pass to xdebug via XDEBUG_CONFIG environment variable').option('elasticsearch', 'Enable Elasticsearch (needed by Enterprise Search)', undefined, processBooleanOption).option(['r', 'media-redirect-domain'], 'Domain to redirect for missing media files. This can be used to still have images without the need to import them locally.').option('php', 'Explicitly choose PHP version to use', undefined, processVersionOption).option(['A', 'mailhog'], 'Enable MailHog. By default it is disabled', undefined, processBooleanOption);
575
+ 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);
573
576
  }
574
577
 
575
578
  /**
@@ -634,4 +637,13 @@ function getEnvTrackingInfo(slug) {
634
637
  slug
635
638
  };
636
639
  }
640
+ }
641
+ function handleDeprecatedOptions(opts) {
642
+ if (opts.mailhog) {
643
+ console.warn(_chalk.default.yellow('Warning: --mailhog is deprecated and will be removed in a future release. Please use --mailpit instead.'));
644
+ if (opts.mailpit === undefined) {
645
+ opts.mailpit = opts.mailhog;
646
+ }
647
+ delete opts.mailhog;
648
+ }
637
649
  }
@@ -63,6 +63,7 @@ async function getConfigurationFileOptions() {
63
63
  return configuration;
64
64
  }
65
65
  async function sanitizeConfiguration(configuration) {
66
+ var _configuration$mailpi;
66
67
  const genericConfigurationError = `Configuration file ${_chalk.default.grey(CONFIGURATION_FILE_NAME)} is available but ` + `couldn't be loaded. Ensure there is a ${_chalk.default.cyan('configuration-version')} and ${_chalk.default.cyan('slug')} ` + `configured. For example:\n\n${_chalk.default.grey(getConfigurationFileExample())}`;
67
68
  if (Array.isArray(configuration) || typeof configuration !== 'object') {
68
69
  throw new Error(genericConfigurationError);
@@ -91,7 +92,7 @@ async function sanitizeConfiguration(configuration) {
91
92
  elasticsearch: stringToBooleanIfDefined(configuration.elasticsearch),
92
93
  phpmyadmin: stringToBooleanIfDefined(configuration.phpmyadmin),
93
94
  xdebug: stringToBooleanIfDefined(configuration.xdebug),
94
- mailhog: stringToBooleanIfDefined(configuration.mailhog)
95
+ mailpit: stringToBooleanIfDefined((_configuration$mailpi = configuration.mailpit) !== null && _configuration$mailpi !== void 0 ? _configuration$mailpi : configuration.mailhog)
95
96
  };
96
97
 
97
98
  // Remove undefined values
@@ -99,6 +100,7 @@ async function sanitizeConfiguration(configuration) {
99
100
  return sanitizedConfiguration;
100
101
  }
101
102
  function mergeConfigurationFileOptions(preselectedOptions, configurationFileOptions) {
103
+ var _configurationFileOpt;
102
104
  // configurationFileOptions holds different parameters than present in
103
105
  // preselectedOptions like "slug", and friendly-named parameters (e.g.
104
106
  // 'app-code' vs 'appCode'). Selectively merge configurationFileOptions
@@ -113,7 +115,8 @@ function mergeConfigurationFileOptions(preselectedOptions, configurationFileOpti
113
115
  elasticsearch: configurationFileOptions.elasticsearch,
114
116
  phpmyadmin: configurationFileOptions.phpmyadmin,
115
117
  xdebug: configurationFileOptions.xdebug,
116
- mailhog: configurationFileOptions.mailhog
118
+ xdebugConfig: configurationFileOptions['xdebug-config'],
119
+ mailpit: (_configurationFileOpt = configurationFileOptions.mailpit) !== null && _configurationFileOpt !== void 0 ? _configurationFileOpt : configurationFileOptions.mailhog
117
120
  };
118
121
  const mergedOptions = {};
119
122
  Object.keys(configurationFileInstanceOptions).forEach(key => {
@@ -144,9 +144,10 @@ function preProcessInstanceData(instanceData) {
144
144
  newInstanceData.phpmyadmin = false;
145
145
  }
146
146
 
147
- // Mailhog migration
148
- if (!newInstanceData.mailhog) {
149
- newInstanceData.mailhog = false;
147
+ // Mailpit migration
148
+ if (!newInstanceData.mailpit) {
149
+ var _newInstanceData$mail;
150
+ newInstanceData.mailpit = (_newInstanceData$mail = newInstanceData.mailhog) !== null && _newInstanceData$mail !== void 0 ? _newInstanceData$mail : false;
150
151
  }
151
152
 
152
153
  // MariaDB migration
@@ -206,7 +207,7 @@ async function showLogs(lando, slug, options = {}) {
206
207
  const instancePath = getEnvironmentPath(slug);
207
208
  debug('Instance path for', slug, 'is:', instancePath);
208
209
  if (options.service) {
209
- const appInfo = await (0, _devEnvironmentLando.landoInfo)(lando, instancePath);
210
+ const appInfo = await (0, _devEnvironmentLando.landoInfo)(lando, instancePath, false);
210
211
  if (!appInfo.services.includes(options.service)) {
211
212
  throw new _userError.default(`Service '${options.service}' not found. Please choose from one: ${appInfo.services}`);
212
213
  }
@@ -277,6 +278,10 @@ function readEnvironmentData(slug) {
277
278
  // clientCode was renamed to appCode
278
279
  instanceData.appCode = instanceData.clientCode;
279
280
  }
281
+ if (instanceData.mailhog) {
282
+ instanceData.mailpit = instanceData.mailhog;
283
+ delete instanceData.mailhog;
284
+ }
280
285
  return instanceData;
281
286
  }
282
287
 
@@ -468,7 +473,9 @@ async function updateWordPressImage(slug) {
468
473
  if (!versions.length) {
469
474
  return false;
470
475
  }
471
- let message, envData, currentWordPressTag;
476
+ let message;
477
+ let envData;
478
+ let currentWordPressTag;
472
479
 
473
480
  // Get the current environment configuration
474
481
  try {