@automattic/vip 2.13.0 → 2.15.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/README.md CHANGED
@@ -14,7 +14,7 @@ Then, launch the command and follow the prompts:
14
14
  vip
15
15
  ```
16
16
 
17
- If you need more information, check out our [VIP CLI documentation](https://docs.wpvip.com/technical-references/vip-cli/).
17
+ If you need more information, check out our [VIP-CLI documentation](https://docs.wpvip.com/technical-references/vip-cli/).
18
18
 
19
19
  ## Contributing
20
20
 
@@ -26,6 +26,28 @@ By default, we record information about the usage of this tool using an in-house
26
26
 
27
27
  ## Changelog
28
28
 
29
+ ### 2.15.0 (3 Aug 2022)
30
+
31
+ - #1067 FORNO-1244: SQL Import: Increase max import size for launched sites to 1GB
32
+ - #1064 FORNO-1254: SQL Import: Add multisite primary domain validation
33
+ - #1062 [dev-env] Change wizard wording
34
+ - #1065 [dev-env] Update default software versions for PHP and Elasticsearch
35
+ - #1063 [dev-env] Remove redundant healthchecks
36
+ - #1061 [dev-env] Fix duplicate shortcuts
37
+
38
+ ### 2.14.0 (19 Jul 2022)
39
+
40
+ - #1059 Update engines to show support for npm > 6
41
+ - #1058 [dev-env] switch dev-env error during import to warning
42
+ - #1057 [dev-env] Update debug instruction example
43
+ - #1055 [dev-env] Makes exec attempt to run the task even if env seems to be down
44
+
45
+ ### 2.13.1 (20 Jun 2022)
46
+
47
+ - #1052 [dev-env] Update/lando compose version
48
+ - #1045 README: Fix Typo `VIP CLI` => `VIP-CLI`
49
+ - #1048 Switch to GitHub Actions
50
+
29
51
  ### 2.13.0 (16 Jun 2022)
30
52
 
31
53
  - #1046 Add Cache Purge Command
@@ -223,7 +245,7 @@ Fixes:
223
245
  - #844 Expose DB and expose extra services in info table
224
246
  - #865 spawn WP-CLI as root to allow for FS operations
225
247
  - #895 Fix rmdir deprecation warning
226
- - #870 Add the VIP CLI release process and release schedule
248
+ - #870 Add the VIP-CLI release process and release schedule
227
249
 
228
250
  Dependencies updates:
229
251
 
@@ -68,8 +68,6 @@ services:
68
68
  command: docker-entrypoint.sh mysqld
69
69
  ports:
70
70
  - ":3306"
71
- healthcheck:
72
- test: 'mysql -uroot --silent --execute "SHOW DATABASES;"'
73
71
  environment:
74
72
  MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: 'true'
75
73
  volumes:
@@ -106,8 +104,6 @@ services:
106
104
  discovery.type: 'single-node'
107
105
  ports:
108
106
  - ":9200"
109
- healthcheck:
110
- test: "curl --noproxy '*' -XGET localhost:9200"
111
107
  volumes:
112
108
  - search_data:/usr/share/elasticsearch/data
113
109
  volumes:
@@ -38,7 +38,11 @@ const examples = [{
38
38
  }];
39
39
  (0, _command.default)({
40
40
  wildcardCommand: true
41
- }).option('slug', 'Custom name of the dev environment').examples(examples).argv(process.argv, async (unmatchedArgs, opt) => {
41
+ }).option('slug', 'Custom name of the dev environment').option('force', 'Disabling validations before task execution', undefined, value => {
42
+ var _value$toLowerCase;
43
+
44
+ return 'false' !== (value === null || value === void 0 ? void 0 : (_value$toLowerCase = value.toLowerCase) === null || _value$toLowerCase === void 0 ? void 0 : _value$toLowerCase.call(value));
45
+ }).examples(examples).argv(process.argv, async (unmatchedArgs, opt) => {
42
46
  await (0, _devEnvironmentCli.validateDependencies)();
43
47
  const slug = (0, _devEnvironmentCli.getEnvironmentName)(opt);
44
48
  const trackingInfo = (0, _devEnvironmentCli.getEnvTrackingInfo)(slug);
@@ -59,7 +63,10 @@ const examples = [{
59
63
  arg = process.argv.slice(argSplitterIx + 1);
60
64
  }
61
65
 
62
- await (0, _devEnvironmentCore.exec)(slug, arg);
66
+ const options = {
67
+ force: opt.force
68
+ };
69
+ await (0, _devEnvironmentCore.exec)(slug, arg, options);
63
70
  await (0, _tracker.trackEvent)('dev_env_exec_command_success', trackingInfo);
64
71
  } catch (error) {
65
72
  (0, _devEnvironmentCli.handleCLIException)(error, 'dev_env_exec_command_error', trackingInfo);
@@ -44,7 +44,7 @@ const examples = [{
44
44
  }];
45
45
  (0, _command.default)({
46
46
  requiredArgs: 1
47
- }).option('slug', 'Custom name of the dev environment').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('skip-validate', 'Do not perform file validation.').examples(examples).argv(process.argv, async (unmatchedArgs, opt) => {
47
+ }).option('slug', 'Custom name of the dev environment').option(['r', 'search-replace'], 'Perform Search and Replace on the specified SQL file').option('in-place', 'Search and Replace explicitly on the given input file').option('skip-validate', 'Do not perform file validation.').examples(examples).argv(process.argv, async (unmatchedArgs, opt) => {
48
48
  await (0, _devEnvironmentCli.validateDependencies)();
49
49
  const [fileName] = unmatchedArgs;
50
50
  const {
@@ -249,7 +249,8 @@ async function validateAndGetTableNames({
249
249
  skipValidate,
250
250
  appId,
251
251
  envId,
252
- fileNameToUpload
252
+ fileNameToUpload,
253
+ searchReplace
253
254
  }) {
254
255
  const validations = [_sql.staticSqlValidations, _siteType.siteTypeValidations];
255
256
 
@@ -259,10 +260,10 @@ async function validateAndGetTableNames({
259
260
  }
260
261
 
261
262
  try {
262
- await (0, _lineByLine.fileLineValidations)(appId, envId, fileNameToUpload, validations);
263
+ await (0, _lineByLine.fileLineValidations)(appId, envId, fileNameToUpload, validations, searchReplace);
263
264
  } catch (validateErr) {
264
265
  console.log('');
265
- exit.withError(`${validateErr.message}
266
+ exit.withError(`${validateErr.message}\n
266
267
  If you are confident the file does not contain unsupported statements, you can retry the command with the ${_chalk.default.yellow('--skip-validate')} option.
267
268
  `);
