@automattic/vip 2.8.2 → 2.9.2

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
@@ -26,6 +26,33 @@ For help with contributing to this project, including instructions for local dev
26
26
  By default, we record information about the usage of this tool using an in-house analytics sytem. If you would prefer to opt-out of this data collection, you can do so via the `DO_NOT_TRACK` environment variable. You may either export it in your shell configuration or specify it on the command line (e.g. `DO_NOT_TRACK=1 vip app list`).
27
27
 
28
28
  ## Changelog
29
+ ### 2.9.2 (9 March 2022)
30
+ - #980 [dev-env] Fix/tag formatting on stapled images
31
+ - #986 Clean the build folder prior to rebuilding it
32
+ - #985 Adding webP to the list of accepted extensions for files
33
+ - #972 Run tests in Windows Env
34
+
35
+ https://github.com/Automattic/vip/releases/tag/v2.9.2
36
+
37
+ ### 2.9.1 (2 March 2022)
38
+ - #982 Remove unused dependencies - Fixes Error: Cannot find module 'core-js'
39
+ - #978 [dev-env] Added phpmyadmin proxy value
40
+
41
+ https://github.com/Automattic/vip/releases/tag/v2.9.1
42
+
43
+ ### 2.9.0 (1 March 2022)
44
+ - #966 [dev-env] Dynamic WordPress Image List
45
+ - #975 [dev-env] prompt On Unselected Env
46
+ - #974 [dev-env] Corrections of text for -h menu in dev-env create
47
+ - #973 [dev-env] update Nginx image
48
+ - #971 [dev-env] Use custom add user command
49
+ - #964 [dev-env] Validate sql on import
50
+ - #970 [dev-env] Do not use /tmp as a userConfRoot
51
+ - #977 Fix flow errors
52
+ - #976 Fix/duplicate shortcut parameter
53
+ - #968 Update minimum Node version
54
+
55
+ https://github.com/Automattic/vip/releases/tag/v2.9.0
29
56
 
30
57
  ### 2.8.2 (27 January 2021)
31
58
  - #961 Fixes md5 calculation failing when search-replace is used
@@ -7,6 +7,8 @@ proxy:
7
7
  <% if ( multisite ) { %>
8
8
  - '*.<%= siteSlug %>.vipdev.lndo.site'
9
9
  <% } %>
10
+ phpmyadmin:
11
+ - <%= siteSlug %>-pma.vipdev.lndo.site
10
12
  services:
11
13
 
12
14
  devtools:
@@ -26,7 +28,7 @@ services:
26
28
  ssl: true
27
29
  sslExpose: false
28
30
  services:
29
- image: ghcr.io/automattic/vip-container-images/nginx:1.21.3
31
+ image: ghcr.io/automattic/vip-container-images/nginx:1.21.6
30
32
  command: nginx -g "daemon off;"
31
33
  volumes:
32
34
  - ./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_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,10 @@ 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;
@@ -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,11 +338,21 @@ 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(); // Tag strings come back from the api with excess whitespace
342
+ // This strips the whitespace for matching to the defaultObject.tag
343
+
344
+ const formatted = tagChoices.map(elm => {
345
+ return elm.trim();
346
+ }); // First tag not: "Pre-Release"
347
+
348
+ const firstNonPreRelease = formatted.find(img => {
349
+ return !img.match(/Pre\-Release/g);
350
+ }); // Set initialTagIndex as the first non Pre-Release
351
+
352
+ let initialTagIndex = formatted.indexOf(firstNonPreRelease);
329
353
 
