@automattic/vip 2.8.2 → 2.9.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.
@@ -6,14 +6,8 @@ workflows:
6
6
  - lint
7
7
  - flow
8
8
  - test:
9
- version: 10-stretch
10
- name: 10-stretch
11
- requires:
12
- - lint
13
- - flow
14
- - test:
15
- version: 12-stretch
16
- name: 12-stretch
9
+ version: 14-stretch
10
+ name: 14-stretch
17
11
  requires:
18
12
  - lint
19
13
  - flow
package/README.md CHANGED
@@ -27,6 +27,18 @@ By default, we record information about the usage of this tool using an in-house
27
27
 
28
28
  ## Changelog
29
29
 
30
+ ### 2.9.0 (1 March 2022)
31
+ - #966 [dev-env] Dynamic WordPress Image List
32
+ - #975 [dev-env] prompt On Unselected Env
33
+ - #974 [dev-env] Corrections of text for -h menu in dev-env create
34
+ - #973 [dev-env] update Nginx image
35
+ - #971 [dev-env] Use custom add user command
36
+ - #964 [dev-env] Validate sql on import
37
+ - #970 [dev-env] Do not use /tmp as a userConfRoot
38
+ - #977 Fix flow errors
39
+ - #976 Fix/duplicate shortcut parameter
40
+ - #968 Update minimum Node version
41
+
30
42
  ### 2.8.2 (27 January 2021)
31
43
  - #961 Fixes md5 calculation failing when search-replace is used
32
44
  - #959 Fixes md5 calculation for SQL Imports on VIPd
@@ -26,7 +26,7 @@ services:
26
26
  ssl: true
27
27
  sslExpose: false
28
28
  services:
29
- image: ghcr.io/automattic/vip-container-images/nginx:1.21.3
29
+ image: ghcr.io/automattic/vip-container-images/nginx:1.21.6
30
30
  command: nginx -g "daemon off;"
31
31
  volumes:
32
32
  - ./nginx/extra.conf:/etc/nginx/conf.extra/extra.conf
@@ -39,15 +39,15 @@ const examples = [{
39
39
  description: 'Creates a local dev environment'
40
40
  }, {
41
41
  usage: `vip @123.production ${_devEnvironment.DEV_ENVIRONMENT_SUBCOMMAND} create`,
42
- description: 'Creates a local dev environment for prodaction site for id 123'
42
+ description: 'Creates a local dev environment for production site for id 123'
43
43
  }, {
44
- usage: `vip @123.production ${_devEnvironment.DEV_ENVIRONMENT_SUBCOMMAND} create --slug 'my_site'`,
45
- description: 'Creates a local dev environment for prodaction site for id 123 aliased as "my_site"'
44
+ usage: `vip ${_devEnvironment.DEV_ENVIRONMENT_SUBCOMMAND} create --slug=my_site`,
45
+ description: 'Creates a local dev environment aliased as "my_site"'
46
46
  }, {
47
- usage: `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} create --slug test`,
48
- description: 'Creates a blank local dev environment with custom name "test", this enables to create multiple independent environments'
47
+ usage: `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} create --slug=test`,
48
+ description: 'Assigning unique slugs to environments allows multiple environments to be created.'
49
49
  }, {
50
- usage: `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} create --multisite --wordpress "5.8" --client-code "~/git/my_code"`,
50
+ usage: `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} create --multisite --wordpress="5.8" --client-code="~/git/my_code"`,
51
51
  description: 'Creates a local multisite dev environment using WP 5.8 and client code is expected to be in "~/git/my_code"'
52
52
  }];