268
269
  } // this can only be called after static validation of the SQL file
@@ -407,7 +408,8 @@ const displayPlaybook = ({
407
408
  skipValidate,
408
409
  appId,
409
410
  envId,
410
- fileNameToUpload
411
+ fileNameToUpload,
412
+ searchReplace
411
413
  }); // display playbook of what will happen during execution
412
414
 
413
415
  displayPlaybook({
@@ -11,9 +11,9 @@ exports.DEV_ENVIRONMENT_FULL_COMMAND = DEV_ENVIRONMENT_FULL_COMMAND;
11
11
  const DEV_ENVIRONMENT_DEFAULTS = {
12
12
  title: 'VIP Dev',
13
13
  multisite: false,
14
- elasticsearchVersion: '7.10.1',
14
+ elasticsearchVersion: '7.17.2',
15
15
  mariadbVersion: '10.3',
16
- phpImage: 'default'
16
+ phpVersion: '8.0'
17
17
  };
18
18
  exports.DEV_ENVIRONMENT_DEFAULTS = DEV_ENVIRONMENT_DEFAULTS;
19
19
  const DEV_ENVIRONMENT_PROMPT_INTRO = 'This is a wizard to help you set up your local dev environment.\n\n' + 'Sensible default values were pre-selected for convenience. ' + 'You may also choose to create multiple environments with different settings using the --slug option.\n\n';
@@ -32,11 +32,10 @@ const DEV_ENVIRONMENT_WORDPRESS_VERSION_TTL = 86400; // once per day
32
32
 
33
33
  exports.DEV_ENVIRONMENT_WORDPRESS_VERSION_TTL = DEV_ENVIRONMENT_WORDPRESS_VERSION_TTL;
34
34
  const DEV_ENVIRONMENT_PHP_VERSIONS = {
35
- default: 'ghcr.io/automattic/vip-container-images/php-fpm:7.4',
36
- // eslint-disable-next-line quote-props -- flow does nit support non-string keys
37
- '7.4': 'ghcr.io/automattic/vip-container-images/php-fpm-alt:7.4',
38
- '8.0': 'ghcr.io/automattic/vip-container-images/php-fpm-alt:8.0',
39
35
  // eslint-disable-next-line quote-props
40
- '8.1': 'ghcr.io/automattic/vip-container-images/php-fpm-alt:8.1'
36
+ '8.1': 'ghcr.io/automattic/vip-container-images/php-fpm-alt:8.1',
37
+ '8.0': 'ghcr.io/automattic/vip-container-images/php-fpm-alt:8.0',
38
+ // eslint-disable-next-line quote-props -- flow does nit support non-string keys
39
+ '7.4': 'ghcr.io/automattic/vip-container-images/php-fpm-alt:7.4'
41
40
  };
42
41
  exports.DEV_ENVIRONMENT_PHP_VERSIONS = DEV_ENVIRONMENT_PHP_VERSIONS;
@@ -91,9 +91,9 @@ async function handleCLIException(exception, trackKey, trackBaseInfo = {}) {
91
91
  }
92
92
 
93
93
  if (!process.env.DEBUG) {
94
- console.log(`Please re-run the command with "--debug ${_chalk.default.bold('@automattic/vip:bin:dev-environment')}" appended to it and provide the stack trace on the support ticket.`);
94
+ console.log(`\nPlease re-run the command with "--debug ${_chalk.default.bold('@automattic/vip:bin:dev-environment')}" appended to it and provide the stack trace on the support ticket.`);
95
95
  console.log(_chalk.default.bold('\nExample:\n'));
96
- console.log('vip dev-env create --debug @automattic/vip:bin:dev-environment \n');
96
+ console.log('vip dev-env <command> <arguments> --debug @automattic/vip:bin:dev-environment \n');
97
97
  }
98
98
 
99
99
  debug(exception);
@@ -248,7 +248,7 @@ async function promptForArguments(preselectedOptions, defaultOptions) {
248
248
  instanceData[component] = result;
249
249
  }
250
250
 
251
- instanceData.enterpriseSearchEnabled = await promptForBoolean('Enable Enterprise Search?', defaultOptions.enterpriseSearchEnabled);
251
+ instanceData.enterpriseSearchEnabled = await promptForBoolean('Enable Elasticsearch (needed by Enterprise Search)?', defaultOptions.enterpriseSearchEnabled);
252
252
 
253
253
  if (instanceData.enterpriseSearchEnabled) {
254
254
  instanceData.statsd = preselectedOptions.statsd || defaultOptions.statsd || false;
@@ -213,8 +213,8 @@ async function printEnvironmentInfo(slug) {
213
213
  (0, _devEnvironmentCli.printTable)(appInfo);
214
214
  }
215
215
 
216
- async function exec(slug, args) {
217
- debug('Will run a wp command on env', slug, 'with args', args);
216
+ async function exec(slug, args, options = {}) {
217
+ debug('Will run a wp command on env', slug, 'with args', args, ' and options', options);
218
218
  const instancePath = getEnvironmentPath(slug);
219
219
  debug('Instance path for', slug, 'is:', instancePath);
220
220
 
@@ -231,7 +231,7 @@ async function exec(slug, args) {
231
231
  commandArgs = [...args.map(argument => argument.replace('--new-site-', '--'))];
232
232
  }
233
233
 
234
- await (0, _devEnvironmentLando.landoExec)(instancePath, command, commandArgs);
234
+ await (0, _devEnvironmentLando.landoExec)(instancePath, command, commandArgs, options);
235
235
  }
236
236
 
237
237
  function doesEnvironmentExist(slug) {
@@ -140,7 +140,7 @@ async function healthcheckHook(app, lando) {
140
140
  services: [container.service]
141
141
  }
142
142
  });
143
- } catch (e) {
143
+ } catch (exception) {
144
144
  debug(`${container.service} Health check failed`);
145
145
  notHealthyContainers.push(container);
146
146
  }
@@ -272,15 +272,18 @@ async function isEnvUp(app) {
272
272
  return (scanResult === null || scanResult === void 0 ? void 0 : scanResult.length) && scanResult.filter(result => result.status).length === scanResult.length;
273
273
  }
274
274
 
275
- async function landoExec(instancePath, toolName, args) {
275
+ async function landoExec(instancePath, toolName, args, options) {
276
276
  const lando = new _lando.default(getLandoConfig());
277
277
  await lando.bootstrap();
278
278
  const app = lando.getApp(instancePath);
279
279
  await app.init();
280
- const isUp = await isEnvUp(app);
281
280
 
282
- if (!isUp) {
283
- throw new Error('environment needs to be started before running wp command');
281
+ if (!options.force) {
282
+ const isUp = await isEnvUp(app);
283
+
284
+ if (!isUp) {
285
+ throw new Error('environment needs to be started before running wp command');
286
+ }
284
287
  }
285
288
 
286
289
  const tool = app.config.tooling[toolName];
@@ -18,7 +18,7 @@ var _fileSize = require("../constants/file-size");
18
18
  */
19
19
  const SQL_IMPORT_FILE_SIZE_LIMIT = 100 * _fileSize.GB_IN_BYTES;
20
20
  exports.SQL_IMPORT_FILE_SIZE_LIMIT = SQL_IMPORT_FILE_SIZE_LIMIT;
21
- const SQL_IMPORT_FILE_SIZE_LIMIT_LAUNCHED = 350 * _fileSize.MB_IN_BYTES;
21
+ const SQL_IMPORT_FILE_SIZE_LIMIT_LAUNCHED = 1 * _fileSize.GB_IN_BYTES;
22
22
  exports.SQL_IMPORT_FILE_SIZE_LIMIT_LAUNCHED = SQL_IMPORT_FILE_SIZE_LIMIT_LAUNCHED;
23
23
 
24
24
  function currentUserCanImportForApp(app) {
@@ -0,0 +1,161 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.isMultisitePrimaryDomainMapped = isMultisitePrimaryDomainMapped;
7
+ exports.getPrimaryDomain = exports.maybeSearchReplacePrimaryDomain = exports.getPrimaryDomainFromSQL = void 0;
8
+
9
+ var _graphqlTag = _interopRequireDefault(require("graphql-tag"));
10
+
11
+ var _api = _interopRequireDefault(require("../api"));
12
+
13
+ var _tracker = require("../tracker");
14
+
15
+ var exit = _interopRequireWildcard(require("../cli/exit"));
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); }
18
+
19
+ 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; }
20
+
21
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
22
+
23
+ /**
24
+ *
25
+ * @format
26
+ */
27
+
28
+ /**
29
+ * External dependencies
30
+ */
31
+
32
+ /**
33
+ * Internal dependencies
34
+ */
35
+
36
+ /**
37
+ * Extracts the domain for site with ID 1 from an INSERT INTO `wp_site` SQL statement
38
+ *
39
+ * @param {array} statements An array of SQL statements
40
+ * @returns {string} The domain
41
+ */
42
+ const getPrimaryDomainFromSQL = statements => {
43
+ var _statements$;
44
+
45
+ if (!statements.length) {
46
+ return '';
47
+ }
48
+
49
+ const SQL_WP_SITE_DOMAINS_REGEX = /\(1,'(.*?)'/s;
50
+ const matches = (_statements$ = statements[0]) === null || _statements$ === void 0 ? void 0 : _statements$.join('').replace(/\s/g, '').match(SQL_WP_SITE_DOMAINS_REGEX);
51
+ return matches ? matches[1] : '';
52
+ };
53
+ /**
54
+ * Apply search-replacements to a domain
55
+ *
56
+ * @param {string} domain The domain to apply replacements to
57
+ * @param {(string|array)} searchReplace The search-replace pairs
58
+ * @returns {string} The processed domain
59
+ */
60
+
61
+
62
+ exports.getPrimaryDomainFromSQL = getPrimaryDomainFromSQL;
63
+
64
+ const maybeSearchReplacePrimaryDomain = function (domain, searchReplace) {
65
+ if (searchReplace) {
66
+ var _primaryDomainReplace;
67
+
68
+ let pairs = searchReplace;
69
+
70
+ if (!Array.isArray(pairs)) {
71
+ pairs = [searchReplace];
72
+ }
73
+
74
+ const domainReplacements = pairs.map(pair => pair.split(','));
75
+ const primaryDomainReplacement = domainReplacements.find(pair => pair[0] === domain);
76
+ return (_primaryDomainReplace = primaryDomainReplacement === null || primaryDomainReplacement === void 0 ? void 0 : primaryDomainReplacement[1]) !== null && _primaryDomainReplace !== void 0 ? _primaryDomainReplace : domain;
77
+ }
78
+
79
+ return domain;
80
+ };
81
+ /**
82
+ * Get the primary domain as it will be imported
83
+ *
84
+ * @param {array} statements An array of SQL statements
85
+ * @param {(string|array)} searchReplace The search-replace pairs
86
+ * @returns {string} The replaced domain, or the domain as found in the SQL dump
87
+ */
88
+
89
+
90
+ exports.maybeSearchReplacePrimaryDomain = maybeSearchReplacePrimaryDomain;
91
+
92
+ const getPrimaryDomain = function (statements, searchReplace) {
93
+ const domainFromSQL = getPrimaryDomainFromSQL(statements);
94
+ return maybeSearchReplacePrimaryDomain(domainFromSQL, searchReplace);
95
+ };
96
+ /**
97
+ * Gets the mapped domains and checks if the primary domain from the provided SQL dump is one of them
98
+ *
99
+ * @param {number} appId The ID of the app in GOOP
100
+ * @param {number} envId The ID of the enviroment in GOOP
101
+ * @param {string} primaryDomain The primary domain found in the provided SQL file
102
+ * @returns {boolean} Whether the primary domain is mapped
103
+ */
104
+
105
+
106
+ exports.getPrimaryDomain = getPrimaryDomain;
107
+
108
+ async function isMultisitePrimaryDomainMapped(appId, envId, primaryDomain) {
109
+ var _res, _res$data, _res$data$app, _environments$, _environments$$domain, _environments$$domain2;
110
+
111
+ const track = _tracker.trackEventWithEnv.bind(null, appId, envId);
112
+
113
+ const api = await (0, _api.default)();
114
+ let res;
115
+
116
+ try {
117
+ res = await api.query({
118
+ query: (0, _graphqlTag.default)`
119
+ query AppMappedDomains($appId: Int, $envId: Int) {
120
+ app(id: $appId) {
121
+ id
122
+ name
123
+ environments(id: $envId) {
124
+ uniqueLabel
125
+ isMultisite
126
+ domains {
127
+ nodes {
128
+ name
129
+ isPrimary
130
+ }
131
+ }
132
+ }
133
+ }
134
+ }
135
+ `,
136
+ variables: {
137
+ appId,
138
+ envId
139
+ }
140
+ });
141
+ } catch (GraphQlError) {
142
+ await track('import_sql_command_error', {
143
+ error_type: 'GraphQL-MappedDomain-Check-failed',
144
+ gql_err: GraphQlError
145
+ });
146
+ exit.withError(`StartImport call failed: ${GraphQlError}`);
147
+ }
148
+
149
+ if (!Array.isArray((_res = res) === null || _res === void 0 ? void 0 : (_res$data = _res.data) === null || _res$data === void 0 ? void 0 : (_res$data$app = _res$data.app) === null || _res$data$app === void 0 ? void 0 : _res$data$app.environments)) {
150
+ return false;
151
+ }
152
+
153
+ const environments = res.data.app.environments;
154
+
155
+ if (!environments.length) {
156
+ return false;
157
+ }
158
+
159
+ const mappedDomains = (_environments$ = environments[0]) === null || _environments$ === void 0 ? void 0 : (_environments$$domain = _environments$.domains) === null || _environments$$domain === void 0 ? void 0 : (_environments$$domain2 = _environments$$domain.nodes) === null || _environments$$domain2 === void 0 ? void 0 : _environments$$domain2.map(domain => domain.name);
160
+ return mappedDomains.includes(primaryDomain);
161
+ }
@@ -64,7 +64,7 @@ async function getReadInterface(filename) {
64
64
  });
65
65
  }
66
66
 
67
- async function fileLineValidations(appId, envId, fileName, validations) {
67
+ async function fileLineValidations(appId, envId, fileName, validations, searchReplace) {
68
68
  const isImport = true;
69
69
  const readInterface = await getReadInterface(fileName);
70
70
  debug('Validations: ', validations);
@@ -85,7 +85,8 @@ async function fileLineValidations(appId, envId, fileName, validations) {
85
85
  fileName,
86
86
  isImport,
87
87
  appId,
88
- envId
88
+ envId,
89
+ searchReplace
89
90
  });
90
91
  }
91
92
  }));
