@automattic/vip 2.13.1 → 2.16.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.
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ *
5
+ * @format
6
+ */
7
+
8
+ /**
9
+ * External dependencies
10
+ */
11
+
12
+ /**
13
+ * Internal dependencies
14
+ */
15
+ "use strict";
16
+
17
+ var _command = _interopRequireDefault(require("../lib/cli/command"));
18
+
19
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
20
+
21
+ (0, _command.default)({
22
+ requiredArgs: 1
23
+ }).command('get', 'Read current software settings').argv(process.argv);
@@ -15,6 +15,6 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
15
15
 
16
16
  (0, _command.default)({
17
17
  requiredArgs: 2
18
- }).command('envvar', 'Manage environment variables for an application environment').argv(process.argv, async () => {
18
+ }).command('envvar', 'Manage environment variables for an application environment').command('software', 'Software management').argv(process.argv, async () => {
19
19
  process.exit(0);
20
20
  });
@@ -49,26 +49,28 @@ const examples = [{
49
49
  usage: `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} create --slug=test`,
50
50
  description: 'Assigning unique slugs to environments allows multiple environments to be created.'
51
51
  }, {
52
- usage: `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} create --multisite --wordpress="5.8" --client-code="~/git/my_code"`,
53
- description: 'Creates a local multisite dev environment using WP 5.8 and client code is expected to be in "~/git/my_code"'
52
+ usage: `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} create --multisite --wordpress="5.8" --app-code="~/git/my_code"`,
53
+ description: 'Creates a local multisite dev environment using WP 5.8 and application code is expected to be in "~/git/my_code"'
54
54
  }];
55
- 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 => {
56
- var _value$toLowerCase;
57
-
58
- return 'false' !== (value === null || value === void 0 ? void 0 : (_value$toLowerCase = value.toLowerCase) === null || _value$toLowerCase === void 0 ? void 0 : _value$toLowerCase.call(value));
59
- });
55
+ 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, _devEnvironmentCli.processBooleanOption);
60
56
  (0, _devEnvironmentCli.addDevEnvConfigurationOptions)(cmd);
61
57
  cmd.examples(examples);