330
354
  if (defaultObject !== null && defaultObject !== void 0 && defaultObject.tag) {
331
- const defaultTagIndex = tagChoices.indexOf(defaultObject.tag);
355
+ const defaultTagIndex = formatted.indexOf(defaultObject.tag);
332
356
 
333
357
  if (defaultTagIndex !== -1) {
334
358
  initialTagIndex = defaultTagIndex;
@@ -337,10 +361,21 @@ async function promptForComponent(component, allowLocal, defaultObject) {
337
361
 
338
362
  const selectTag = new _enquirer.Select({
339
363
  message,
340
- choices: tagChoices,
364
+ choices: formatted,
341
365
  initial: initialTagIndex
342
366
  });
343
- const tag = await selectTag.run();
367
+ const option = await selectTag.run(); // Validate the input
368
+ // Some of the options are like: '5.7 → 5.7.5'
369
+ // Extract first occurrence of something that looks like a tag
370
+
371
+ const tagRgx = new RegExp(/(\d+\.\d+(?:\.\d+)?)/);
372
+ const match = tagRgx.exec(option);
373
+
374
+ if (!Array.isArray(match) || match.length < 2) {
375
+ throw new Error(`Invalid WordPress selection: ${option}`);
376
+ }
377
+
378
+ const tag = match[1];
344
379
  return {
345
380
  mode: modeResult,
346
381
  tag
@@ -366,9 +401,34 @@ function addDevEnvConfigurationOptions(command) {
366
401
  var _value$toLowerCase3;
367
402
 
368
403
  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.');
404
+ }).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
405
  }
406
+ /**
407
+ * Provides the list of tag choices for selection
408
+ */
409
+
410
+
411
+ async function getTagChoices() {
412
+ const tagChoices = [];
413
+ let tagFormatted, prerelease, mapping;
414
+ const versions = await (0, _devEnvironmentCore.getVersionList)();
415
+
416
+ if (versions.length < 1) {
417
+ return ['5.9', '5.8', '5.7', '5.6', '5.5'];
418
+ }
419
+
420
+ for (const version of versions) {
421
+ tagFormatted = version.tag.padEnd(8 - version.tag.length);
422
+ prerelease = version.prerelease ? '(Pre-Release)' : '';
423
+
424
+ if (version.tag !== version.ref) {
425
+ mapping = `→ ${prerelease} ${version.ref}`;
426
+ } else {
427
+ mapping = '';
428
+ }
429
+
430
+ tagChoices.push(`${tagFormatted} ${mapping}`);
431
+ }
371
432
 
372
- function getWordpressImageTags() {
373
- return ['5.9', '5.8.3', '5.8.2', '5.8.1', '5.8', '5.7.3', '5.7.2'];
433
+ return tagChoices.sort().reverse();
374
434
  }
@@ -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,7 +88,9 @@ async function startEnvironment(slug, options) {
84
88
  throw new Error(_devEnvironment.DEV_ENVIRONMENT_NOT_FOUND);
85
89
  }
86
90
 
87
- if (options.skipRebuild) {
91
+ const updated = await updateWordPressImage(slug);
92
+
93
+ if (options.skipRebuild && !updated) {
88
94
  await (0, _devEnvironmentLando.landoStart)(instancePath);
89
95
  } else {
90
96
  await (0, _devEnvironmentLando.landoRebuild)(instancePath);
@@ -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,150 @@ 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=} slug slug
470
+ * @return {boolean} boolean
471
+ */
472
+
473
+
474
+ async function updateWordPressImage(slug) {
475
+ const versions = await getVersionList();
476
+ const refRgx = new RegExp(/\d+\.\d+(?:\.\d+)?/);
477
+ let message, envData, currentWordPressTag; // Get the current environment configuration
478
+
479
+ try {
480
+ envData = readEnvironmentData(slug);
481
+ currentWordPressTag = envData.wordpress.tag;
482
+ } catch (error) {
483
+ // This can throw an exception if the env is build with older vip version
484
+ if ('ENOENT' === error.code) {
485
+ message = 'Environment was created before update was supported.\n\n';
486
+ message += 'To update environment please destroy it and create a new one.';
487
+ } else {
488
+ message = `An error prevented reading the configuration of: ${slug}\n\n ${error}`;
489
+ }
490
+
491
+ (0, _devEnvironmentCli.handleCLIException)(new Error(message));
492
+ } // filter
493
+
494
+
495
+ const filteredVersions = versions.filter(vsn => {
496
+ return refRgx.test(vsn.ref);
497
+ }); // sort
498
+
499
+ filteredVersions.sort((before, after) => before.tag < after.tag ? 1 : -1); // Newest WordPress Image
500
+
501
+ const newestWordPressImage = filteredVersions[0];
502
+ console.log('The most recent WordPress version available is: ' + _chalk.default.green(newestWordPressImage.tag)); // If the currently used version is the most up to date: exit.
503
+
504
+ if (currentWordPressTag === newestWordPressImage.ref) {
505
+ console.log('Environment WordPress version is: ' + _chalk.default.green(currentWordPressTag) + ' ... 😎 nice! ');
506
+ return false;
507
+ } // Determine if there is an image available for the current WordPress version
508
+
509
+
510
+ const match = filteredVersions.find(({
511
+ ref
512
+ }) => ref === currentWordPressTag); // If there is no available image for the currently installed version, give user a path to change
513
+
514
+ if (typeof match === 'undefined') {
515
+ console.log(`Installed WordPress: ${currentWordPressTag} has no available container image in repository. `);
516
+ console.log('You must select a new WordPress image to continue... ');
517
+ } else {
518
+ console.log('Environment WordPress version is: ' + _chalk.default.yellow(match.ref));
519
+ } // Prompt the user to select a new WordPress Version
520
+
521
+
522
+ const confirm = await (0, _enquirer.prompt)({
523
+ type: 'confirm',
524
+ name: 'upgrade',
525
+ message: 'Would You like to change the WordPress version? '
526
+ }); // If the user takes the new WP version path
527
+
528
+ if (confirm.upgrade) {
529
+ console.log('Upgrading from: ' + _chalk.default.yellow(currentWordPressTag) + ' to:'); // Select a new image
530
+
531
+ const choice = await (0, _devEnvironmentCli.promptForComponent)('wordpress');
532
+ const version = filteredVersions.find(({
533
+ tag
534
+ }) => tag.trim() === choice.tag.trim()); // Write new data and stage for rebuild
535
+
536
+ envData.wordpress.tag = version.tag;
537
+ await updateEnvironment(envData);
538
+ return true;
539
+ }
540
+
541
+ return false;
542
+ }
543
+ /**
544
+ * Makes a web call to raw.githubusercontent.com
545
+ */
546
+
547
+
548
+ async function fetchVersionList() {
549
+ const url = `https://${_devEnvironment.DEV_ENVIRONMENT_RAW_GITHUB_HOST}${_devEnvironment.DEV_ENVIRONMENT_WORDPRESS_VERSIONS_URI}`;
550
+ return (0, _nodeFetch.default)(url).then(res => res.text());
551
+ }
552
+ /**
553
+ * Encapsulates the logic for determining if a file is expired by an arbitrary TTL
554
+ * @param {string} cacheFile uri of cache file
555
+ * @param {number} ttl time to live in seconds
556
+ * @returns {boolean} version list expired true/false
557
+ */
558
+
559
+
560
+ function isVersionListExpired(cacheFile, ttl) {
561
+ const stats = _fs.default.statSync(cacheFile);
562
+
563
+ const expire = new Date(stats.mtime);
564
+ expire.setSeconds(expire.getSeconds() + ttl);
565
+ return +new Date() > expire;
566
+ }
567
+ /**
568
+ * Uses a cache file to keep the version list in tow until it is ultimately outdated
569
+ */
570
+
571
+
572
+ async function getVersionList() {
573
+ let res;
574
+
575
+ const mainEnvironmentPath = _xdgBasedir.default.data || _os.default.tmpdir();
576
+
577
+ const cacheFile = _path.default.join(mainEnvironmentPath, 'vip', _devEnvironment.DEV_ENVIRONMENT_WORDPRESS_CACHE_KEY); // Handle from cache
578
+
579
+
580
+ try {
581
+ // If the cache doesn't exist, create it
582
+ if (!_fs.default.existsSync(cacheFile)) {
583
+ res = await fetchVersionList();
584
+
585
+ _fs.default.writeFileSync(cacheFile, res);
586
+ } // If the cache is expired, refresh it
587
+
588
+
589
+ if (isVersionListExpired(cacheFile, _devEnvironment.DEV_ENVIRONMENT_WORDPRESS_VERSION_TTL)) {
590
+ res = await fetchVersionList();
591
+
592
+ _fs.default.writeFileSync(cacheFile, res);
593
+ }
594
+ } catch (err) {
595
+ // Soft error handling here, since it's still possible to use a previously cached file.
596
+ console.log(_chalk.default.yellow('fetchWordPressVersionList failed to retrieve an updated version list'));
597
+ debug(err);
598
+ } // Try to parse the cached file if it exists.
599
+
600
+
601
+ try {
602
+ return JSON.parse(_fs.default.readFileSync(cacheFile));
603
+ } catch (err) {
604
+ debug(err);
605
+ return [];
606
+ }
444
607
  }
@@ -12,6 +12,8 @@ exports.landoExec = landoExec;
12
12
 
13
13
  var _debug = _interopRequireDefault(require("debug"));
14
14
 
15
+ var _os = _interopRequireDefault(require("os"));
16
+
15
17
  var _path = _interopRequireDefault(require("path"));
16
18
 
17
19
  var _lando = _interopRequireDefault(require("lando/lib/lando"));
@@ -44,6 +46,22 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
44
46
  */
45
47
  const DEBUG_KEY = '@automattic/vip:bin:dev-environment-lando';
46
48
  const debug = (0, _debug.default)(DEBUG_KEY);
49
+ let landoConfRoot;
50
+ /**
51
+ * @returns {string} User configuration root directory (aka userConfRoot in Lando)
52
+ */
53
+
54
+ function getLandoUserConfigurationRoot() {
55
+ if (!landoConfRoot) {
56
+ landoConfRoot = _path.default.join(_os.default.tmpdir(), 'lando');
57
+ }
58
+
59
+ return landoConfRoot;
60
+ }
61
+ /**
62
+ * @returns {object} Lando configuration
63
+ */
64
+
47
65
 
48
66
  function getLandoConfig() {
49
67
  const landoPath = _path.default.join(__dirname, '..', '..', '..', 'node_modules', 'lando');
@@ -59,7 +77,8 @@ function getLandoConfig() {
59
77
  path: _path.default.join(landoPath, 'integrations'),
60
78
  subdir: '.'
61
79
  }],
62
- proxyName: 'vip-dev-env-proxy'
80
+ proxyName: 'vip-dev-env-proxy',
81
+ userConfRoot: getLandoUserConfigurationRoot()
63
82
  };
64
83
  }
65
84
 
@@ -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
 
@@ -17,7 +17,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
17
17
  * External dependencies
18
18
  */
19
19
  // Accepted media file extensions
20
- const acceptedExtensions = ['jpg', 'jpeg', 'jpe', 'gif', 'png', 'bmp', 'svg', 'tiff', 'tif', 'ico', 'asf', 'asx', 'wmv', 'wmx', 'wm', 'avi', 'divx', 'mov', 'qt', 'mpeg', 'mpg', 'mpe', 'mp4', 'm4v', 'ogv', 'webm', 'mkv', '3gp', '3gpp', '3g2', '3gp2', 'txt', 'asc', 'c', 'cc', 'h', 'srt', 'csv', 'tsv', 'ics', 'rtx', 'css', 'vtt', 'dfxp', 'mp3', 'm4a', 'm4b', 'ra', 'ram', 'wav', 'ogg', 'oga', 'mid', 'midi', 'wma', 'wax', 'mka', 'rtf', 'js', 'pdf', 'class', 'psd', 'xcf', 'doc', 'pot', 'pps', 'ppt', 'wri', 'xla', 'xls', 'xlt', 'xlw', 'mdb', 'mpp', 'docx', 'docm', 'dotx', 'dotm', 'xlsx', 'xlsm', 'xlsb', 'xltx', 'xltm', 'xlam', 'pptx', 'pptm', 'ppsx', 'ppsm', 'potx', 'potm', 'ppam', 'sldx', 'sldm', 'onetoc', ' onetoc2', 'onetmp', 'onepkg', 'oxps', 'xps', 'odt', 'odp', 'ods', 'odg', 'odc', 'odb', 'odf', 'wp', 'wpd', 'key', 'numbers', 'pages'];
20
+ const acceptedExtensions = ['jpg', 'jpeg', 'jpe', 'gif', 'png', 'bmp', 'svg', 'tiff', 'tif', 'ico', 'asf', 'asx', 'wmv', 'wmx', 'wm', 'avi', 'divx', 'mov', 'qt', 'mpeg', 'mpg', 'mpe', 'mp4', 'm4v', 'ogv', 'webm', 'mkv', '3gp', '3gpp', '3g2', '3gp2', 'txt', 'asc', 'c', 'cc', 'h', 'srt', 'csv', 'tsv', 'ics', 'rtx', 'css', 'vtt', 'dfxp', 'mp3', 'm4a', 'm4b', 'ra', 'ram', 'wav', 'ogg', 'oga', 'mid', 'midi', 'wma', 'wax', 'mka', 'rtf', 'js', 'pdf', 'class', 'psd', 'xcf', 'doc', 'pot', 'pps', 'ppt', 'wri', 'xla', 'xls', 'xlt', 'xlw', 'mdb', 'mpp', 'docx', 'docm', 'dotx', 'dotm', 'xlsx', 'xlsm', 'xlsb', 'xltx', 'xltm', 'xlam', 'pptx', 'pptm', 'ppsx', 'ppsm', 'potx', 'potm', 'ppam', 'sldx', 'sldm', 'onetoc', ' onetoc2', 'onetmp', 'onepkg', 'oxps', 'xps', 'odt', 'odp', 'ods', 'odg', 'odc', 'odb', 'odf', 'webp', 'wp', 'wpd', 'key', 'numbers', 'pages'];
21
21
  /**
22
22
  * Character validation global variables
23
23
  *
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automattic/vip",
3
- "version": "2.8.2",
3
+ "version": "2.9.2",
4
4
  "lockfileVersion": 1,
5
5
  "requires": true,
6
6
  "dependencies": {
@@ -5392,8 +5392,8 @@
5392
5392
  }
5393
5393
  },
5394
5394
  "cli-table": {
5395
- "version": "github:automattic/cli-table#7b14232ba779929e1859b267bf753c150d90a618",
5396
- "from": "github:automattic/cli-table#7b14232",
5395
+ "version": "git+ssh://git@github.com/automattic/cli-table.git#7b14232ba779929e1859b267bf753c150d90a618",
5396
+ "from": "cli-table@github:automattic/cli-table#7b14232",
5397
5397
  "requires": {
5398
5398
  "chalk": "^2.4.1",
5399
5399
  "wcwidth": "^1.0.1"
@@ -5667,12 +5667,6 @@
5667
5667
  "mkdir-p": "~0.0.4"
5668
5668
  }
5669
5669
  },
5670
- "core-js": {
5671
- "version": "3.12.0",
5672
- "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.12.0.tgz",
5673
- "integrity": "sha512-SaMnchL//WwU2Ot1hhkPflE8gzo7uq1FGvUJ8GKmi3TOU7rGTHIU+eir1WGf6qOtTyxdfdcp10yPdGZ59sQ3hw==",
5674
- "dev": true
5675
- },
5676
5670
  "core-js-compat": {
5677
5671
  "version": "3.12.0",
5678
5672
  "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.12.0.tgz",
@@ -6608,8 +6602,8 @@
6608
6602
  "dev": true
6609
6603
  },
6610
6604
  "eslint-config-wpvip": {
6611
- "version": "github:automattic/eslint-config-wpvip#c6605d1c3a545d43ac2149cd464ec3c38ebc58d5",
6612
- "from": "github:automattic/eslint-config-wpvip#c6605d1",
6605
+ "version": "git+ssh://git@github.com/automattic/eslint-config-wpvip.git#c6605d1c3a545d43ac2149cd464ec3c38ebc58d5",
6606
+ "from": "eslint-config-wpvip@github:automattic/eslint-config-wpvip#c6605d1",
6613
6607
  "dev": true,
6614
6608
  "requires": {
6615
6609
  "eslint-config-wpcalypso": "^4.0.0"
@@ -7571,9 +7565,9 @@
7571
7565
  "dev": true
7572
7566
  },
7573
7567
  "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==",
7568
+ "version": "0.172.0",
7569
+ "resolved": "https://registry.npmjs.org/flow-bin/-/flow-bin-0.172.0.tgz",
7570
+ "integrity": "sha512-v9KolEk3qd+wFc2ABPaSr5/7VWbHOjdAzRaWwynEtaeMcKN0awlx0Q7b71g/XgVf/fWMR+K8q+3s/TCH+Gky/Q==",
7577
7571
  "dev": true
7578
7572
  },
7579
7573
  "follow-redirects": {
@@ -10027,8 +10021,8 @@
10027
10021
  "dev": true
10028
10022
  },
10029
10023
  "lando": {
10030
- "version": "git+https://github.com/Automattic/lando-cli.git#57d2cd8a23e73b3e5165c3e2862b42aa0881ef2f",
10031
- "from": "git+https://github.com/Automattic/lando-cli.git#v3.5.1-patch2021_12_06",
10024
+ "version": "git+ssh://git@github.com/Automattic/lando-cli.git#57d2cd8a23e73b3e5165c3e2862b42aa0881ef2f",
10025
+ "from": "lando@git+https://github.com/Automattic/lando-cli.git#v3.5.1-patch2021_12_06",
10032
10026
  "requires": {
10033
10027
  "@lando/platformsh": "^0.6.0",
10034
10028
  "axios": "0.21.4",
@@ -10595,7 +10589,7 @@
10595
10589
  },
10596
10590
  "onetime": {
10597
10591
  "version": "1.1.0",
10598
- "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz",
10592
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz",
10599
10593
  "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=",
10600
10594
  "dev": true
10601
10595
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automattic/vip",
3
- "version": "2.8.2",
3
+ "version": "2.9.2",
4
4
  "description": "The VIP Javascript library & CLI",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -42,7 +42,8 @@
42
42
  },
43
43
  "scripts": {
44
44
  "test": "npm run lint && npm run flow && jest --coverage",
45
- "prepare": "npm run build",
45
+ "clean": "rimraf dist",
46
+ "prepare": "npm run clean && npm run build",
46
47
  "prepack": "npm run prepareConfig:publish",
47
48
  "postinstall": "node ./helpers/check-version.js",
48
49
  "build": "npm run prepareConfig:local && babel src -d dist",
@@ -70,7 +71,7 @@
70
71
  ],
71
72
  "license": "MIT",
72
73
  "engines": {
73
- "node": ">=10"
74
+ "node": ">=14.14.0"
74
75
  },
75
76
  "bugs": {
76
77
  "url": "https://github.com/Automattic/vip/issues"
@@ -86,7 +87,6 @@
86
87
  "babel-core": "7.0.0-bridge.0",
87
88
  "babel-jest": "26.6.3",
88
89
  "babel-plugin-module-resolver": "4.1.0",
89
- "core-js": "3.12.0",
90
90
  "eslint": "7.32.0",
91
91
  "eslint-config-wpvip": "github:automattic/eslint-config-wpvip#c6605d1",
92
92
  "eslint-plugin-flowtype": "5.7.2",
@@ -97,11 +97,12 @@
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",
104
- "publish-please": "5.5.2"
104
+ "publish-please": "5.5.2",
105
+ "rimraf": "3.0.2"
105
106
  },
106
107
  "dependencies": {
107
108
  "@apollo/client": "^3.3.6",