@@ -13,6 +13,10 @@ var _isMultiSiteSqlDump = require("./is-multi-site-sql-dump");
13
13
 
14
14
  var _isMultiSite = require("./is-multi-site");
15
15
 
16
+ var _isMultisiteDomainMapped = require("./is-multisite-domain-mapped");
17
+
18
+ var _utils = require("./utils");
19
+
16
20
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
17
21
 
18
22
  /**
@@ -29,9 +33,12 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
29
33
  */
30
34
  const debug = (0, _debug.default)('vip:vip-import-sql');
31
35
  let isMultiSiteSqlDump = false;
36
+ let wpSiteInsertStatement;
37
+ const getWpSiteInsertStatement = (0, _utils.getMultilineStatement)(/INSERT INTO `wp_site`/s);
32
38
  const siteTypeValidations = {
33
39
  execute: line => {
34
40
  const lineIsMultiSite = (0, _isMultiSiteSqlDump.sqlDumpLineIsMultiSite)(line);
41
+ wpSiteInsertStatement = getWpSiteInsertStatement(line);
35
42
 
36
43
  if (lineIsMultiSite) {
37
44
  isMultiSiteSqlDump = true;
@@ -39,9 +46,12 @@ const siteTypeValidations = {
39
46
  },
40
47
  postLineExecutionProcessing: async ({
41
48
  appId,
42
- envId
49
+ envId,
50
+ searchReplace
43
51
  }) => {
44
52
  const isMultiSite = await (0, _isMultiSite.isMultiSiteInSiteMeta)(appId, envId);
53
+ const primaryDomainFromSQL = (0, _isMultisiteDomainMapped.getPrimaryDomain)(wpSiteInsertStatement, searchReplace);
54
+ const isPrimaryDomainMapped = primaryDomainFromSQL && (await (0, _isMultisiteDomainMapped.isMultisitePrimaryDomainMapped)(appId, envId, primaryDomainFromSQL));
45
55
 
46
56
  const track = _tracker.trackEventWithEnv.bind(null, appId, envId);
47
57
 
@@ -61,6 +71,13 @@ const siteTypeValidations = {
61
71
  });
62
72
  throw new Error('You have requested a subsite SQL import but have not provided a subsite compatiable SQL dump.');
63
73
  }
74
+
75
+ if (isMultiSite && !isPrimaryDomainMapped) {
76
+ await track('import_sql_command_error', {
77
+ error_type: 'multisite-import-where-primary-domain-unmapped'
78
+ });
79
+ throw new Error('This import would set the network\'s main site domain to ' + primaryDomainFromSQL + ', however this domain is not mapped to the target environment. Please replace this domain in your ' + 'import file, or map it to the environment.');
80
+ }
64
81
  }
65
82
  };
