@automattic/vip 2.19.2 → 2.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,7 +1,43 @@
1
1
  ## Changelog
2
2
 
3
+ ### 2.21.0 (24 Oct 2022)
4
+
5
+ - #1143 [dev-env] adds default for wordpress version prompt
6
+ - #1146 fix(dev-env): Resolve the "xdebugConfig is not defined" issue
7
+ - #1142 Update Slack channel on which to (optionally) ping before release
8
+ - #1144 fix: Suppress ES security-related warnings
9
+ - #1132 [PIE-3105] Update VIP-CLI command line output
10
+ - #1118 Remove unused confirmation prompt during sql import
11
+ - #1137 [dev-env] simplify and rename elasticsearch service config
12
+ - #1138 [dev-env] Update/rename app code and mu-plugins
13
+
14
+ ### 2.20.0 (19 Oct 2022)
15
+
16
+ - #1121 Add support for custom PHP images
17
+ - #1136 SQL Import: Add validation for unique_checks being disabled
18
+ - #1131 SQL Import - Add ALTER TABLE statement validation
19
+ - #1134 [dev-env] add app and env to tracks when creating env
20
+ - #1135 [dev-env] Make xdebug config not break during wp update
21
+ - #1098 Include options into reading software config
22
+ - #1114 [dev-env] Verify current user is a docker user
23
+ - #1130 Update/exec help
24
+ - #1125 add validation output
25
+ - #1127 Run wp-cli as www-data
26
+ - #1120 Update php-fpm image names
27
+ - #1126 Update memcached to 1.6-alpine3.16
28
+ - #1124 [dev-env] Supress Setup wizard during update
29
+ - #1111 [dev-env] prompt for es index after db import
30
+ - #1119 Use --yes instead of --force
31
+ - #1115 [dev-env] reclasify most common error
32
+ - #1122 [dev-env] support xdebug_config environment variable
33
+ - #1129 Fix ESLint/Flow/SonarScan issues
34
+ - #1133 Add PHP 8.2 image
35
+ - #1141 SQL validation: Change formatting to make it more readable
36
+
37
+ https://github.com/Automattic/vip-cli/releases/tag/v2.20.0
38
+
3
39
  ### 2.19.2 (23 Sep 2022)
4
- - #1116 Reverted #1049 changes
40
+ - #1116 Reverted #1049 changes
5
41
 
6
42
  ### 2.19.0 (23 Sep 2022)
7
43
  - #1108 [dev-env] Validate import file and add debug lines
package/CONTRIBUTING.md CHANGED
@@ -50,7 +50,7 @@ Our release flow for VIP CLI follows this pattern:
50
50
  - This is a public repository. Please do not include any internal links in PRs, changelogs, testing instructions, etc.
51
51
  - Merge changes from your feature branch to the `develop` branch
52
52
  - If you are ready to release your changes publicly, merge your changes from the `develop` branch to the `master` branch. All changes that are not ready to be public should be feature flagged or stay in the `develop` branch to avoid conflicts when releasing urgent fixes (not recommended).
53
- - Finally, release your changes as a new minor or major NPM version. Ping in the #vip-platform-patisserie channel to notify folks of a new release, but please feel free to release your changes without any blockers from the team. Any team member that is part of the Automattic NPM organization can release a new version; if you aren't a member, generic credentials are available in the Secret Store.
53
+ - Finally, release your changes as a new minor or major NPM version. Ping in the #vip-platform channel to notify folks of a new release, but please feel free to release your changes without any blockers from the team. Any team member that is part of the Automattic NPM organization can release a new version; if you aren't a member, generic credentials are available in the Secret Store.
54
54
 
55
55
  ### Changelogs