62
58
  cmd.argv(process.argv, async (arg, opt) => {
63
59
  await (0, _devEnvironmentCli.validateDependencies)();
64
- const slug = (0, _devEnvironmentCli.getEnvironmentName)(opt);
60
+ const environmentNameOptions = {
61
+ slug: opt.slug,
62
+ app: opt.app,
63
+ env: opt.env,
64
+ allowAppEnv: true
65
+ };
66
+ const slug = (0, _devEnvironmentCli.getEnvironmentName)(environmentNameOptions);
65
67
  debug('Args: ', arg, 'Options: ', opt);
66
68
  const trackingInfo = {
67
69
  slug
68
70
  };
69
71
  await (0, _tracker.trackEvent)('dev_env_create_command_execute', trackingInfo);
70
72
 
71
- const startCommand = _chalk.default.bold((0, _devEnvironmentCli.getEnvironmentStartCommand)(opt));
73
+ const startCommand = _chalk.default.bold((0, _devEnvironmentCli.getEnvironmentStartCommand)(slug));
72
74
 
73
75
  const environmentAlreadyExists = (0, _devEnvironmentCore.doesEnvironmentExist)(slug);
74
76
 
@@ -38,7 +38,11 @@ const examples = [{
38
38
  }];
39
39
  (0, _command.default)({
40
40
  wildcardCommand: true
41
- }).option('slug', 'Custom name of the dev environment').examples(examples).argv(process.argv, async (unmatchedArgs, opt) => {
41
+ }).option('slug', 'Custom name of the dev environment').option('force', 'Disabling validations before task execution', undefined, value => {
42
+ var _value$toLowerCase;
43
+
44
+ return 'false' !== (value === null || value === void 0 ? void 0 : (_value$toLowerCase = value.toLowerCase) === null || _value$toLowerCase === void 0 ? void 0 : _value$toLowerCase.call(value));
45
+ }).examples(examples).argv(process.argv, async (unmatchedArgs, opt) => {
42
46
  await (0, _devEnvironmentCli.validateDependencies)();
43
47
  const slug = (0, _devEnvironmentCli.getEnvironmentName)(opt);
44
48
  const trackingInfo = (0, _devEnvironmentCli.getEnvTrackingInfo)(slug);
@@ -59,7 +63,10 @@ const examples = [{
59
63
  arg = process.argv.slice(argSplitterIx + 1);
60
64
  }
61
65
 
62
- await (0, _devEnvironmentCore.exec)(slug, arg);
66
+ const options = {
67
+ force: opt.force
68
+ };
69
+ await (0, _devEnvironmentCore.exec)(slug, arg, options);
63
70
  await (0, _tracker.trackEvent)('dev_env_exec_command_success', trackingInfo);
64
71
  } catch (error) {
65
72
  (0, _devEnvironmentCli.handleCLIException)(error, 'dev_env_exec_command_error', trackingInfo);
@@ -44,7 +44,7 @@ const examples = [{
44
44
  }];
45
45
  (0, _command.default)({
46
46
  requiredArgs: 1
47
- }).option('slug', 'Custom name of the dev environment').option('search-replace', 'Perform Search and Replace on the specified SQL file').option('in-place', 'Search and Replace explicitly on the given input file').option('skip-validate', 'Do not perform file validation.').examples(examples).argv(process.argv, async (unmatchedArgs, opt) => {
47
+ }).option('slug', 'Custom name of the dev environment').option(['r', 'search-replace'], 'Perform Search and Replace on the specified SQL file').option('in-place', 'Search and Replace explicitly on the given input file').option('skip-validate', 'Do not perform file validation.').examples(examples).argv(process.argv, async (unmatchedArgs, opt) => {
48
48
  await (0, _devEnvironmentCli.validateDependencies)();
49
49
  const [fileName] = unmatchedArgs;
50
50
  const {
@@ -31,9 +31,6 @@ const debug = (0, _debug.default)('@automattic/vip:bin:dev-environment');
31
31
  const examples = [{
32
32
  usage: `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} info --all`,
33
33
  description: 'Return information about all local dev environments'
34
- }, {
35
- usage: `vip @123 ${_devEnvironment.DEV_ENVIRONMENT_SUBCOMMAND} info`,
36
- description: 'Return information about dev environment for site 123'
37
34
  }, {
38
35
  usage: `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} info --slug=my_site`,
39
36
  description: 'Return information about a local dev environment named "my_site"'
@@ -38,7 +38,7 @@ const examples = [{
38
38
  usage: `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} start`,
39
39
  description: 'Starts a local dev environment'
40
40
  }];
41
- (0, _command.default)().option('slug', 'Custom name of the dev environment').option('skip-rebuild', 'Only start stopped services').examples(examples).argv(process.argv, async (arg, opt) => {
41
+ (0, _command.default)().option('slug', 'Custom name of the dev environment').option('skip-rebuild', 'Only start stopped services').option(['w', 'skip-wp-versions-check'], 'Skip propting for wordpress update if non latest').examples(examples).argv(process.argv, async (arg, opt) => {
42
42
  await (0, _devEnvironmentCli.validateDependencies)();
43
43
  const startProcessing = new Date();
44
44
  const slug = (0, _devEnvironmentCli.getEnvironmentName)(opt);
@@ -46,7 +46,8 @@ const examples = [{
46
46
  await (0, _tracker.trackEvent)('dev_env_start_command_execute', trackingInfo);
47
47
  debug('Args: ', arg, 'Options: ', opt);
48
48
  const options = {
49
- skipRebuild: !!opt.skipRebuild
49
+ skipRebuild: !!opt.skipRebuild,
50
+ skipWpVersionsCheck: !!opt.skipWpVersionsCheck
50
51
  };
51
52
 
52
53
  try {
@@ -59,16 +59,16 @@ cmd.argv(process.argv, async (arg, opt) => {
59
59
  ...opt
60
60
  };
61
61
  const defaultOptions = {
62
- clientCode: currentInstanceData.clientCode.dir || currentInstanceData.clientCode.tag || 'latest',
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,
65
66
  elasticsearch: currentInstanceData.elasticsearch,
66
67
  php: currentInstanceData.php || _devEnvironment.DEV_ENVIRONMENT_PHP_VERSIONS.default,
67
68
  mariadb: currentInstanceData.mariadb,
68
69
  statsd: currentInstanceData.statsd,
69
70
  phpmyadmin: currentInstanceData.phpmyadmin,
70
71
  xdebug: currentInstanceData.xdebug,
71
- enterpriseSearchEnabled: currentInstanceData.enterpriseSearchEnabled,
72
72
  mediaRedirectDomain: currentInstanceData.mediaRedirectDomain,
73
73
  multisite: false,
74
74
  title: ''
@@ -249,7 +249,8 @@ async function validateAndGetTableNames({
249
249
  skipValidate,
250
250
  appId,
251
251
  envId,
252
- fileNameToUpload
252
+ fileNameToUpload,
253
+ searchReplace
253
254
  }) {
254
255
  const validations = [_sql.staticSqlValidations, _siteType.siteTypeValidations];
255
256
 
@@ -259,10 +260,10 @@ async function validateAndGetTableNames({
259
260
  }
260
261
 
261
262
  try {
262
- await (0, _lineByLine.fileLineValidations)(appId, envId, fileNameToUpload, validations);
263
+ await (0, _lineByLine.fileLineValidations)(appId, envId, fileNameToUpload, validations, searchReplace);
263
264
  } catch (validateErr) {
264
265
  console.log('');
265
- exit.withError(`${validateErr.message}
266
+ exit.withError(`${validateErr.message}\n
266
267
  If you are confident the file does not contain unsupported statements, you can retry the command with the ${_chalk.default.yellow('--skip-validate')} option.
267
268
  `);
268
269
  } // this can only be called after static validation of the SQL file
@@ -407,7 +408,8 @@ const displayPlaybook = ({
407
408
  skipValidate,
408
409
  appId,
409
410
  envId,
410
- fileNameToUpload
411
+ fileNameToUpload,
412
+ searchReplace
411
413
  }); // display playbook of what will happen during execution
412
414
 
413
415
  displayPlaybook({
@@ -18,11 +18,15 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
18
18
  /**
19
19
  * Internal dependencies
20
20
  */
21
- async function _default(app, fields) {
21
+ async function _default(app, fields, fragments) {
22
22
  if (!fields) {
23
23
  fields = 'id,name';
24
24
  }
25
25
 
26
+ if (!fragments) {
27
+ fragments = '';
28
+ }
29
+
26
30
  const api = await (0, _api.default)();
27
31
 
28
32
  if (isNaN(app)) {
@@ -36,7 +40,8 @@ async function _default(app, fields) {
36
40
  ${fields}
37
41
  }
38
42
  }
39
- }`,
43
+ }
44
+ ${fragments || ''}`,
40
45
  variables: {
41
46
  name: app
42
47
  }
@@ -56,7 +61,8 @@ async function _default(app, fields) {
56
61
  app( id: $id ){
57
62
  ${fields}
58
63
  }
59
- }`,
64
+ }
65
+ ${fragments || ''}`,
60
66
  variables: {
61
67
  id: app
62
68
  }
@@ -39,6 +39,8 @@ var exit = _interopRequireWildcard(require("./exit"));
39
39
 
40
40
  var _debug = _interopRequireDefault(require("debug"));
41
41
 
42
+ var _userError = _interopRequireDefault(require("./userError"));
43
+
42
44
  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); }
43
45
 
44
46
  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; }
@@ -56,6 +58,10 @@ function uncaughtError(err) {
56
58
  return;
57
59
  }
58
60
 
61
+ if (err instanceof _userError.default) {
62
+ exit.withError(err.message);
63
+ }
64
+
59
65
  console.log(_chalk.default.red('✕'), 'Please contact VIP Support with the following information:');
60
66
  console.log(_chalk.default.dim(err.stack));
61
67
  exit.withError('Unexpected error');
@@ -189,7 +195,8 @@ _args.default.argv = async function (argv, cb) {
189
195
  ${_opts.appQuery}
190
196
  }
191
197
  }
192
- }`,
198
+ }
199
+ ${_opts.appQueryFragments || ''}`,
193
200
  variables: {
194
201
  first: 100,
195
202
  after: null // TODO make dynamic?
@@ -251,7 +258,7 @@ _args.default.argv = async function (argv, cb) {
251
258
  let appLookup;
252
259
 
253
260
  try {
254
- appLookup = await (0, _app.default)(options.app, _opts.appQuery);
261
+ appLookup = await (0, _app.default)(options.app, _opts.appQuery, _opts.appQueryFragments);
255
262
  } catch (err) {
256
263
  await (0, _tracker.trackEvent)('command_appcontext_param_error', {
257
264
  error: 'App lookup failed'
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+
8
+ function UserError(message) {
9
+ this.message = message;
10
+ }
11
+
12
+ UserError.prototype = new Error();
13
+ var _default = UserError;
14
+ exports.default = _default;
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.appQueryFragments = exports.appQuery = void 0;
7
+ const appQuery = `
8
+ id,
9
+ name,
10
+ type,
11
+ organization { id, name },
12
+ environments{
13
+ appId
14
+ id
15
+ name
16
+ type
17
+ softwareSettings {
18
+ php {
19
+ ...Software
20
+ }
21
+ wordpress {
22
+ ...Software
23
+ }
24
+ muplugins {
25
+ ...Software
26
+ }
27
+ nodejs {
28
+ ...Software
29
+ }
30
+ }
31
+ }`;
32
+ exports.appQuery = appQuery;
33
+ const appQueryFragments = `fragment Software on AppEnvironmentSoftwareSettingsSoftware {
34
+ name
35
+ slug
36
+ pinned
37
+ current {
38
+ version
39
+ default
40
+ deprecated
41
+ unstable
42
+ compatible
43
+ latestRelease
44
+ private
45
+ }
46
+ options {
47
+ version
48
+ default
49
+ deprecated
50
+ unstable
51
+ compatible
52
+ latestRelease
53
+ private
54
+ }
55
+ }
56
+ `;
57
+ exports.appQueryFragments = appQueryFragments;
@@ -11,16 +11,16 @@ exports.DEV_ENVIRONMENT_FULL_COMMAND = DEV_ENVIRONMENT_FULL_COMMAND;
11
11
  const DEV_ENVIRONMENT_DEFAULTS = {
12
12
  title: 'VIP Dev',
13
13
  multisite: false,
14
- elasticsearchVersion: '7.10.1',
14
+ elasticsearchVersion: '7.17.2',
15
15
  mariadbVersion: '10.3',
16
- phpImage: 'default'
16
+ phpVersion: '8.0'
17
17
  };
18
18
  exports.DEV_ENVIRONMENT_DEFAULTS = DEV_ENVIRONMENT_DEFAULTS;
19
19
  const DEV_ENVIRONMENT_PROMPT_INTRO = 'This is a wizard to help you set up your local dev environment.\n\n' + 'Sensible default values were pre-selected for convenience. ' + 'You may also choose to create multiple environments with different settings using the --slug option.\n\n';
20
20
  exports.DEV_ENVIRONMENT_PROMPT_INTRO = DEV_ENVIRONMENT_PROMPT_INTRO;
21
21
  const DEV_ENVIRONMENT_NOT_FOUND = 'Environment not found.';
22
22
  exports.DEV_ENVIRONMENT_NOT_FOUND = DEV_ENVIRONMENT_NOT_FOUND;
23
- const DEV_ENVIRONMENT_COMPONENTS = ['wordpress', 'muPlugins', 'clientCode'];
23
+ const DEV_ENVIRONMENT_COMPONENTS = ['wordpress', 'muPlugins', 'appCode'];
24
24
  exports.DEV_ENVIRONMENT_COMPONENTS = DEV_ENVIRONMENT_COMPONENTS;
25
25
  const DEV_ENVIRONMENT_RAW_GITHUB_HOST = 'raw.githubusercontent.com';
26
26
  exports.DEV_ENVIRONMENT_RAW_GITHUB_HOST = DEV_ENVIRONMENT_RAW_GITHUB_HOST;
@@ -32,11 +32,10 @@ const DEV_ENVIRONMENT_WORDPRESS_VERSION_TTL = 86400; // once per day
32
32
 
33
33
  exports.DEV_ENVIRONMENT_WORDPRESS_VERSION_TTL = DEV_ENVIRONMENT_WORDPRESS_VERSION_TTL;
34
34
  const DEV_ENVIRONMENT_PHP_VERSIONS = {
35
- default: 'ghcr.io/automattic/vip-container-images/php-fpm:7.4',
36
- // eslint-disable-next-line quote-props -- flow does nit support non-string keys
37
- '7.4': 'ghcr.io/automattic/vip-container-images/php-fpm-alt:7.4',
38
- '8.0': 'ghcr.io/automattic/vip-container-images/php-fpm-alt:8.0',
39
35
  // eslint-disable-next-line quote-props
40
- '8.1': 'ghcr.io/automattic/vip-container-images/php-fpm-alt:8.1'
36
+ '8.1': 'ghcr.io/automattic/vip-container-images/php-fpm-alt:8.1',
37
+ '8.0': 'ghcr.io/automattic/vip-container-images/php-fpm-alt:8.0',
38
+ // eslint-disable-next-line quote-props -- flow does nit support non-string keys
39
+ '7.4': 'ghcr.io/automattic/vip-container-images/php-fpm-alt:7.4'
41
40
  };
42
41
  exports.DEV_ENVIRONMENT_PHP_VERSIONS = DEV_ENVIRONMENT_PHP_VERSIONS;
@@ -15,6 +15,7 @@ exports.promptForText = promptForText;
15
15
  exports.promptForBoolean = promptForBoolean;
16
16
  exports.promptForPhpVersion = promptForPhpVersion;
17
17
  exports.promptForComponent = promptForComponent;
18
+ exports.processBooleanOption = processBooleanOption;
18
19
  exports.addDevEnvConfigurationOptions = addDevEnvConfigurationOptions;
19
20
  exports.getTagChoices = getTagChoices;
20
21
  exports.getEnvTrackingInfo = getEnvTrackingInfo;
@@ -44,6 +45,8 @@ var _devEnvironmentCore = require("./dev-environment-core");
44
45
 
45
46
  var _devEnvironmentLando = require("./dev-environment-lando");
46
47
 
48
+ var _userError = _interopRequireDefault(require("../cli/userError"));
49
+
47
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); }
48
51
 
49
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; }
@@ -82,7 +85,8 @@ async function handleCLIException(exception, trackKey, trackBaseInfo = {}) {
82
85
  if (trackKey) {
83
86
  try {
84
87
  const errorTrackingInfo = { ...trackBaseInfo,
85
- failure: message
88
+ failure: message,
89
+ stack: exception.stack
86
90
  };
87
91
  await (0, _tracker.trackEvent)(trackKey, errorTrackingInfo);
88
92
  } catch (trackException) {
@@ -91,9 +95,9 @@ async function handleCLIException(exception, trackKey, trackBaseInfo = {}) {
91
95
  }
92
96
 
93
97
  if (!process.env.DEBUG) {
94
- console.log(`Please re-run the command with "--debug ${_chalk.default.bold('@automattic/vip:bin:dev-environment')}" appended to it and provide the stack trace on the support ticket.`);
98
+ console.log(`\nPlease re-run the command with "--debug ${_chalk.default.bold('@automattic/vip:bin:dev-environment')}" appended to it and provide the stack trace on the support ticket.`);
95
99
  console.log(_chalk.default.bold('\nExample:\n'));
96
- console.log('vip dev-env create --debug @automattic/vip:bin:dev-environment \n');
100
+ console.log('vip dev-env <command> <arguments> --debug @automattic/vip:bin:dev-environment \n');
97
101
  }
98
102
 
99
103
  debug(exception);
@@ -117,28 +121,25 @@ function getEnvironmentName(options) {
117
121
 
118
122
  if (options.app) {
119
123
  const envSuffix = options.env ? `-${options.env}` : '';
120
- return options.app + envSuffix;
121
- }
124
+ const appName = options.app + envSuffix;
122
125
 
123
- return DEFAULT_SLUG;
124
- }
126
+ if (options.allowAppEnv) {
127
+ return appName;
128
+ }
125
129
 
126
- function getEnvironmentStartCommand(options) {
127
- if (options.slug) {
128
- return `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} start --slug ${options.slug}`;
130
+ const message = `This command does not support @app.env notation. Use '--slug=${appName}' to target the local environment.`;
131
+ throw new _userError.default(message);
129
132
  }
130
133
 
131
- if (options.app) {
132
- let application = `@${options.app}`;
133
-
134
- if (options.env) {
135
- application += `.${options.env}`;
136
- }
134
+ return DEFAULT_SLUG;
135
+ }
137
136
 
138
- return `vip ${application} ${_devEnvironment.DEV_ENVIRONMENT_SUBCOMMAND} start`;
137
+ function getEnvironmentStartCommand(slug) {
138
+ if (!slug || slug === DEFAULT_SLUG) {
139
+ return `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} start`;
139
140
  }
140
141
 
141
- return `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} start`;
142
+ return `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} start --slug ${slug}`;
142
143
  }
143
144
 
144
145
  function printTable(data) {
@@ -199,6 +200,7 @@ async function promptForArguments(preselectedOptions, defaultOptions) {
199
200
  const instanceData = {
200
201
  wpTitle: preselectedOptions.title || (await promptForText('WordPress site title', defaultOptions.title || _devEnvironment.DEV_ENVIRONMENT_DEFAULTS.title)),
201
202
  multisite: 'multisite' in preselectedOptions ? preselectedOptions.multisite : await promptForBoolean(multisiteText, !!multisiteDefault),
203
+ elasticsearchEnabled: false,
202
204
  elasticsearch: preselectedOptions.elasticsearch || defaultOptions.elasticsearch || _devEnvironment.DEV_ENVIRONMENT_DEFAULTS.elasticsearchVersion,
203
205
  php: preselectedOptions.php ? resolvePhpVersion(preselectedOptions.php) : await promptForPhpVersion(resolvePhpVersion(defaultOptions.php || _devEnvironment.DEV_ENVIRONMENT_DEFAULTS.phpVersion)),
204
206
  mariadb: preselectedOptions.mariadb || defaultOptions.mariadb || _devEnvironment.DEV_ENVIRONMENT_DEFAULTS.mariadbVersion,
@@ -209,14 +211,13 @@ async function promptForArguments(preselectedOptions, defaultOptions) {
209
211
  muPlugins: {
210
212
  mode: 'image'
211
213
  },
212
- clientCode: {
214
+ appCode: {
213
215
  mode: 'image'
214
216
  },
215
217
  statsd: false,
216
218
  phpmyadmin: false,
217
219
  xdebug: false,
218
- siteSlug: '',
219
- enterpriseSearchEnabled: preselectedOptions.enterpriseSearchEnabled || defaultOptions.enterpriseSearchEnabled
220
+ siteSlug: ''
220
221
  };
221
222
  const promptLabels = {
222
223
  xdebug: 'XDebug',
@@ -248,9 +249,15 @@ async function promptForArguments(preselectedOptions, defaultOptions) {
248
249
  instanceData[component] = result;
249
250
  }
250
251
 
251
- instanceData.enterpriseSearchEnabled = await promptForBoolean('Enable Enterprise Search?', defaultOptions.enterpriseSearchEnabled);
252
+ debug(`Processing elasticsearch with preselected "${preselectedOptions.elasticsearch}"`);
252
253
 
253
- if (instanceData.enterpriseSearchEnabled) {
254
+ if ('elasticsearch' in preselectedOptions) {
255
+ instanceData.elasticsearchEnabled = !!preselectedOptions.elasticsearch;
256
+ } else {
257
+ instanceData.elasticsearchEnabled = await promptForBoolean('Enable Elasticsearch (needed by Enterprise Search)?', defaultOptions.elasticsearchEnabled);
258
+ }
259
+
260
+ if (instanceData.elasticsearchEnabled) {
254
261
  instanceData.statsd = preselectedOptions.statsd || defaultOptions.statsd || false;
255
262
  } else {
256
263
  instanceData.statsd = false;
@@ -258,7 +265,11 @@ async function promptForArguments(preselectedOptions, defaultOptions) {
258
265
 
259
266
  for (const service of ['phpmyadmin', 'xdebug']) {
260
267
  if (service in instanceData) {
261
- instanceData[service] = await promptForBoolean(`Enable ${promptLabels[service] || service}`, instanceData[service]);
268
+ if (service in preselectedOptions) {
269
+ instanceData[service] = preselectedOptions[service];
270
+ } else {
271
+ instanceData[service] = await promptForBoolean(`Enable ${promptLabels[service] || service}`, instanceData[service]);
272
+ }
262
273
  }
263
274
  }
264
275
 
@@ -283,15 +294,14 @@ async function processComponent(component, preselectedValue, defaultValue) {
283
294
 
284
295
  const resolvedPath = resolvePath(result.dir || '');
285
296
  result.dir = resolvedPath;
297
+ const {
298
+ result: isPathValid,
299
+ message
300
+ } = validateLocalPath(component, resolvedPath);
286
301
 
287
- const isDirectory = resolvedPath && _fs.default.existsSync(resolvedPath) && _fs.default.lstatSync(resolvedPath).isDirectory();
288
-
289
- const isEmpty = isDirectory ? _fs.default.readdirSync(resolvedPath).length === 0 : true;
290
-
291
- if (isDirectory && !isEmpty) {
302
+ if (isPathValid) {
292
303
  break;
293
304
  } else {
294
- const message = `Provided path "${resolvedPath}" does not point to a valid or existing directory.`;
295
305
  console.log(_chalk.default.yellow('Warning:'), message);
296
306
  result = await promptForComponent(component, allowLocal, defaultObject);
297
307
  }
@@ -300,6 +310,49 @@ async function processComponent(component, preselectedValue, defaultValue) {
300
310
  return result;
301
311
  }
302
312
 
313
+ function validateLocalPath(component, providedPath) {
314
+ if (!isNonEmptyDirectory(providedPath)) {
315
+ const message = `Provided path "${providedPath}" does not point to a valid or existing directory.`;
316
+ return {
317
+ result: false,
318
+ message
319
+ };
320
+ }
321
+
322
+ if (component === 'appCode') {
323
+ const files = ['languages', 'plugins', 'themes', 'private', 'images', 'client-mu-plugins', 'vip-config'];
324
+ const missingFiles = [];
325
+
326
+ for (const file of files) {
327
+ const filePath = _path.default.resolve(providedPath, file);
328
+
329
+ if (!_fs.default.existsSync(filePath)) {
330
+ missingFiles.push(file);
331
+ }
332
+ }
333
+
334
+ if (missingFiles.length > 0) {
335
+ const message = `Provided path "${providedPath}" is missing following files/folders: ${missingFiles.join(', ')}. Learn more: https://docs.wpvip.com/technical-references/vip-codebase/#1-wordpress`;
336
+ return {
337
+ result: false,
338
+ message
339
+ };
340
+ }
341
+ }
342
+
343
+ return {
344
+ result: true,
345
+ message: ''
346
+ };
347
+ }
348
+
349
+ function isNonEmptyDirectory(directoryPath) {
350
+ const isDirectory = directoryPath && _fs.default.existsSync(directoryPath) && _fs.default.lstatSync(directoryPath).isDirectory();
351
+
352
+ const isEmpty = isDirectory ? _fs.default.readdirSync(directoryPath).length === 0 : true;
353
+ return !isEmpty && isDirectory;
354
+ }
355
+
303
356
  function resolvePath(input) {
304
357
  // Resolve does not do ~ reliably
305
358
  const resolvedPath = input.replace(/^~/, _os.default.homedir()); // And resolve to handle relative paths
@@ -366,7 +419,7 @@ async function promptForPhpVersion(initialValue) {
366
419
  const componentDisplayNames = {
367
420
  wordpress: 'WordPress',
368
421
  muPlugins: 'vip-go-mu-plugins',
369
- clientCode: 'site-code'
422
+ appCode: 'application code'
370
423
  };
371
424
 
372
425
  async function promptForComponent(component, allowLocal, defaultObject) {
@@ -382,12 +435,12 @@ async function promptForComponent(component, allowLocal, defaultObject) {
382
435
  }
383
436
 
384
437
  modChoices.push({
385
- message: 'image - that gets automatically fetched',
438
+ message: 'demo image - that gets automatically fetched',
386
439
  value: 'image'
387
440
  });
388
441
  let initialMode = 'image';
389
442
 
390
- if ('clientCode' === component) {
443
+ if ('appCode' === component) {
391
444
  initialMode = 'local';
392
445
  }
393
446
 
@@ -439,20 +492,28 @@ async function promptForComponent(component, allowLocal, defaultObject) {
439
492
  };
440
493
  }
441
494
 
442
- function addDevEnvConfigurationOptions(command) {
443
- return command.option('wordpress', 'Use a specific WordPress version').option(['u', 'mu-plugins'], 'Use a specific mu-plugins changeset or local directory').option('client-code', 'Use the client code from a local directory or VIP skeleton').option('statsd', 'Enable statsd component. By default it is disabled', undefined, value => {
444
- var _value$toLowerCase;
495
+ const FALSE_OPTIONS = ['false', 'no', 'n', '0'];
445
496
 
446
- return 'false' !== (value === null || value === void 0 ? void 0 : (_value$toLowerCase = value.toLowerCase) === null || _value$toLowerCase === void 0 ? void 0 : _value$toLowerCase.call(value));
447
- }).option('phpmyadmin', 'Enable PHPMyAdmin component. By default it is disabled', undefined, value => {
448
- var _value$toLowerCase2;
497
+ function processBooleanOption(value) {
498
+ var _value$toLowerCase;
449
499
 
450
- return 'false' !== (value === null || value === void 0 ? void 0 : (_value$toLowerCase2 = value.toLowerCase) === null || _value$toLowerCase2 === void 0 ? void 0 : _value$toLowerCase2.call(value));
451
- }).option('xdebug', 'Enable XDebug. By default it is disabled', undefined, value => {
452
- var _value$toLowerCase3;
500
+ if (!value) {
501
+ return false;
502
+ }
503
+
504
+ if (FALSE_OPTIONS.includes((_value$toLowerCase = value.toLowerCase) === null || _value$toLowerCase === void 0 ? void 0 : _value$toLowerCase.call(value))) {
505
+ return false;
506
+ }
507
+
508
+ return true;
509
+ }
510
+
511
+ function addDevEnvConfigurationOptions(command) {
512
+ 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 => {
513
+ var _value$toLowerCase2;
453
514
 
454
- return 'false' !== (value === null || value === void 0 ? void 0 : (_value$toLowerCase3 = value.toLowerCase) === null || _value$toLowerCase3 === void 0 ? void 0 : _value$toLowerCase3.call(value));
455
- }).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.').option('php', 'Explicitly choose PHP version to use');
515
+ 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;
516
+ }).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');
456
517
  }
457
518
  /**
458
519
  * Provides the list of tag choices for selection