66
83
  exports.siteTypeValidations = siteTypeValidations;
@@ -41,6 +41,10 @@ function formatError(message) {
41
41
  return `${_chalk.default.red('SQL Error:')} ${message}`;
42
42
  }
43
43
 
44
+ function formatWarning(message) {
45
+ return `${_chalk.default.yellow('Warning:')} ${message}`;
46
+ }
47
+
44
48
  function formatRecommendation(message) {
45
49
  return `${_chalk.default.yellow('Recommendation:')} ${message}`;
46
50
  }
@@ -51,13 +55,24 @@ const generalCheckFormatter = check => {
51
55
  const validProblems = check.results.filter(result => !result.falsePositive);
52
56
 
53
57
  if (validProblems.length > 0) {
54
- problemsFound += 1;
58
+ if (validProblems.some(result => !result.warning)) {
59
+ problemsFound += 1;
60
+ }
55
61
 
56
62
  for (const problem of validProblems) {
57
- errors.push({
58
- error: formatError(`${problem.text || check.message} on line ${problem.lineNumber || ''}.`),
59
- recommendation: formatRecommendation(problem.recomendation || check.recommendation)
60
- });
63
+ const text = `${problem.text || check.message} on line ${problem.lineNumber || ''}.`;
64
+
65
+ if (problem.warning) {
66
+ errors.push({
67
+ warning: formatWarning(text),
68
+ recommendation: formatRecommendation(problem.recomendation || check.recommendation)
69
+ });
70
+ } else {
71
+ errors.push({
72
+ error: formatError(text),
73
+ recommendation: formatRecommendation(problem.recomendation || check.recommendation)
74
+ });
75
+ }
61
76
  }
62
77
  } else {
63
78
  infos.push(`✅ ${check.message} was found 0 times.`);
@@ -268,6 +283,7 @@ const checks = {
268
283
  }
269
284
 
270
285
  return {
286
+ warning: true,
271
287
  lineNumber,
272
288
  recomendation: `Use '--search-replace="${foundDomain},${expectedDomain}"' switch to replace the domain`
273
289
  };
@@ -304,12 +320,21 @@ const postValidation = async options => {
304
320
 
305
321
  const errorSummary = {};
306
322
  const checkEntries = Object.entries(checks).filter(([type]) => !options.skipChecks.includes(type));
323
+ const formattedWarnings = [];
307
324
  let formattedErrors = [];
308
325
  let formattedInfos = [];
309
326
 
310
327
  for (const [type, check] of checkEntries) {
311
328
  const formattedOutput = check.outputFormatter(check, type, options.isImport);
312
- formattedErrors = formattedErrors.concat(formattedOutput.errors);
329
+
330
+ for (const error of formattedOutput.errors) {
331
+ if (error.warning) {
332
+ formattedWarnings.push(error);
333
+ } else {
334
+ formattedErrors.push(error);
335
+ }
336
+ }
337
+
313
338
  formattedInfos = formattedInfos.concat(formattedOutput.infos);
314
339
  errorSummary[type] = check.results.length;
315
340
  } // eslint-disable-next-line camelcase
@@ -341,6 +366,21 @@ const postValidation = async options => {
341
366
  formattedErrors = formattedErrors.concat(errorObject);
342
367
  }
343
368
 
369
+ if (formattedWarnings.length) {
370
+ const warningOutput = [];
371
+ formattedWarnings.forEach(warning => {
372
+ warningOutput.push(warning.warning);
373
+
374
+ if (warning.recommendation) {
375
+ warningOutput.push(warning.recommendation);
376
+ }
377
+
378
+ warningOutput.push('');
379
+ });
380
+ console.log(warningOutput.join('\n'));
381
+ console.log('');
382
+ }
383
+
344
384
  if (problemsFound > 0) {
345
385
  await (0, _tracker.trackEvent)('import_validate_sql_command_failure', {
346
386
  is_import: options.isImport,
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.getMultilineStatement = getMultilineStatement;
7
+
8
+ /**
9
+ * Get SQL statements matching a supplied pattern from a file stream
10
+ *
11
+ * @param {RegExp} statementRegex A RegExp pattern representing the start of the statement to capture
12
+ * @returns {function} A function which processes individual lines to capture the matching statements
13
+ */
14
+ function getMultilineStatement(statementRegex) {
15
+ const matchingStatements = [];
16
+ let isCapturing = false;
17
+ let index = 0;
18
+ /**
19
+ * Processes each line of the file stream and builds an array of statements which start with the supplied pattern
20
+ *
21
+ * @param {string} line A line from the file stream
22
+ * @returns {array} An array of matching statements where each statement is presented as an array of lines
23
+ */
24
+
25
+ return line => {
26
+ const shouldStartCapture = statementRegex.test(line);
27
+ const shouldEndCapture = (shouldStartCapture || isCapturing) && /;$/.test(line);
28
+
29
+ if (shouldStartCapture) {
30
+ isCapturing = true;
31
+ matchingStatements[index] = [];
32
+ }
33
+
34
+ if (isCapturing) {
35
+ matchingStatements[index].push(line);
36
+ }
37
+
38
+ if (shouldEndCapture) {
39
+ isCapturing = false;
40
+ index++;
41
+ }
42
+
43
+ return matchingStatements;
44
+ };
45
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automattic/vip",
3
- "version": "2.13.0",
3
+ "version": "2.15.0",
4
4
  "lockfileVersion": 1,
5
5
  "requires": true,
6
6
  "dependencies": {
@@ -10104,8 +10104,8 @@
10104
10104
  "dev": true
10105
10105
  },
10106
10106
  "lando": {
10107
- "version": "git+https://github.com/Automattic/lando-cli.git#2a5c2a4c1b1a428be62d5907d62b4d8c6e659b50",
10108
- "from": "git+https://github.com/Automattic/lando-cli.git#v3.5.2-patch2022_06_13",
10107
+ "version": "git+https://github.com/Automattic/lando-cli.git#3c74058161e51c9f482e6fa2727f616070853b98",
10108
+ "from": "git+https://github.com/Automattic/lando-cli.git#v3.5.2-patch2022_06_20",
10109
10109
  "requires": {
10110
10110
  "@lando/platformsh": "^0.6.0",
10111
10111
  "axios": "0.21.4",
@@ -10865,9 +10865,9 @@
10865
10865
  "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
10866
10866
  },
10867
10867
  "minipass": {
10868
- "version": "3.1.6",
10869
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz",
10870
- "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==",
10868
+ "version": "3.3.3",
10869
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.3.tgz",
10870
+ "integrity": "sha512-N0BOsdFAlNRfmwMhjAsLVWOk7Ljmeb39iqFlsV1At+jqRhSUP9yeof8FyJu4imaJiSUp8vQebWD/guZwGQC8iA==",
10871
10871
  "requires": {
10872
10872
  "yallist": "^4.0.0"
10873
10873
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automattic/vip",
3
- "version": "2.13.0",
3
+ "version": "2.15.0",
4
4
  "description": "The VIP Javascript library & CLI",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -72,7 +72,7 @@
72
72
  "license": "MIT",
73
73
  "engines": {
74
74
  "node": ">=14.14.0",
75
- "npm": "^6 "
75
+ "npm": ">=6"
76
76
  },
77
77
  "bugs": {
78
78
  "url": "https://github.com/Automattic/vip/issues"
@@ -124,7 +124,7 @@
124
124
  "ini": "2.0.0",
125
125
  "json2csv": "5.0.6",
126
126
  "jwt-decode": "2.2.0",
127
- "lando": "git+https://github.com/Automattic/lando-cli.git#v3.5.2-patch2022_06_13",
127
+ "lando": "git+https://github.com/Automattic/lando-cli.git#v3.5.2-patch2022_06_20",
128
128
  "node-fetch": "^2.6.1",
129
129
  "opn": "5.5.0",
130
130
  "proxy-from-env": "^1.1.0",
@@ -1,67 +0,0 @@
1
- version: 2.1
2
-
3
- workflows:
4
- build:
5
- jobs:
6
- - lint
7
- - flow
8
- - test:
9
- version: 14-stretch
10
- name: 14-stretch
11
- requires:
12
- - lint
13
- - flow
14
- - test:
15
- version: lts-stretch
16
- name: lts-stretch
17
- requires:
18
- - lint
19
- - flow
20
- - test:
21
- version: current-stretch
22
- name: current-stretch
23
- requires:
24
- - lint
25
- - flow
26
-
27
- jobs:
28
- lint:
29
- docker:
30
- - image: circleci/node:current-stretch
31
-
32
- working_directory: ~/repo
33
-
34
- steps:
35
- - checkout
36
- - run: npm ci
37
- - run:
38
- name: Run lint
39
- command: npm run lint
40
-
41
- flow:
42
- docker:
43
- - image: circleci/node:current-stretch
44
-
45
- working_directory: ~/repo
46
-
47
- steps:
48
- - checkout
49
- - run: npm ci
50
- - run:
51
- name: Run flow
52
- command: npm run flow
53
-
54
- test:
55
- parameters:
56
- version:
57
- type: string
58
-
59
- docker:
60
- - image: circleci/node:<< parameters.version >>
61
-
62
- working_directory: ~/repo
63
-
64
- steps:
65
- - checkout
66
- - run: npm ci
67
- - run: npm run jest