56
56
  Changelogs allow customers to keep up with all the changes happening across our VIP Platform. Changelogs for VIP CLI are posted to the [VIP Cloud Changelog P2](https://wpvipchangelog.wordpress.com/), along with the repository’s `README.md`.
@@ -14,7 +14,7 @@ services:
14
14
  devtools:
15
15
  type: compose
16
16
  services:
17
- image: ghcr.io/automattic/vip-container-images/dev-tools:0.8
17
+ image: ghcr.io/automattic/vip-container-images/dev-tools:0.9
18
18
  command: sleep infinity
19
19
  volumes:
20
20
  - devtools:/dev-tools
@@ -40,9 +40,11 @@ services:
40
40
  command: run.sh
41
41
  working_dir: /wp
42
42
  environment:
43
- XDEBUG: <%= xdebug ? 'enable' : 'disable' %>
44
43
  STATSD: <%= statsd ? 'enable' : 'disable' %>
45
-
44
+ XDEBUG: <%= xdebug ? 'enable' : 'disable' %>
45
+ <% if ( xdebugConfig ) { %>
46
+ XDEBUG_CONFIG: "<%= xdebugConfig %>"
47
+ <% } %>
46
48
  LANDO_NO_USER_PERMS: 'enable'
47
49
 
48
50
 
@@ -78,7 +80,7 @@ services:
78
80
  memcached:
79
81
  type: memcached:custom
80
82
  overrides:
81
- image: memcached:1.6-alpine3.14
83
+ image: memcached:1.6-alpine3.16
82
84
  command: docker-entrypoint.sh memcached
83
85
 
84
86
  <% if ( phpmyadmin ) { %>
@@ -90,11 +92,11 @@ services:
90
92
  environment:
91
93
  UPLOAD_LIMIT: 4G
92
94
  <% } %>
93
- <% if ( elasticsearchEnabled ) { %>
94
- vip-search:
95
+ <% if ( elasticsearch ) { %>
96
+ elasticsearch:
95
97
  type: compose
96
98
  services:
97
- image: elasticsearch:<%= elasticsearch %>
99
+ image: elasticsearch:7.17.2
98
100
  command: /usr/local/bin/docker-entrypoint.sh
99
101
  environment:
100
102
  ELASTICSEARCH_IS_DEDICATED_NODE: 'no'
@@ -102,6 +104,7 @@ services:
102
104
  ELASTICSEARCH_NODE_NAME: 'lando'
103
105
  ELASTICSEARCH_PORT_NUMBER: 9200
104
106
  discovery.type: 'single-node'
107
+ xpack.security.enabled: 'false'
105
108
  ports:
106
109
  - ":9200"
107
110
  volumes:
@@ -131,7 +134,7 @@ services:
131
134
  nocopy: true
132
135
 
133
136
  <% if ( muPlugins.mode == 'image' ) { %>
134
- mu-plugins:
137
+ vip-mu-plugins:
135
138
  type: compose
136
139
  services:
137
140
  image: ghcr.io/automattic/vip-container-images/mu-plugins:0.1
@@ -148,7 +151,7 @@ services:
148
151
  <% } %>
149
152
 
150
153
  <% if ( appCode.mode == 'image' ) { %>
151
- client-code:
154
+ demo-app-code:
152
155
  type: compose
153
156
  services:
154
157
  image: ghcr.io/automattic/vip-container-images/skeleton:latest
@@ -175,15 +178,9 @@ tooling:
175
178
  wp:
176
179
  service: php
177
180
  description: "Run WP-CLI command"
178
- user: root
179
- cmd:
180
- - wp --allow-root
181
-
182
- add-site:
183
- service: php
184
- description: "Add site to a multisite installation"
181
+ user: www-data
185
182
  cmd:
186
- - bash /dev-tools/add-site.sh
183
+ - wp
187
184
 
188
185
  db:
189
186
  service: php
@@ -28,12 +28,13 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
28
28
 
29
29
  // Command examples
30
30
  const examples = [{
31
- usage: 'vip @mysite.develop config software get wordpress --format json',
32
- description: 'Read current software settings for WordPress in JSON format'
31
+ usage: 'vip @mysite.develop config software get wordpress --include available_versions --format json',
32
+ description: 'Read current software settings for WordPress in JSON format including available versions'
33
33
  }, {
34
34
  usage: 'vip @mysite.develop config software get',
35
35
  description: 'Read current software settings for all components'
36
36
  }];
37
+ const VALID_INCLUDES = ['available_versions'];
37
38
  (0, _command.default)({
38
39
  appContext: true,
39
40
  appQuery: _software.appQuery,
@@ -42,7 +43,7 @@ const examples = [{
42
43
  wildcardCommand: true,
43
44
  format: true,
44
45
  usage: 'vip @mysite.develop config software get <wordpress|php|nodejs|muplugins>'
45
- }).examples(examples).argv(process.argv, async (arg, opt) => {
46
+ }).option('include', `Extra information to be included. Valida values: ${VALID_INCLUDES.join(',')}`).examples(examples).argv(process.argv, async (arg, opt) => {
46
47
  var _opt$env;
47
48
 
48
49
  const trackingInfo = {
@@ -50,6 +51,22 @@ const examples = [{
50
51
  args: JSON.stringify(arg)
51
52
  };
52
53
  await (0, _tracker.trackEvent)('config_software_get_execute', trackingInfo);
54
+ let include = [];
55
+
56
+ if (opt.include) {
57
+ if (Array.isArray(opt.include)) {
58
+ include = opt.include;
59
+ } else {
60
+ include = [opt.include];
61
+ }
62
+
63
+ const invalidIncludes = include.filter(includeKey => !VALID_INCLUDES.includes(includeKey));
64
+
65
+ if (invalidIncludes.length > 0) {
66
+ throw new _userError.default(`Invalid include value(s): ${invalidIncludes.join(',')}`);
67
+ }
68
+ }
69
+
53
70
  const {
54
71
  softwareSettings
55
72
  } = opt.env;
@@ -72,19 +89,7 @@ const examples = [{
72
89
  chosenSettings = [softwareSettings.wordpress, softwareSettings.php, softwareSettings.muplugins, softwareSettings.nodejs];
73
90
  }
74
91
 
75
- const preFormatted = chosenSettings.filter(softwareSetting => !!softwareSetting).map(softwareSetting => {
76
- let version = softwareSetting.current.version;
77
-
78
- if (softwareSetting.slug === 'wordpress' && !softwareSetting.pinned) {
79
- version += ' (managed updates)';
80
- }
81
-
82
- return {
83
- name: softwareSetting.name,
84
- slug: softwareSetting.slug,
85
- version
86
- };
87
- });
92
+ const preFormatted = chosenSettings.filter(softwareSetting => !!softwareSetting).map(softwareSetting => (0, _software.formatSoftwareSettings)(softwareSetting, include, opt.format));
88
93
  console.log((0, _format.formatData)(preFormatted, opt.format));
89
94
  await (0, _tracker.trackEvent)('config_software_get_success', trackingInfo);
90
95
  });
@@ -51,7 +51,7 @@ const cmd = (0, _command.default)({
51
51
  usage: 'vip @mysite.develop config software update nodejs 16',
52
52
  description: 'Update Node.js to v16'
53
53
  }]);
54
- cmd.option('force', 'Auto-confirm update');
54
+ cmd.option('yes', 'Auto-confirm update');
55
55
  cmd.argv(process.argv, async (arg, opt) => {
56
56
  const {
57
57
  app,
@@ -73,7 +73,7 @@ cmd.argv(process.argv, async (arg, opt) => {
73
73
  }
74
74
 
75
75
  const updateOptions = {
76
- force: !!opt.force
76
+ force: !!opt.yes
77
77
  };
78
78
 
79
79
  if (arg.length > 0) {
@@ -66,7 +66,9 @@ cmd.argv(process.argv, async (arg, opt) => {
66
66
  await (0, _devEnvironmentCli.validateDependencies)(slug);
67
67
  debug('Args: ', arg, 'Options: ', opt);
68
68
  const trackingInfo = {
69
- slug
69
+ slug,
70
+ app: opt.app,
71
+ env: opt.env
70
72
  };
71
73
  await (0, _tracker.trackEvent)('dev_env_create_command_execute', trackingInfo);
72
74
 
@@ -29,12 +29,12 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
29
29
  const examples = [{
30
30
  usage: `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} exec -- wp post list`,
31
31
  description: 'Use dev-environment to run `wp post list`'
32
+ }, {
33
+ usage: `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} exec --slug my_site -- wp post list --posts_per_page=500`,
34
+ description: 'Use dev-environment "my-site" to run `wp post list --posts_per_page=500`'
32
35
  }, {
33
36
  usage: `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} exec --slug my_site -- wp shell`,
34
37
  description: 'Use dev-environment "my_site" to run interactive wp shell'
35
- }, {
36
- usage: `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} exec -- add-site --new-site-slug subsite --new-site-title "New Subsite"`,
37
- description: 'Execute script to add a subsite to multisite dev environment'
38
38
  }];
39
39
  (0, _command.default)({
40
40
  wildcardCommand: true
@@ -81,6 +81,17 @@ const examples = [{
81
81
 
82
82
  const cacheArg = ['wp', 'cache', 'flush'];
83
83
  await (0, _devEnvironmentCore.exec)(slug, cacheArg);
84
+
85
+ try {
86
+ await (0, _devEnvironmentCore.exec)(slug, ['wp', 'cli', 'has-command', 'vip-search']);
87
+ const doIndex = await (0, _devEnvironmentCli.promptForBoolean)('Do you want to index data in ElasticSearch (used by enterprise search)?', true);
88
+
89
+ if (doIndex) {
90
+ await (0, _devEnvironmentCore.exec)(slug, ['wp', 'vip-search', 'index', '--setup', '--network-wide', '--skip-confirm']);
91
+ }
92
+ } catch (err) {// Exception means they don't have vip-search enabled.
93
+ }
94
+
84
95
  const addUserArg = ['wp', 'dev-env-add-admin', '--username=vipgo', '--password=password'];
85
96
  await (0, _devEnvironmentCore.exec)(slug, addUserArg);
86
97
  await (0, _tracker.trackEvent)('dev_env_import_sql_command_success', trackingInfo);
@@ -62,7 +62,6 @@ cmd.argv(process.argv, async (arg, opt) => {
62
62
  appCode: currentInstanceData.appCode.dir || currentInstanceData.appCode.tag || 'latest',
63
63
  muPlugins: currentInstanceData.muPlugins.dir || currentInstanceData.muPlugins.tag || 'latest',
64
64
  wordpress: currentInstanceData.wordpress.tag,
65
- elasticsearchEnabled: currentInstanceData.elasticsearchEnabled,
66
65
  elasticsearch: currentInstanceData.elasticsearch,
67
66
  php: currentInstanceData.php || _devEnvironment.DEV_ENVIRONMENT_PHP_VERSIONS.default,
68
67
  mariadb: currentInstanceData.mariadb,
@@ -73,7 +72,11 @@ cmd.argv(process.argv, async (arg, opt) => {
73
72
  multisite: false,
74
73
  title: ''
75
74
  };
76
- const instanceData = await (0, _devEnvironmentCli.promptForArguments)(preselectedOptions, defaultOptions);
75
+ const providedOptions = Object.keys(opt).filter(option => option.length > 1) // Filter out single letter aliases
76
+ .filter(option => !['debug', 'help', 'slug'].includes(option)); // Filter out options that are not related to instance configuration
77
+
78
+ const supressPrompts = providedOptions.length > 0;
79
+ const instanceData = await (0, _devEnvironmentCli.promptForArguments)(preselectedOptions, defaultOptions, supressPrompts);
77
80
  instanceData.siteSlug = slug;
78
81
  await (0, _devEnvironmentCore.updateEnvironment)(instanceData);
79
82
  const message = '\n' + _chalk.default.green('✓') + ' environment updated. Restart environment for changes to take an affect.';
@@ -362,9 +362,7 @@ const displayPlaybook = ({
362
362
  appQuery,
363
363
  envContext: true,
364
364
  requiredArgs: 1,
365
- module: 'import-sql',
366
- requireConfirm: 'Are you sure you want to import the contents of the provided SQL file?',
367
- skipConfirmPrompt: true
365
+ module: 'import-sql'
368
366
  }).command('status', 'Check the status of the current running import').option('skip-validate', 'Do not perform pre-upload file validation. If unsupported entries are present, the import is likely to fail').option('search-replace', 'Perform Search and Replace on the specified SQL file').option('in-place', 'Search and Replace explicitly on the given input file').option('output', 'Specify the replacement output file for Search and Replace', 'process.stdout').examples(examples).argv(process.argv, async (arg, opts) => {
369
367
  var _env$primaryDomain;
370
368
 
package/dist/bin/vip.js CHANGED
@@ -65,22 +65,21 @@ const rootCmd = async function () {
65
65
  runCmd();
66
66
  } else {
67
67
  console.log();
68
- console.log(' Welcome to');
69
68
  console.log(' _ __ ________ ________ ____');
70
- console.log(' | | / // _/ __ \ / ____/ / / _/');
69
+ console.log(' | | / // _/ __ / ____/ / / _/');
71
70
  console.log(' | | / / / // /_/ /______/ / / / / / ');
72
71
  console.log(' | |/ /_/ // ____//_____/ /___/ /____/ / ');
73
- console.log(' |___//___/_/ \____/_____/___/ ');
72
+ console.log(' |___//___/_/ ____/_____/___/ ');
74
73
  console.log();
75
- console.log(' VIP CLI is your tool for interacting with and managing your VIP applications.');
74
+ console.log(' VIP-CLI is your tool for interacting with and managing your VIP applications.');
76
75
  console.log();
77
- console.log(` To get started, we need an access token for your VIP account. We'll open ${tokenURL} in your web browser; follow the instructions there to continue.`);
76
+ console.log(' Authenticate your installation of VIP-CLI with your Personal Access Token. This URL will be opened in your web browser automatically so that you can retrieve your token: ' + tokenURL);
78
77
  console.log();
79
78
  await (0, _tracker.trackEvent)('login_command_execute');
80
79
  const answer = await (0, _enquirer.prompt)({
81
80
  type: 'confirm',
82
81
  name: 'continue',
83
- message: 'Ready?'
82
+ message: 'Ready to authenticate?'
84
83
  });
85
84
 
86
85
  if (!answer.continue) {
package/dist/lib/api.js CHANGED
@@ -7,8 +7,6 @@ exports.disableGlobalGraphQLErrorHandling = disableGlobalGraphQLErrorHandling;
7
7
  exports.default = API;
8
8
  exports.API_URL = exports.API_HOST = exports.PRODUCTION_API_HOST = void 0;
9
9
 
10
- var _nodeFetch = _interopRequireDefault(require("node-fetch"));
11
-
12
10
  var _core = require("@apollo/client/core");
13
11
 
14
12
  var _core2 = require("@apollo/client/link/core");
@@ -108,9 +106,8 @@ async function API({
108
106
  agent: proxyAgent
109
107
  }
110
108
  });
111
- const apiClient = new _core.ApolloClient({
109
+ return new _core.ApolloClient({
112
110
  link: _core2.ApolloLink.from([withToken, errorLink, authLink, httpLink]),
113
111
  cache: new _core.InMemoryCache()
114
112
  });
115
- return apiClient;
116
113
  }
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.getUpdateResult = exports.triggerUpdate = exports.promptForUpdate = exports.appQueryFragments = exports.appQuery = void 0;
6
+ exports.formatSoftwareSettings = exports.getUpdateResult = exports.triggerUpdate = exports.promptForUpdate = exports.appQueryFragments = exports.appQuery = void 0;
7
7
 
8
8
  var _enquirer = require("enquirer");
9
9
 
@@ -231,7 +231,9 @@ const _processComponent = async (appTypeId, userProvidedComponent) => {
231
231
  message: 'Component to update',
232
232
  choices
233
233
  });
234
- return await select.run();
234
+ return select.run().catch(() => {
235
+ throw new _userError.default('Command cancelled by user.');
236
+ });
235
237
  };
236
238
 
237
239
  const _processComponentVersion = async (softwareSettings, component, userProvidedVersion) => {
@@ -251,7 +253,9 @@ const _processComponentVersion = async (softwareSettings, component, userProvide
251
253
  message: `Version for ${COMPONENT_NAMES[component]} to upgrade to`,
252
254
  choices: versionChoices
253
255
  });
254
- return await versionSelect.run();
256
+ return versionSelect.run().catch(() => {
257
+ throw new _userError.default('Command cancelled by user.');
258
+ });
255
259
  };
256
260
 
257
261
  const promptForUpdate = async (appTypeId, opts, softwareSettings) => {
@@ -259,7 +263,9 @@ const promptForUpdate = async (appTypeId, opts, softwareSettings) => {
259
263
  const version = await _processComponentVersion(softwareSettings, component, opts.version);
260
264
  const confirm = opts.force || (await new _enquirer.Confirm({
261
265
  message: `Are you sure you want to upgrade ${COMPONENT_NAMES[component]} to ${version}?`
262
- }).run());
266
+ }).run().catch(() => {
267
+ throw new _userError.default('Command cancelled by user.');
268
+ }));
263
269
 
264
270
  if (confirm) {
265
271
  return {
@@ -276,7 +282,7 @@ exports.promptForUpdate = promptForUpdate;
276
282
  const triggerUpdate = async variables => {
277
283
  debug('Triggering update', variables);
278
284
  const api = await (0, _api.default)();
279
- return await api.mutate({
285
+ return api.mutate({
280
286
  mutation: updateSoftwareMutation,
281
287
  variables
282
288
  });
@@ -342,4 +348,30 @@ const getUpdateResult = async (appId, envId) => {
342
348
  };
343
349
  };
344
350
 
345
- exports.getUpdateResult = getUpdateResult;
351
+ exports.getUpdateResult = getUpdateResult;
352
+
353
+ const formatSoftwareSettings = (softwareSetting, includes, format) => {
354
+ let version = softwareSetting.current.version;
355
+
356
+ if (softwareSetting.slug === 'wordpress' && !softwareSetting.pinned) {
357
+ version += ' (managed updates)';
358
+ }
359
+
360
+ const result = {
361
+ name: softwareSetting.name,
362
+ slug: softwareSetting.slug,
363
+ version
364
+ };
365
+
366
+ if (includes.includes('available_versions')) {
367
+ result.available_versions = _optionsForVersion(softwareSetting).map(option => option.value);
368
+
369
+ if (format !== 'json') {
370
+ result.available_versions = result.available_versions.join(',');
371
+ }
372
+ }
373
+
374
+ return result;
375
+ };
376
+
377
+ exports.formatSoftwareSettings = formatSoftwareSettings;
@@ -11,7 +11,6 @@ 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.17.2',
15
14
  mariadbVersion: '10.3',
16
15
  phpVersion: '8.0'
17
16
  };
@@ -33,9 +32,11 @@ const DEV_ENVIRONMENT_WORDPRESS_VERSION_TTL = 86400; // once per day
33
32
  exports.DEV_ENVIRONMENT_WORDPRESS_VERSION_TTL = DEV_ENVIRONMENT_WORDPRESS_VERSION_TTL;
34
33
  const DEV_ENVIRONMENT_PHP_VERSIONS = {
35
34
  // eslint-disable-next-line quote-props
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'
35
+ '8.2': 'ghcr.io/automattic/vip-container-images/php-fpm:8.2',
36
+ // eslint-disable-next-line quote-props
37
+ '8.1': 'ghcr.io/automattic/vip-container-images/php-fpm:8.1',
38
+ '8.0': 'ghcr.io/automattic/vip-container-images/php-fpm:8.0',
39
+ // eslint-disable-next-line quote-props -- flow does not support non-string keys
40
+ '7.4': 'ghcr.io/automattic/vip-container-images/php-fpm:7.4'
40
41
  };
41
42
  exports.DEV_ENVIRONMENT_PHP_VERSIONS = DEV_ENVIRONMENT_PHP_VERSIONS;
@@ -35,7 +35,7 @@ var _path = _interopRequireDefault(require("path"));
35
35
 
36
36
  var _os = _interopRequireDefault(require("os"));
37
37
 
38
- var exit = _interopRequireWildcard(require("../cli/exit"));
38
+ var _progress = require("../cli/progress");
39
39
 
40
40
  var _tracker = require("../tracker");
41
41
 
@@ -47,10 +47,6 @@ var _devEnvironmentLando = require("./dev-environment-lando");
47
47
 
48
48
  var _userError = _interopRequireDefault(require("../user-error"));
49
49
 
50
- 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); }
51
-
52
- 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; }
53
-
54
50
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
55
51
 
56
52
  /**
@@ -131,14 +127,48 @@ const verifyDNSResolution = slug => {
131
127
  });
132
128
  };
133
129
 
130
+ const VALIDATION_STEPS = [{
131
+ id: 'docker',
132
+ name: 'Check for docker installation'
133
+ }, {
134
+ id: 'compose',
135
+ name: 'Check for docker-compose installation'
136
+ }, {
137
+ id: 'access',
138
+ name: 'Check access to docker for current user'
139
+ }, {
140
+ id: 'dns',
141
+ name: 'Check DNS resolution'
142
+ }];
143
+
134
144
  const validateDependencies = async slug => {
145
+ const progressTracker = new _progress.ProgressTracker(VALIDATION_STEPS);
146
+ console.log('Running validation steps...');
147
+ progressTracker.startPrinting();
148
+ progressTracker.stepRunning('docker');
149
+
135
150
  try {
136
151
  await (0, _devEnvironmentLando.validateDockerInstalled)();
137
152
  } catch (exception) {
138
153
  throw new _userError.default(exception.message);
139
154
  }
140
155
 
156
+ progressTracker.stepSuccess('docker');
157
+ progressTracker.stepSuccess('compose');
158
+ progressTracker.print();
159
+
160
+ try {
161
+ await (0, _devEnvironmentLando.validateDockerAccess)();
162
+ } catch (exception) {
163
+ throw new _userError.default(exception.message);
164
+ }
165
+
166
+ progressTracker.stepSuccess('access');
167
+ progressTracker.print();
141
168
  await verifyDNSResolution(slug);
169
+ progressTracker.stepSuccess('dns');
170
+ progressTracker.print();
171
+ progressTracker.stopPrinting();
142
172
  };
143
173
 
144
174
  exports.validateDependencies = validateDependencies;
@@ -213,13 +243,22 @@ function getOptionsFromAppInfo(appInfo) {
213
243
  * Prompt for arguments
214
244
  * @param {InstanceOptions} preselectedOptions - options to be used without prompt
215
245
  * @param {InstanceOptions} defaultOptions - options to be used as default values for prompt
246
+ * @param {boolean} supressPrompts - supress prompts and use default values where needed
216
247
  * @returns {any} instance data
217
248
  */
218
249
 
219
250
 
220
- async function promptForArguments(preselectedOptions, defaultOptions) {
251
+ async function promptForArguments(preselectedOptions, defaultOptions, supressPrompts = false) {
221
252
  debug('Provided preselected', preselectedOptions, 'and default', defaultOptions);
222
- console.log(_devEnvironment.DEV_ENVIRONMENT_PROMPT_INTRO);
253
+
254
+ if (supressPrompts) {
255
+ preselectedOptions = { ...defaultOptions,
256
+ ...preselectedOptions
257
+ };
258
+ } else {
259
+ console.log(_devEnvironment.DEV_ENVIRONMENT_PROMPT_INTRO);
260
+ }
261
+
223
262
  let multisiteText = 'Multisite';
224
263
  let multisiteDefault = _devEnvironment.DEV_ENVIRONMENT_DEFAULTS.multisite;
225
264
 
@@ -231,8 +270,7 @@ async function promptForArguments(preselectedOptions, defaultOptions) {
231
270
  const instanceData = {
232
271
  wpTitle: preselectedOptions.title || (await promptForText('WordPress site title', defaultOptions.title || _devEnvironment.DEV_ENVIRONMENT_DEFAULTS.title)),
233
272
  multisite: 'multisite' in preselectedOptions ? preselectedOptions.multisite : await promptForBoolean(multisiteText, !!multisiteDefault),
234
- elasticsearchEnabled: false,
235
- elasticsearch: preselectedOptions.elasticsearch || defaultOptions.elasticsearch || _devEnvironment.DEV_ENVIRONMENT_DEFAULTS.elasticsearchVersion,
273
+ elasticsearch: false,
236
274
  php: preselectedOptions.php ? resolvePhpVersion(preselectedOptions.php) : await promptForPhpVersion(resolvePhpVersion(defaultOptions.php || _devEnvironment.DEV_ENVIRONMENT_DEFAULTS.phpVersion)),
237
275
  mariadb: preselectedOptions.mariadb || defaultOptions.mariadb || _devEnvironment.DEV_ENVIRONMENT_DEFAULTS.mariadbVersion,
238
276
  mediaRedirectDomain: preselectedOptions.mediaRedirectDomain || '',
@@ -248,6 +286,7 @@ async function promptForArguments(preselectedOptions, defaultOptions) {
248
286
  statsd: false,
249
287
  phpmyadmin: false,
250
288
  xdebug: false,
289
+ xdebugConfig: preselectedOptions.xdebugConfig,
251
290
  siteSlug: ''
252
291
  };
253
292
  const promptLabels = {
@@ -283,12 +322,12 @@ async function promptForArguments(preselectedOptions, defaultOptions) {
283
322
  debug(`Processing elasticsearch with preselected "${preselectedOptions.elasticsearch}"`);
284
323
 
285
324
  if ('elasticsearch' in preselectedOptions) {
286
- instanceData.elasticsearchEnabled = !!preselectedOptions.elasticsearch;
325
+ instanceData.elasticsearch = !!preselectedOptions.elasticsearch;
287
326
  } else {
288
- instanceData.elasticsearchEnabled = await promptForBoolean('Enable Elasticsearch (needed by Enterprise Search)?', defaultOptions.elasticsearchEnabled);
327
+ instanceData.elasticsearch = await promptForBoolean('Enable Elasticsearch (needed by Enterprise Search)?', defaultOptions.elasticsearch);
289
328
  }
290
329
 
291
- if (instanceData.elasticsearchEnabled) {
330
+ if (instanceData.elasticsearch) {
292
331
  instanceData.statsd = preselectedOptions.statsd || defaultOptions.statsd || false;
293
332
  } else {
294
333
  instanceData.statsd = false;
@@ -299,7 +338,7 @@ async function promptForArguments(preselectedOptions, defaultOptions) {
299
338
  if (service in preselectedOptions) {
300
339
  instanceData[service] = preselectedOptions[service];
301
340
  } else {
302
- instanceData[service] = await promptForBoolean(`Enable ${promptLabels[service] || service}`, instanceData[service]);
341
+ instanceData[service] = await promptForBoolean(`Enable ${promptLabels[service] || service}`, defaultOptions[service]);
303
342
  }
304
343
  }
305
344
  }
@@ -363,6 +402,7 @@ function validateLocalPath(component, providedPath) {
363
402
  }
364
403
 
365
404
  if (missingFiles.length > 0) {
405
+ // eslint-disable-next-line max-len
366
406
  const message = `Provided path "${providedPath}" is missing following files/folders: ${missingFiles.join(', ')}. Learn more: https://docs.wpvip.com/technical-references/vip-codebase/#1-wordpress`;
367
407
  return {
368
408
  result: false,
@@ -420,6 +460,11 @@ async function promptForBoolean(message, initial) {
420
460
 
421
461
  function resolvePhpVersion(version) {
422
462
  debug(`Resolving PHP version '${version}'`);
463
+
464
+ if (typeof version === 'string' && version.startsWith('image:')) {
465
+ return version;
466
+ }
467
+
423
468
  const versions = Object.keys(_devEnvironment.DEV_ENVIRONMENT_PHP_VERSIONS);
424
469
  const images = Object.values(_devEnvironment.DEV_ENVIRONMENT_PHP_VERSIONS); // eslint-disable-next-line eqeqeq -- use loose comparison because commander resolves '8.0' to '8'
425
470
 
@@ -513,7 +558,8 @@ async function promptForComponent(component, allowLocal, defaultObject) {
513
558
  const tagChoices = await getTagChoices();
514
559
  const selectTag = new _enquirer.Select({
515
560
  message,
516
- choices: tagChoices
561
+ choices: tagChoices,
562
+ initial: (defaultObject === null || defaultObject === void 0 ? void 0 : defaultObject.tag) || ''
517
563
  });
518
564
  const option = await selectTag.run();
519
565
  return {
@@ -537,19 +583,11 @@ function processBooleanOption(value) {
537
583
  return false;
538
584
  }
539
585
 
540
- if (FALSE_OPTIONS.includes((_value$toLowerCase = value.toLowerCase) === null || _value$toLowerCase === void 0 ? void 0 : _value$toLowerCase.call(value))) {
541
- return false;
542
- }
543
-
544
- return true;
586
+ return !FALSE_OPTIONS.includes((_value$toLowerCase = value.toLowerCase) === null || _value$toLowerCase === void 0 ? void 0 : _value$toLowerCase.call(value));
545
587
  }
546
588
 
547
589
  function addDevEnvConfigurationOptions(command) {
548
- return command.option('wordpress', 'Use a specific WordPress version').option(['u', 'mu-plugins'], 'Use a specific mu-plugins changeset or local directory').option('app-code', 'Use the application code from a local directory or use "demo" for VIP skeleton code').option('statsd', 'Enable statsd component. By default it is disabled', undefined, processBooleanOption).option('phpmyadmin', 'Enable PHPMyAdmin component. By default it is disabled', undefined, processBooleanOption).option('xdebug', 'Enable XDebug. By default it is disabled', undefined, processBooleanOption).option('elasticsearch', 'Explicitly choose Elasticsearch version to use or false to disable it', undefined, value => {
549
- var _value$toLowerCase2;
550
-
551
- return FALSE_OPTIONS.includes(value === null || value === void 0 ? void 0 : (_value$toLowerCase2 = value.toLowerCase) === null || _value$toLowerCase2 === void 0 ? void 0 : _value$toLowerCase2.call(value)) ? false : value;
552
- }).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.').option('php', 'Explicitly choose PHP version to use');
590
+ return command.option('wordpress', 'Use a specific WordPress version').option(['u', 'mu-plugins'], 'Use a specific mu-plugins changeset or local directory').option('app-code', 'Use the application code from a local directory or use "demo" for VIP skeleton code').option('statsd', 'Enable statsd component. By default it is disabled', undefined, processBooleanOption).option('phpmyadmin', 'Enable PHPMyAdmin component. By default it is disabled', undefined, processBooleanOption).option('xdebug', 'Enable XDebug. By default it is disabled', undefined, processBooleanOption).option('xdebug_config', 'Extra configuration to pass to xdebug via XDEBUG_CONFIG environment variable').option('elasticsearch', 'Enable Elasticsearch (needed by Enterprise Search)', undefined, processBooleanOption).option('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.').option('php', 'Explicitly choose PHP version to use');
553
591
  }
554
592
  /**
555
593
  * Provides the list of tag choices for selection
@@ -164,8 +164,17 @@ function preProcessInstanceData(instanceData) {
164
164
  newInstanceData.mediaRedirectDomain = `https://${instanceData.mediaRedirectDomain}`;
165
165
  }
166
166
 
167
- newInstanceData.elasticsearchEnabled = instanceData.elasticsearchEnabled || false;
167
+ newInstanceData.elasticsearch = instanceData.elasticsearch || false;
168
168
  newInstanceData.php = instanceData.php || _devEnvironment.DEV_ENVIRONMENT_PHP_VERSIONS.default;
169
+
170
+ if (newInstanceData.php.startsWith('image:')) {
171
+ newInstanceData.php = newInstanceData.php.slice('image:'.length);
172
+ }
173
+
174
+ if (!newInstanceData.xdebugConfig) {
175
+ newInstanceData.xdebugConfig = '';
176
+ }
177
+
169
178
  return newInstanceData;
170
179
  }
171
180
 
@@ -258,12 +267,7 @@ async function exec(slug, args, options = {}) {
258
267
  }
259
268
 
260
269
  const command = args.shift();
261
- let commandArgs = [...args];
262
-
263
- if ('add-site' === command) {
264
- commandArgs = [...args.map(argument => argument.replace('--new-site-', '--'))];
265
- }
266
-
270
+ const commandArgs = [...args];
267
271
  await (0, _devEnvironmentLando.landoExec)(instancePath, command, commandArgs, options);
268
272
  }
269
273
 
@@ -289,9 +293,9 @@ function readEnvironmentData(slug) {
289
293
  ***********************************/
290
294
  // REMOVEME after the wheel of time spins around few times
291
295
 
292
- if (instanceData.enterpriseSearchEnabled) {
293
- // enterpriseSearchEnabled was renamed to elasticsearchEnabled
294
- instanceData.elasticsearchEnabled = instanceData.enterpriseSearchEnabled;
296
+ if (instanceData.enterpriseSearchEnabled || instanceData.elasticsearchEnabled) {
297
+ // enterpriseSearchEnabled and elasticsearchEnabled was renamed to elasticsearch
298
+ instanceData.elasticsearch = instanceData.enterpriseSearchEnabled || instanceData.elasticsearchEnabled;
295
299
  } // REMOVEME after the wheel of time spins around few times
296
300
 
297
301
 
@@ -10,6 +10,7 @@ exports.landoDestroy = landoDestroy;
10
10
  exports.landoInfo = landoInfo;
11
11
  exports.landoExec = landoExec;
12
12
  exports.validateDockerInstalled = validateDockerInstalled;
13
+ exports.validateDockerAccess = validateDockerAccess;
13
14
 
14
15
  var _debug = _interopRequireDefault(require("debug"));
15
16
 
@@ -27,6 +28,8 @@ var _chalk = _interopRequireDefault(require("chalk"));
27
28
 
28
29
  var _app = _interopRequireDefault(require("lando/lib/app"));
29
30
 
31
+ var _userError = _interopRequireDefault(require("../user-error"));
32
+
30
33
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
31
34
 
32
35
  /**
@@ -283,7 +286,7 @@ async function landoExec(instancePath, toolName, args, options) {
283
286
  const isUp = await isEnvUp(app);
284
287
 
285
288
  if (!isUp) {
286
- throw new Error('environment needs to be started before running wp command');
289
+ throw new _userError.default('Environment needs to be started before running wp command');
287
290
  }
288
291
  }
289
292
 
@@ -359,4 +362,17 @@ async function validateDockerInstalled() {
359
362
  if (lando.engine.composeInstalled === false) {
360
363
  throw Error('docker-compose could not be located! Please follow the following instructions to install it - https://docs.docker.com/compose/install/');
361
364
  }
365
+ }
366
+
367
+ async function validateDockerAccess() {
368
+ const lando = new _lando.default(getLandoConfig());
369
+ await lando.bootstrap();
370
+ const docker = lando.engine.docker;
371
+ lando.log.verbose('Fetching docker info to verify user is in docker group');
372
+
373
+ try {
374
+ await docker.info();
375
+ } catch (error) {
376
+ throw Error('Failed to connect to docker. Please verify that the current user is part of docker group and has access to docker commands.');
377
+ }
362
378
  }
@@ -254,6 +254,28 @@ const checks = {
254
254
  excerpt: "'CREATE TABLE' should be present (case-insensitive)",
255
255
  recommendation: 'Check import settings to include CREATE TABLE statements'
256
256
  },
257
+ alterTable: {
258
+ matcher: /^ALTER TABLE `?([a-z0-9_]*)/i,
259
+ matchHandler: lineNumber => ({
260
+ lineNumber
261
+ }),
262
+ outputFormatter: lineNumberCheckFormatter,
263
+ results: [],
264
+ message: 'ALTER TABLE statement',
265
+ excerpt: "'ALTER TABLE' should not be present (case-insensitive)",
266
+ recommendation: 'Remove these lines and define table structure in the CREATE TABLE statement instead'
267
+ },
268
+ uniqueChecks: {
269
+ matcher: /^SET UNIQUE_CHECKS\s*=\s*0/i,
270
+ matchHandler: lineNumber => ({
271
+ lineNumber
272
+ }),
273
+ outputFormatter: lineNumberCheckFormatter,
274
+ results: [],
275
+ message: 'SET UNIQUE_CHECKS = 0',
276
+ excerpt: "'SET UNIQUE_CHECKS = 0' should not be present",
277
+ recommendation: "Disabling 'UNIQUE_CHECKS' is not allowed. These lines should be removed"
278
+ },
257
279
  siteHomeUrl: {
258
280
  matcher: "'(siteurl|home)',\\s?'(.*?)'",
259
281
  matchHandler: (lineNumber, results) => ({
@@ -386,7 +408,7 @@ const postValidation = async options => {
386
408
  is_import: options.isImport,
387
409
  error: errorSummary
388
410
  });
389
- const errorOutput = [`SQL validation failed due to ${_chalk.default.red(problemsFound)} error(s)`, ''];
411
+ const errorOutput = [];
390
412
  formattedErrors.forEach(error => {
391
413
  errorOutput.push(error.error);
392
414
 
@@ -396,6 +418,7 @@ const postValidation = async options => {
396
418
 
397
419
  errorOutput.push('');
398
420
  });
421
+ errorOutput.push(_chalk.default.bold.red(`SQL validation failed due to ${problemsFound} error(s)`));
399
422
 
400
423
  if (options.isImport) {
401
424
  throw new Error(errorOutput.join('\n'));
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automattic/vip",
3
- "version": "2.19.2",
3
+ "version": "2.21.0",
4
4
  "lockfileVersion": 1,
5
5
  "requires": true,
6
6
  "dependencies": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automattic/vip",
3
- "version": "2.19.2",
3
+ "version": "2.21.0",
4
4
  "description": "The VIP Javascript library & CLI",
5
5
  "main": "index.js",
6
6
  "bin": {
Binary file