53
53
  const cmd = (0, _command.default)().option('slug', 'Custom name of the dev environment').option('title', 'Title for the WordPress site').option('multisite', 'Enable multisite install', undefined, value => {
@@ -38,7 +38,7 @@ const examples = [{
38
38
  usage: `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} destroy`,
39
39
  description: 'Destroys the default local dev environment'
40
40
  }, {
41
- usage: `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} destroy --slug foo`,
41
+ usage: `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} destroy --slug=foo`,
42
42
  description: 'Destroys a local dev environment named foo'
43
43
  }];
44
44
  (0, _command.default)().option('slug', 'Custom name of the dev environment').option('soft', 'Keep config files needed to start an environment intact').examples(examples).argv(process.argv, async (arg, opt) => {
@@ -28,7 +28,7 @@ const examples = [{
28
28
  usage: `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} import media path/to/wp-content/uploads`,
29
29
  description: 'Import contents of the given WP uploads folder file into the media library of the default dev environment'
30
30
  }, {
31
- usage: `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} import media path/to/wp-content/uploads --slug mysite`,
31
+ usage: `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} import media path/to/wp-content/uploads --slug=mysite`,
32
32
  description: 'Import contents of the given WP uploads folder file into the media library of a dev environment named `mysite`'
33
33
  }];
34
34
  (0, _command.default)({
@@ -22,6 +22,8 @@ var _devEnvironmentCore = require("../lib/dev-environment/dev-environment-core")
22
22
 
23
23
  var _devEnvironment = require("../lib/constants/dev-environment");
24
24
 
25
+ var _sql = require("../lib/validations/sql");
26
+
25
27
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
26
28
 
27
29
  /**
@@ -31,7 +33,7 @@ const examples = [{
31
33
  usage: `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} import sql some-wp-db-file.sql`,
32
34
  description: 'Import the contents of a WordPress database from an SQL file'
33
35
  }, {
34
- usage: `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} import sql wordpress.sql --slug my_site`,
36
+ usage: `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} import sql wordpress.sql --slug=my_site`,
35
37
  description: 'Import the contents of a WordPress database from an SQL file into `my_site`'
36
38
  }, {
37
39
  usage: `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} import sql wordpress.sql --search-replace="testsite.com,test-site.go-vip.net"`,
@@ -42,7 +44,7 @@ const examples = [{
42
44
  }];
43
45
  (0, _command.default)({
44
46
  requiredArgs: 1
45
- }).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').examples(examples).argv(process.argv, async (unmatchedArgs, opt) => {
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) => {
46
48
  const [fileName] = unmatchedArgs;
47
49
  const {
48
50
  searchReplace,
@@ -55,6 +57,11 @@ const examples = [{
55
57
  resolvedPath,
56
58
  inContainerPath
57
59
  } = await (0, _devEnvironmentCore.resolveImportPath)(slug, fileName, searchReplace, inPlace);
60
+
61
+ if (!opt.skipValidate) {
62
+ await (0, _sql.validate)(fileName, []);
63
+ }
64
+
58
65
  const importArg = ['wp', 'db', 'import', inContainerPath];
59
66
  await (0, _devEnvironmentCore.exec)(slug, importArg);
60
67
 
@@ -64,17 +71,8 @@ const examples = [{
64
71
 
65
72
  const cacheArg = ['wp', 'cache', 'flush'];
66
73
  await (0, _devEnvironmentCore.exec)(slug, cacheArg);
67
-
68
- try {
69
- const addUserArg = ['wp', 'user', 'create', 'vipgo', 'vipgo@go-vip.net', '--user_pass=password', '--role=administrator'];
70
- await (0, _devEnvironmentCore.exec)(slug, addUserArg);
71
- } catch (exception) {
72
- if ((exception.message || '').includes('is already registered')) {
73
- console.log(_chalk.default.bold(_chalk.default.green('Success: ')) + 'Skipping user vipgo provisioning');
74
- } else {
75
- throw exception;
76
- }
77
- }
74
+ const addUserArg = ['wp', 'dev-env-add-admin', '--username=vipgo', '--password=password'];
75
+ await (0, _devEnvironmentCore.exec)(slug, addUserArg);
78
76
  } catch (error) {
79
77
  (0, _devEnvironmentCli.handleCLIException)(error);
80
78
  }
@@ -27,13 +27,13 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
27
27
  */
28
28
  const debug = (0, _debug.default)('@automattic/vip:bin:dev-environment');
29
29
  const examples = [{
30
- usage: `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} info -all`,
30
+ usage: `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} info --all`,
31
31
  description: 'Return information about all local dev environments'
32
32
  }, {
33
33
  usage: `vip @123 ${_devEnvironment.DEV_ENVIRONMENT_SUBCOMMAND} info`,
34
34
  description: 'Return information about dev environment for site 123'
35
35
  }, {
36
- usage: `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} info -slug my_site`,
36
+ usage: `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} info --slug=my_site`,
37
37
  description: 'Return information about a local dev environment named "my_site"'
38
38
  }];
39
39
  (0, _command.default)().option('slug', 'Custom name of the dev environment').option('all', 'Show Info for all local dev environments').examples(examples).argv(process.argv, async (arg, opt) => {
@@ -62,7 +62,9 @@ cmd.argv(process.argv, async (arg, opt) => {
62
62
  statsd: currentInstanceData.statsd,
63
63
  phpmyadmin: currentInstanceData.phpmyadmin,
64
64
  xdebug: currentInstanceData.xdebug,
65
- mediaRedirectDomain: currentInstanceData.mediaRedirectDomain
65
+ mediaRedirectDomain: currentInstanceData.mediaRedirectDomain,
66
+ multisite: false,
67
+ title: ''
66
68
  };
67
69
  const instanceData = await (0, _devEnvironmentCli.promptForArguments)(preselectedOptions, defaultOptions);
68
70
  instanceData.siteSlug = slug;
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.DEV_ENVIRONMENT_COMPONENTS = exports.DEV_ENVIRONMENT_NOT_FOUND = exports.DEV_ENVIRONMENT_PROMPT_INTRO = exports.DEV_ENVIRONMENT_DEFAULTS = exports.DEV_ENVIRONMENT_FULL_COMMAND = exports.DEV_ENVIRONMENT_SUBCOMMAND = void 0;
6
+ exports.DEV_ENVIRONMENT_WORDPRESS_VERSION_FILE = exports.DEV_ENVIRONMENT_WORDPRESS_CACHE_KEY = exports.DEV_ENVIRONMENT_WORDPRESS_VERSIONS_URI = exports.DEV_ENVIRONMENT_RAW_GITHUB_HOST = exports.DEV_ENVIRONMENT_COMPONENTS = exports.DEV_ENVIRONMENT_NOT_FOUND = exports.DEV_ENVIRONMENT_PROMPT_INTRO = exports.DEV_ENVIRONMENT_DEFAULTS = exports.DEV_ENVIRONMENT_FULL_COMMAND = exports.DEV_ENVIRONMENT_SUBCOMMAND = void 0;
7
7
  const DEV_ENVIRONMENT_SUBCOMMAND = 'dev-env';
8
8
  exports.DEV_ENVIRONMENT_SUBCOMMAND = DEV_ENVIRONMENT_SUBCOMMAND;
9
9
  const DEV_ENVIRONMENT_FULL_COMMAND = `vip ${DEV_ENVIRONMENT_SUBCOMMAND}`;
@@ -20,4 +20,12 @@ exports.DEV_ENVIRONMENT_PROMPT_INTRO = DEV_ENVIRONMENT_PROMPT_INTRO;
20
20
  const DEV_ENVIRONMENT_NOT_FOUND = 'Environment not found.';
21
21
  exports.DEV_ENVIRONMENT_NOT_FOUND = DEV_ENVIRONMENT_NOT_FOUND;
22
22
  const DEV_ENVIRONMENT_COMPONENTS = ['wordpress', 'muPlugins', 'clientCode'];
23
- exports.DEV_ENVIRONMENT_COMPONENTS = DEV_ENVIRONMENT_COMPONENTS;
23
+ exports.DEV_ENVIRONMENT_COMPONENTS = DEV_ENVIRONMENT_COMPONENTS;
24
+ const DEV_ENVIRONMENT_RAW_GITHUB_HOST = 'raw.githubusercontent.com';
25
+ exports.DEV_ENVIRONMENT_RAW_GITHUB_HOST = DEV_ENVIRONMENT_RAW_GITHUB_HOST;
26
+ const DEV_ENVIRONMENT_WORDPRESS_VERSIONS_URI = '/Automattic/vip-container-images/master/wordpress/versions.json';
27
+ exports.DEV_ENVIRONMENT_WORDPRESS_VERSIONS_URI = DEV_ENVIRONMENT_WORDPRESS_VERSIONS_URI;
28
+ const DEV_ENVIRONMENT_WORDPRESS_CACHE_KEY = 'worpress-versions.json';
29
+ exports.DEV_ENVIRONMENT_WORDPRESS_CACHE_KEY = DEV_ENVIRONMENT_WORDPRESS_CACHE_KEY;
30
+ const DEV_ENVIRONMENT_WORDPRESS_VERSION_FILE = 'wordpress/wp-includes/version.php';
31
+ exports.DEV_ENVIRONMENT_WORDPRESS_VERSION_FILE = DEV_ENVIRONMENT_WORDPRESS_VERSION_FILE;
@@ -15,6 +15,7 @@ exports.promptForText = promptForText;
15
15
  exports.promptForBoolean = promptForBoolean;
16
16
  exports.promptForComponent = promptForComponent;
17
17
  exports.addDevEnvConfigurationOptions = addDevEnvConfigurationOptions;
18
+ exports.getTagChoices = getTagChoices;
18
19
 
19
20
  var _chalk = _interopRequireDefault(require("chalk"));
20
21
 
@@ -32,7 +33,7 @@ var _os = _interopRequireDefault(require("os"));
32
33
 
33
34
  var _devEnvironment = require("../constants/dev-environment");
34
35
 
35
- var _types = require("./types");
36
+ var _devEnvironmentCore = require("./dev-environment-core");
36
37
 
37
38
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
38
39
 
@@ -128,12 +129,8 @@ function processComponentOptionInput(passedParam, allowLocal) {
128
129
  function getOptionsFromAppInfo(appInfo) {
129
130
  var _appInfo$environment, _appInfo$environment2, _appInfo$environment3;
130
131
 
131
- if (!appInfo) {
132
- return {};
133
- }
134
-
135
132
  return {
136
- title: ((_appInfo$environment = appInfo.environment) === null || _appInfo$environment === void 0 ? void 0 : _appInfo$environment.name) || appInfo.name,
133
+ title: ((_appInfo$environment = appInfo.environment) === null || _appInfo$environment === void 0 ? void 0 : _appInfo$environment.name) || appInfo.name || '',
137
134
  multisite: !!(appInfo !== null && appInfo !== void 0 && (_appInfo$environment2 = appInfo.environment) !== null && _appInfo$environment2 !== void 0 && _appInfo$environment2.isMultisite),
138
135
  mediaRedirectDomain: (_appInfo$environment3 = appInfo.environment) === null || _appInfo$environment3 === void 0 ? void 0 : _appInfo$environment3.primaryDomain
139
136
  };
@@ -163,12 +160,19 @@ async function promptForArguments(preselectedOptions, defaultOptions) {
163
160
  elasticsearch: preselectedOptions.elasticsearch || defaultOptions.elasticsearch || _devEnvironment.DEV_ENVIRONMENT_DEFAULTS.elasticsearchVersion,
164
161
  mariadb: preselectedOptions.mariadb || defaultOptions.mariadb || _devEnvironment.DEV_ENVIRONMENT_DEFAULTS.mariadbVersion,
165
162
  mediaRedirectDomain: preselectedOptions.mediaRedirectDomain || '',
166
- wordpress: {},
167
- muPlugins: {},
168
- clientCode: {},
163
+ wordpress: {
164
+ mode: 'image'
165
+ },
166
+ muPlugins: {
167
+ mode: 'image'
168
+ },
169
+ clientCode: {
170
+ mode: 'image'
171
+ },
169
172
  statsd: false,
170
173
  phpmyadmin: false,
171
- xdebug: false
174
+ xdebug: false,
175
+ siteSlug: ''
172
176
  };
173
177
 
174
178
  if (!instanceData.mediaRedirectDomain && defaultOptions.mediaRedirectDomain) {
@@ -176,14 +180,24 @@ async function promptForArguments(preselectedOptions, defaultOptions) {
176
180
  const setMediaRedirectDomain = await promptForBoolean(mediaRedirectPromptText, true);
177
181
 
178
182
  if (setMediaRedirectDomain) {
179
- instanceData.mediaRedirectDomain = defaultOptions.mediaRedirectDomain;
183
+ var _defaultOptions$media;
184
+
185
+ instanceData.mediaRedirectDomain = (_defaultOptions$media = defaultOptions.mediaRedirectDomain) !== null && _defaultOptions$media !== void 0 ? _defaultOptions$media : '';
180
186
  }
181
187
  }
182
188
 
183
189
  for (const component of _devEnvironment.DEV_ENVIRONMENT_COMPONENTS) {
184
- const option = preselectedOptions[component];
185
- const defaultValue = defaultOptions[component];
186
- instanceData[component] = await processComponent(component, option, defaultValue);
190
+ var _preselectedOptions$c, _defaultOptions$compo;
191
+
192
+ const option = ((_preselectedOptions$c = preselectedOptions[component]) !== null && _preselectedOptions$c !== void 0 ? _preselectedOptions$c : '').toString();
193
+ const defaultValue = ((_defaultOptions$compo = defaultOptions[component]) !== null && _defaultOptions$compo !== void 0 ? _defaultOptions$compo : '').toString();
194
+ const result = await processComponent(component, option, defaultValue);
195
+
196
+ if (null === result) {
197
+ throw new Error('processComponent() returned null');
198
+ }
199
+
200
+ instanceData[component] = result;
187
201
  }
188
202
 
189
203
  for (const service of ['statsd', 'phpmyadmin', 'xdebug']) {
@@ -324,8 +338,13 @@ async function promptForComponent(component, allowLocal, defaultObject) {
324
338
 
325
339
  if (component === 'wordpress') {
326
340
  const message = `${messagePrefix}Which version would you like`;
327
- const tagChoices = getWordpressImageTags();
328
- let initialTagIndex = 0;
341
+ const tagChoices = await getTagChoices(); // First tag not: "Pre-Release"
342
+
343
+ const firstNonPreRelease = tagChoices.find(tag => {
344
+ return !tag.match(/Pre\-Release/g);
345
+ }); // Set initialTagIndex as the first non Pre-Release
346
+
347
+ let initialTagIndex = tagChoices.indexOf(firstNonPreRelease);
329
348
 
330
349
  if (defaultObject !== null && defaultObject !== void 0 && defaultObject.tag) {
331
350
  const defaultTagIndex = tagChoices.indexOf(defaultObject.tag);
@@ -366,9 +385,34 @@ function addDevEnvConfigurationOptions(command) {
366
385
  var _value$toLowerCase3;
367
386
 
368
387
  return 'false' !== (value === null || value === void 0 ? void 0 : (_value$toLowerCase3 = value.toLowerCase) === null || _value$toLowerCase3 === void 0 ? void 0 : _value$toLowerCase3.call(value));
369
- }).option('elasticsearch', 'Explicitly choose Elasticsearch version to use').option('mariadb', 'Explicitly choose MariaDB version to use').option('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.');
388
+ }).option('elasticsearch', 'Explicitly choose Elasticsearch version to use').option('mariadb', 'Explicitly choose MariaDB version to use').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.');
370
389
  }
390
+ /**
391
+ * Provides the list of tag choices for selection
392
+ */
393
+
394
+
395
+ async function getTagChoices() {
396
+ const tagChoices = [];
397
+ let tagFormatted, prerelease, mapping;
398
+ const versions = await (0, _devEnvironmentCore.getVersionList)();
399
+
400
+ if (versions.length < 1) {
401
+ return ['5.9', '5.8', '5.7', '5.6', '5.5'];
402
+ }
403
+
404
+ for (const version of versions) {
405
+ tagFormatted = version.tag.padEnd(8 - version.tag.length);
406
+ prerelease = version.prerelease ? '(Pre-Release)' : '';
407
+
408
+ if (version.tag !== version.ref) {
409
+ mapping = `→ ${prerelease} ${version.ref}`;
410
+ } else {
411
+ mapping = '';
412
+ }
413
+
414
+ tagChoices.push(`${tagFormatted} ${mapping}`);
415
+ }
371
416
 
372
- function getWordpressImageTags() {
373
- return ['5.9', '5.8.3', '5.8.2', '5.8.1', '5.8', '5.7.3', '5.7.2'];
417
+ return tagChoices.sort().reverse();
374
418
  }
@@ -17,11 +17,15 @@ exports.getEnvironmentPath = getEnvironmentPath;
17
17
  exports.getApplicationInformation = getApplicationInformation;
18
18
  exports.resolveImportPath = resolveImportPath;
19
19
  exports.importMediaPath = importMediaPath;
20
+ exports.fetchVersionList = fetchVersionList;
21
+ exports.getVersionList = getVersionList;
20
22
 
21
23
  var _debug = _interopRequireDefault(require("debug"));
22
24
 
23
25
  var _xdgBasedir = _interopRequireDefault(require("xdg-basedir"));
24
26
 
27
+ var _nodeFetch = _interopRequireDefault(require("node-fetch"));
28
+
25
29
  var _os = _interopRequireDefault(require("os"));
26
30
 
27
31
  var _fs = _interopRequireDefault(require("fs"));
@@ -84,6 +88,8 @@ async function startEnvironment(slug, options) {
84
88
  throw new Error(_devEnvironment.DEV_ENVIRONMENT_NOT_FOUND);
85
89
  }
86
90
 
91
+ await updateWordPressImage(instancePath, options);
92
+
87
93
  if (options.skipRebuild) {
88
94
  await (0, _devEnvironmentLando.landoStart)(instancePath);
89
95
  } else {
@@ -343,6 +349,17 @@ async function getApplicationInformation(appId, envType) {
343
349
  envData = environments.find(candidateEnv => candidateEnv.type === envType);
344
350
  } else if (1 === environments.length) {
345
351
  envData = environments[0];
352
+ } else {
353
+ const choices = environments.map(candidateEnv => candidateEnv.type);
354
+ const {
355
+ env
356
+ } = await (0, _enquirer.prompt)({
357
+ type: 'select',
358
+ name: 'env',
359
+ message: 'Which environment?',
360
+ choices
361
+ });
362
+ envData = environments.find(candidateEnv => candidateEnv.type === env);
346
363
  }
347
364
 
348
365
  if (envData) {
@@ -441,4 +458,162 @@ async function importMediaPath(slug, filePath) {
441
458
  _copyDir.default.sync(resolvedPath, uploadsPath);
442
459
 
443
460
  console.log(`${_chalk.default.green('✓')} Files successfully copied to ${uploadsPath}.`);
461
+ }
462
+ /**
463
+ * Uses the WordPress versions manifest on github.com
464
+ * Informs the user several things:
465
+ * - If the WordPress image their env uses is no longer available
466
+ * - If there is a newer version of the WordPress version currently used
467
+ * - A choice to use a different image
468
+ *
469
+ * @param {Object=} instancePath Path to local profile
470
+ * @param {Object=} options options
471
+ */
472
+
473
+
474
+ async function updateWordPressImage(instancePath, options) {
475
+ const versions = await getVersionList();
476
+ const refRgx = new RegExp(/\d+\.\d+(?:\.\d+)?/);
477
+ const vsnRgx = new RegExp(/\$wp_version \= \'(.*)\'\;/);
478
+ const landoFile = `${instancePath}/.lando.yml`;
479
+ const versionFile = `${instancePath}/${_devEnvironment.DEV_ENVIRONMENT_WORDPRESS_VERSION_FILE}`; // If versionFile doesn't exist it means that the environment has not been initiated.
480
+
481
+ if (!_fs.default.existsSync(versionFile)) {
482
+ return;
483
+ } // filter
484
+
485
+
486
+ const filteredVersions = versions.filter(vsn => {
487
+ return refRgx.test(vsn.ref);
488
+ }); // sort
489
+
490
+ filteredVersions.sort((before, after) => before.tag < after.tag ? 1 : -1); // Newest WordPress Image
491
+
492
+ const newestWordPressImage = filteredVersions[0];
493
+ console.log('The most recent WordPress version available is: ' + _chalk.default.green(newestWordPressImage.tag)); // Currently Used WordPress Version
494
+
495
+ const code = _fs.default.readFileSync(versionFile).toString();
496
+
497
+ const versionCode = vsnRgx.exec(code);
498
+ let currentWordPressVersion = null;
499
+
500
+ if (null === versionCode) {
501
+ console.log('Cannot determine the currently installed WordPress version.');
502
+ } else {
503
+ currentWordPressVersion = versionCode[1];
504
+ } // If the currently used version is the most up to date: exit.
505
+
506
+
507
+ if (currentWordPressVersion === newestWordPressImage.ref) {
508
+ console.log('Environment WordPress version is: ' + _chalk.default.green(currentWordPressVersion) + ' ... 😎 nice! ');
509
+ return;
510
+ } // Determine if there is an image available for the current WordPress version
511
+
512
+
513
+ const match = filteredVersions.find(({
514
+ ref
515
+ }) => ref === currentWordPressVersion); // If there is no available image for the currently installed version, give user a path to change
516
+
517
+ if (typeof match === 'undefined') {
518
+ console.log(`Installed WordPress: ${currentWordPressVersion} has no available container image in repository. `);
519
+ console.log('You must select a new WordPress image to continue... ');
520
+ } else {
521
+ console.log('Environment WordPress version is: ' + _chalk.default.yellow(match.ref));
522
+ } // Prompt the user to select a new WordPress Version
523
+
524
+
525
+ const confirm = await (0, _enquirer.prompt)({
526
+ type: 'confirm',
527
+ name: 'upgrade',
528
+ message: 'Would You like to change the WordPress version? '
529
+ }); // If the user takes the new WP version path
530
+
531
+ if (confirm.upgrade) {
532
+ console.log('Upgrading from: ' + _chalk.default.yellow(match.ref) + ' to:'); // Select a new image
533
+
534
+ const choice = await (0, _devEnvironmentCli.promptForComponent)('wordpress');
535
+ const version = filteredVersions.find(({
536
+ tag
537
+ }) => tag.trim() === choice.tag.trim());
538
+ const selectedImage = `ghcr.io/automattic/vip-container-images/wordpress:${version.tag}`; // Change the lando file and rebuild.
539
+
540
+ const data = _fs.default.readFileSync(landoFile, {
541
+ encoding: 'utf8',
542
+ flag: 'r'
543
+ });
544
+
545
+ const edit = data.replace(/ghcr\.io\/.*wordpress\:.*\d+\.\d+(?:\.\d+)?/g, selectedImage);
546
+
547
+ _fs.default.writeFileSync(landoFile, edit); // Stage for rebuild
548
+
549
+
550
+ options.skipRebuild = false;
551
+ }
552
+
553
+ return;
554
+ }
555
+ /**
556
+ * Makes a web call to raw.githubusercontent.com
557
+ */
558
+
559
+
560
+ async function fetchVersionList() {
561
+ const url = `https://${_devEnvironment.DEV_ENVIRONMENT_RAW_GITHUB_HOST}${_devEnvironment.DEV_ENVIRONMENT_WORDPRESS_VERSIONS_URI}`;
562
+ return (0, _nodeFetch.default)(url).then(res => res.text());
563
+ }
564
+ /**
565
+ * Encapsulates the logic for determining if a file is expired by an arbitrary TTL
566
+ * @param {string} cacheFile uri of cache file
567
+ * @param {number} ttl time to live in seconds
568
+ * @returns {boolean} version list expired true/false
569
+ */
570
+
571
+
572
+ function isVersionListExpired(cacheFile, ttl) {
573
+ const stats = _fs.default.statSync(cacheFile);
574
+
575
+ const expire = new Date(stats.mtime);
576
+ expire.setSeconds(expire.getSeconds() + ttl);
577
+ return +new Date() > expire;
578
+ }
579
+ /**
580
+ * Uses a cache file to keep the version list in tow until it is ultimately outdated
581
+ */
582
+
583
+
584
+ async function getVersionList() {
585
+ let res;
586
+
587
+ const mainEnvironmentPath = _xdgBasedir.default.data || _os.default.tmpdir();
588
+
589
+ const cacheFile = _path.default.join(mainEnvironmentPath, 'vip', _devEnvironment.DEV_ENVIRONMENT_WORDPRESS_CACHE_KEY); // Handle from cache
590
+
591
+
592
+ try {
593
+ // If the cache doesn't exist, create it
594
+ if (!_fs.default.existsSync(cacheFile)) {
595
+ res = await fetchVersionList();
596
+
597
+ _fs.default.writeFileSync(cacheFile, res);
598
+ } // If the cache is expired, refresh it
599
+
600
+
601
+ if (isVersionListExpired(cacheFile, _devEnvironment.DEV_ENVIRONMENT_WORDPRESS_VERSION_TTL)) {
602
+ res = await fetchVersionList();
603
+
604
+ _fs.default.writeFileSync(cacheFile, res);
605
+ }
606
+ } catch (err) {
607
+ // Soft error handling here, since it's still possible to use a previously cached file.
608
+ console.log(_chalk.default.yellow('fetchWordPressVersionList failed to retrieve an updated version list'));
609
+ debug(err);
610
+ } // Try to parse the cached file if it exists.
611
+
612
+
613
+ try {
614
+ return JSON.parse(_fs.default.readFileSync(cacheFile));
615
+ } catch (err) {
616
+ debug(err);
617
+ return [];
618
+ }
444
619
  }
@@ -12,8 +12,14 @@ exports.landoExec = landoExec;
12
12
 
13
13
  var _debug = _interopRequireDefault(require("debug"));
14
14
 
15
+ var _fs = _interopRequireDefault(require("fs"));
16
+
17
+ var _os = _interopRequireDefault(require("os"));
18
+
15
19
  var _path = _interopRequireDefault(require("path"));
16
20
 
21
+ var _util = require("util");
22
+
17
23
  var _lando = _interopRequireDefault(require("lando/lib/lando"));
18
24
 
19
25
  var _utils = _interopRequireDefault(require("lando/plugins/lando-core/lib/utils"));
@@ -24,6 +30,8 @@ var _chalk = _interopRequireDefault(require("chalk"));
24
30
 
25
31
  var _app = _interopRequireDefault(require("lando/lib/app"));
26
32
 
33
+ var _coreJs = require("core-js");
34
+
27
35
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
28
36
 
29
37
  /**
@@ -44,6 +52,23 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
44
52
  */
45
53
  const DEBUG_KEY = '@automattic/vip:bin:dev-environment-lando';
46
54
  const debug = (0, _debug.default)(DEBUG_KEY);
55
+ const mkdtemp = (0, _util.promisify)(_fs.default.mkdtemp);
56
+ let landoConfRoot;
57
+ /**
58
+ * @returns {string} User configuration root directory (aka userConfRoot in Lando)
59
+ */
60
+
61
+ function getLandoUserConfigurationRoot() {
62
+ if (!landoConfRoot) {
63
+ landoConfRoot = _path.default.join(_os.default.tmpdir(), 'lando');
64
+ }
65
+
66
+ return landoConfRoot;
67
+ }
68
+ /**
69
+ * @returns {object} Lando configuration
70
+ */
71
+
47
72
 
48
73
  function getLandoConfig() {
49
74
  const landoPath = _path.default.join(__dirname, '..', '..', '..', 'node_modules', 'lando');
@@ -59,7 +84,8 @@ function getLandoConfig() {
59
84
  path: _path.default.join(landoPath, 'integrations'),
60
85
  subdir: '.'
61
86
  }],
62
- proxyName: 'vip-dev-env-proxy'
87
+ proxyName: 'vip-dev-env-proxy',
88
+ userConfRoot: getLandoUserConfigurationRoot()
63
89
  };
64
90
  }
65
91
 
@@ -162,6 +162,15 @@ const checks = {
162
162
  excerpt: "'DROP DATABASE' should not be present (case-insensitive)",
163
163
  recommendation: 'Remove these lines'
164
164
  },
165
+ useStatement: {
166
+ matcher: /^USE /i,
167
+ matchHandler: lineNumber => lineNumber,
168
+ outputFormatter: errorCheckFormatter,
169
+ results: [],
170
+ message: 'USE <DATABASE_NAME> statement',
171
+ excerpt: "'USE <DATABASE_NAME>' should not be present (case-insensitive)",
172
+ recommendation: 'Remove these lines'
173
+ },
165
174
  alterUser: {
166
175
  matcher: /^(ALTER USER|SET PASSWORD)/i,
167
176
  matchHandler: lineNumber => lineNumber,
@@ -309,13 +318,14 @@ const checkForTableName = line => {
309
318
  }
310
319
  };
311
320
 
312
- const perLineValidations = (line, runAsImport) => {
321
+ const perLineValidations = (line, runAsImport, skipChecks) => {
313
322
  if (lineNum % 500 === 0) {
314
323
  runAsImport ? '' : (0, _singleLineLog.stdout)(`Reading line ${lineNum} `);
315
324
  }
316
325
 
317
326
  checkForTableName(line);
318
- const checkValues = Object.values(checks);
327
+ const checkKeys = Object.keys(checks).filter(checkItem => !skipChecks.includes(checkItem));
328
+ const checkValues = checkKeys.map(checkKey => checks[checkKey]);
319
329
  checkValues.forEach(check => {
320
330
  const results = line.match(check.matcher);
321
331
 
@@ -326,8 +336,8 @@ const perLineValidations = (line, runAsImport) => {
326
336
  lineNum += 1;
327
337
  };
328
338
 
329
- const execute = (line, isImport = true) => {
330
- perLineValidations(line, isImport);
339
+ const execute = (line, isImport = true, skipChecks = ['useStatement']) => {
340
+ perLineValidations(line, isImport, skipChecks);
331
341
  };
332
342
 
333
343
  const postLineExecutionProcessing = async ({
@@ -344,17 +354,17 @@ const staticSqlValidations = {
344
354
 
345
355
  exports.staticSqlValidations = staticSqlValidations;
346
356
 
347
- const validate = async (filename, isImport = false) => {
357
+ const validate = async (filename, skipChecks = ['useStatement']) => {
348
358
  const readInterface = await (0, _lineByLine.getReadInterface)(filename);
349
359
  readInterface.on('line', line => {
350
- execute(line, isImport);
360
+ execute(line, false, skipChecks);
351
361
  }); // Block until the processing completes
352
362
 
353
363
  await new Promise(resolve => readInterface.on('close', resolve));
354
364
  readInterface.close();
355
365
  await postLineExecutionProcessing({
356
366
  filename,
357
- isImport
367
+ isImport: false
358
368
  });
359
369
  };
360
370
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automattic/vip",
3
- "version": "2.8.2",
3
+ "version": "2.9.0",
4
4
  "lockfileVersion": 1,
5
5
  "requires": true,
6
6
  "dependencies": {
@@ -7571,9 +7571,9 @@
7571
7571
  "dev": true
7572
7572
  },
7573
7573
  "flow-bin": {
7574
- "version": "0.150.0",
7575
- "resolved": "https://registry.npmjs.org/flow-bin/-/flow-bin-0.150.0.tgz",
7576
- "integrity": "sha512-s+0dcKJnZZO6mkS92YtJzBZ6vq7i1o77+NggEiiefPpCVsehwPlyRLmEnb6XN9OI5OfXp+kFnZt4q8a3zLNuUg==",
7574
+ "version": "0.172.0",
7575
+ "resolved": "https://registry.npmjs.org/flow-bin/-/flow-bin-0.172.0.tgz",
7576
+ "integrity": "sha512-v9KolEk3qd+wFc2ABPaSr5/7VWbHOjdAzRaWwynEtaeMcKN0awlx0Q7b71g/XgVf/fWMR+K8q+3s/TCH+Gky/Q==",
7577
7577
  "dev": true
7578
7578
  },
7579
7579
  "follow-redirects": {
@@ -10595,7 +10595,7 @@
10595
10595
  },
10596
10596
  "onetime": {
10597
10597
  "version": "1.1.0",
10598
- "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz",
10598
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz",
10599
10599
  "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=",
10600
10600
  "dev": true
10601
10601
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automattic/vip",
3
- "version": "2.8.2",
3
+ "version": "2.9.0",
4
4
  "description": "The VIP Javascript library & CLI",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -70,7 +70,7 @@
70
70
  ],
71
71
  "license": "MIT",
72
72
  "engines": {
73
- "node": ">=10"
73
+ "node": ">=14.14.0"
74
74
  },
75
75
  "bugs": {
76
76
  "url": "https://github.com/Automattic/vip/issues"
@@ -97,7 +97,7 @@
97
97
  "eslint-plugin-no-async-foreach": "0.1.1",
98
98
  "eslint-plugin-react": "7.26.0",
99
99
  "eslint-plugin-wpcalypso": "5.0.0",
100
- "flow-bin": "0.150.0",
100
+ "flow-bin": "0.172.0",
101
101
  "jest": "27.2.1",
102
102
  "nock": "13.0.11",
103
103
  "prettier": "npm:wp-prettier@2.0.5",