@automattic/vip 3.9.6 → 3.11.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.
@@ -15,19 +15,6 @@ proxy:
15
15
  keys: false
16
16
 
17
17
  services:
18
- devtools:
19
- type: compose
20
- services:
21
- image: ghcr.io/automattic/vip-container-images/dev-tools:0.9
22
- volumes:
23
- - devtools:/dev-tools
24
- - scripts:/scripts
25
- volumes:
26
- devtools: {}
27
- scripts:
28
- initOnly: true
29
- entrypoint: sh -c 'test -d /dev-tools-orig && /usr/bin/rsync -a --delete /dev-tools-orig/ /dev-tools/ || true'
30
-
31
18
  nginx:
32
19
  type: compose
33
20
  ssl: true
@@ -70,8 +57,6 @@ services:
70
57
  elasticsearch:
71
58
  condition: service_started
72
59
  <% } %>
73
- devtools:
74
- condition: service_completed_successfully
75
60
  wordpress:
76
61
  condition: service_completed_successfully
77
62
  <% if ( muPlugins.mode == 'image' ) { %>
@@ -96,6 +81,9 @@ services:
96
81
  <% wpVolumes() %>
97
82
  run_as_root:
98
83
  - chown www-data:www-data /wp/wp-content/mu-plugins /wp/config /wp/log /wp/wp-content/uploads /wp
84
+ <% if ( appCode.mode == 'image' ) { %>
85
+ - chown www-data:www-data /wp/wp-content/plugins
86
+ <% } %>
99
87
  run:
100
88
  - >
101
89
  sh /dev-tools/setup.sh
@@ -193,13 +181,13 @@ services:
193
181
  image: ghcr.io/automattic/vip-container-images/wordpress:<%= wordpress.tag %>
194
182
  volumes:
195
183
  - ./wordpress:/shared
196
- - type: volume
197
- source: scripts
198
- target: /scripts
199
- volume:
200
- nocopy: true
184
+ - devtools:/dev-tools
185
+ - scripts:/scripts
201
186
  initOnly: true
202
- entrypoint: /usr/bin/rsync -a --chown=${LANDO_HOST_USER_ID}:${LANDO_HOST_GROUP_ID} /wp/ /shared/
187
+ entrypoint: sh -c '/usr/bin/rsync -a --delete --chown=${LANDO_HOST_USER_ID}:${LANDO_HOST_GROUP_ID} /wp/ /shared/; /usr/bin/rsync -a --chown=${LANDO_HOST_USER_ID}:${LANDO_HOST_GROUP_ID} --delete /dev-tools-orig/ /dev-tools/'
188
+ volumes:
189
+ devtools:
190
+ scripts:
203
191
 
204
192
  <% if ( muPlugins.mode == 'image' ) { %>
205
193
  vip-mu-plugins:
@@ -301,6 +289,7 @@ tooling:
301
289
  - ./log:/wp/log
302
290
  - ./uploads:/wp/wp-content/uploads
303
291
  - ./wordpress:/wp
292
+ - ./integrations-config:/wp/config/integrations-config
304
293
  <% if ( muPlugins.mode == 'image' ) { %>
305
294
  - type: volume
306
295
  source: mu-plugins
@@ -65,11 +65,14 @@ cmd.argv(process.argv, async (arg, opt) => {
65
65
  exit.withError(messageToShow);
66
66
  }
67
67
 
68
- /** @type {InstanceOptions} */
68
+ /** @type {import('../lib/dev-environment/types').InstanceOptions} */
69
69
  let defaultOptions = {};
70
+ /** @type {Record<string,import('../lib/dev-environment/types').IntegrationConfig>} */
71
+ let integrationsConfig = {};
70
72
  try {
71
73
  if (opt.app) {
72
74
  const appInfo = await (0, _devEnvironmentCore.getApplicationInformation)(opt.app, opt.env);
75
+ integrationsConfig = appInfo.environment?.integrations ?? {};
73
76
  defaultOptions = (0, _devEnvironmentCli.getOptionsFromAppInfo)(appInfo);
74
77
  }
75
78
  } catch (error) {
@@ -87,7 +90,7 @@ cmd.argv(process.argv, async (arg, opt) => {
87
90
  const instanceData = await (0, _devEnvironmentCli.promptForArguments)(preselectedOptions, defaultOptions, suppressPrompts, true);
88
91
  instanceData.siteSlug = slug;
89
92
  try {
90
- await (0, _devEnvironmentCore.createEnvironment)(lando, instanceData);
93
+ await (0, _devEnvironmentCore.createEnvironment)(lando, instanceData, integrationsConfig);
91
94
  await (0, _devEnvironmentCore.printEnvironmentInfo)(lando, slug, {
92
95
  extended: false,
93
96
  suppressWarnings: true
@@ -15,6 +15,7 @@ var _command = _interopRequireDefault(require("../lib/cli/command"));
15
15
  var exit = _interopRequireWildcard(require("../lib/cli/exit"));
16
16
  var _format = require("../lib/cli/format");
17
17
  var _progress = require("../lib/cli/progress");
18
+ var _prompt = require("../lib/cli/prompt");
18
19
  var _clientFileUploader = require("../lib/client-file-uploader");
19
20
  var _searchAndReplace = require("../lib/search-and-replace");
20
21
  var _dbFileImport = require("../lib/site-import/db-file-import");
@@ -339,14 +340,15 @@ void (0, _command.default)({
339
340
  requiredArgs: 1,
340
341
  module: 'import-sql',
341
342
  usage
342
- }).command('status', 'Check the status of a SQL database import currently in progress.').option('skip-validate', 'Do not perform file validation prior to import. If the file contains unsupported entries, the import is likely to fail.').option('search-replace', 'Search for a string in the SQL file and replace it with a new string. Separate the values by a comma only; no spaces (e.g. --search-replace=“from,to”).').option('in-place', 'Overwrite the local input file with the results of the search and replace operation prior to import.').option('output', 'The local file path to save a copy of the results from the search and replace operation when the --search-replace option is passed. Ignored when used with the --in-place option.').examples(examples).argv(process.argv, async (arg, opts) => {
343
+ }).command('status', 'Check the status of a SQL database import currently in progress.').option('skip-validate', 'Do not perform file validation prior to import. If the file contains unsupported entries, the import is likely to fail.').option('search-replace', 'Search for a string in the SQL file and replace it with a new string. Separate the values by a comma only; no spaces (e.g. --search-replace=“from,to”).').option('in-place', 'Overwrite the local input file with the results of the search and replace operation prior to import.').option('output', 'The local file path to save a copy of the results from the search and replace operation when the --search-replace option is passed. Ignored when used with the --in-place option.').option('skip-maintenance-mode', 'Imports data without putting the environment in maintenance mode. Available only for unlaunched environments. Caution: This may cause site instability during import.').examples(examples).argv(process.argv, async (arg, opts) => {
343
344
  const {
344
345
  app,
345
346
  env
346
347
  } = opts;
347
348
  let {
348
349
  skipValidate,
349
- searchReplace
350
+ searchReplace,
351
+ skipMaintenanceMode
350
352
  } = opts;
351
353
  const {
352
354
  id: envId,
@@ -405,6 +407,16 @@ void (0, _command.default)({
405
407
  isMultiSite,
406
408
  tableNames
407
409
  });
410
+ if (opts.inPlace) {
411
+ const approved = await (0, _prompt.confirm)([], 'Are you sure you want to run search and replace on your input file? This operation is not reversible.');
412
+ if (!approved) {
413
+ await (0, _tracker.trackEventWithEnv)('search_replace_in_place_cancelled', {
414
+ is_import: true,
415
+ in_place: opts.inPlace
416
+ });
417
+ process.exit();
418
+ }
419
+ }
408
420
 
409
421
  /**
410
422
  * =========== WARNING =============
@@ -443,7 +455,9 @@ Processing the SQL import for your environment...
443
455
  } = await (0, _searchAndReplace.searchAndReplace)(fileName, searchReplace, {
444
456
  isImport: true,
445
457
  inPlace: opts.inPlace,
446
- output: opts.output ?? true // "true" creates a temp output file instead of printing to stdout, as we need to upload the output to S3.
458
+ output: opts.output ?? true,
459
+ // "true" creates a temp output file instead of printing to stdout, as we need to upload the output to S3.
460
+ batchMode: true
447
461
  });
448
462
  if (typeof outputFileName !== 'string') {
449
463
  progressTracker.stepFailed('replace');
@@ -482,7 +496,8 @@ Processing the SQL import for your environment...
482
496
  environmentId: env.id,
483
497
  basename,
484
498
  md5,
485
- searchReplace: []
499
+ searchReplace: [],
500
+ skipMaintenanceMode
486
501
  };
487
502
  if (searchReplace) {
488
503
  let pairs = searchReplace;
@@ -367,7 +367,7 @@ const examples = [{
367
367
  subShellRl.close();
368
368
  process.exit();
369
369
  }
370
- const startsWithWp = line.startsWith('wp ');
370
+ const startsWithWp = line.trim().startsWith('wp ');
371
371
  const empty = 0 === line.length;
372
372
  const userCmdCancelled = line === cancelCommandChar;
373
373
  if ((empty || !startsWithWp) && !userCmdCancelled) {
@@ -92,6 +92,9 @@ class DevEnvImportSQLCommand {
92
92
  if (searchReplace?.length && !inPlace) {
93
93
  _fs.default.unlinkSync(resolvedPath);
94
94
  }
95
+ if (this.options.postImportSQL) {
96
+ await (0, _devEnvironmentDatabase.executeQuery)(lando, this.slug, this.options.postImportSQL);
97
+ }
95
98
  await (0, _devEnvironmentDatabase.flushCache)(lando, this.slug, this.options.quiet);
96
99
  if (undefined === this.options.skipReindex || !(0, _devEnvironmentCli.processBooleanOption)(this.options.skipReindex)) {
97
100
  try {
@@ -205,15 +205,16 @@ class DevEnvSyncSQLCommand {
205
205
  const importOptions = {
206
206
  inPlace: true,
207
207
  skipValidate: true,
208
- quiet: true
208
+ quiet: true,
209
+ postImportSQL: this.fixBlogsTableQuery()
209
210
  };
210
211
  const importCommand = new _devEnvImportSql.DevEnvImportSQLCommand(this.sqlFile, importOptions, this.slug);
211
212
  await importCommand.run();
212
213
  }
213
- async fixBlogsTable() {
214
+ fixBlogsTableQuery() {
214
215
  const networkSites = this.env.wpSitesSDS?.nodes;
215
216
  if (!networkSites) {
216
- return;
217
+ return '';
217
218
  }
218
219
  const prologue = `
219
220
  DROP PROCEDURE IF EXISTS vip_sync_update_blog_domains;
@@ -240,10 +241,10 @@ DROP PROCEDURE vip_sync_update_blog_domains;
240
241
  const newDomain = primaryDomain !== oldDomain ? `${this.slugifyDomain(oldDomain)}.${this.landoDomain}` : this.landoDomain;
241
242
  queries.push(` UPDATE wp_blogs SET domain = '${newDomain}' WHERE blog_id = ${Number(site.blogId)};`);
242
243
  }
243
- if (queries.length) {
244
- const sql = `${prologue}\n${queries.join('\n')}\n${epilogue}`;
245
- await _fs.default.promises.appendFile(this.sqlFile, sql);
244
+ if (queries.length === 0) {
245
+ return '';
246
246
  }
247
+ return `${prologue}\n${queries.join('\n')}\n${epilogue}`;
247
248
  }
248
249
 
249
250
  /**
@@ -301,7 +302,6 @@ DROP PROCEDURE vip_sync_update_blog_domains;
301
302
  console.log(` ${domain} -> ${landoDomain}`);
302
303
  }
303
304
  await this.runSearchReplace();
304
- await this.fixBlogsTable();
305
305
  console.log(`${_chalk.default.green('✓')} Search-replace operation is complete`);
306
306
  } catch (err) {
307
307
  const error = err;
@@ -5,7 +5,9 @@ exports.get = get;
5
5
  var _graphqlTag = _interopRequireDefault(require("graphql-tag"));
6
6
  var _api = _interopRequireDefault(require("../../lib/api"));
7
7
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
8
- const api = (0, _api.default)();
8
+ const api = (0, _api.default)({
9
+ silenceAuthErrors: true
10
+ });
9
11
  const isVipQuery = (0, _graphqlTag.default)`
10
12
  query isVIP {
11
13
  me {
@@ -20,8 +20,10 @@ const QUERY_CURRENT_USER = (0, _graphqlTag.default)`
20
20
  }
21
21
  }
22
22
  `;
23
- async function getCurrentUserInfo() {
24
- const api = (0, _api.default)();
23
+ async function getCurrentUserInfo(silenceAuthErrors = false) {
24
+ const api = (0, _api.default)({
25
+ silenceAuthErrors
26
+ });
25
27
  const response = await api.query({
26
28
  query: QUERY_CURRENT_USER
27
29
  });
package/dist/lib/api.js CHANGED
@@ -34,13 +34,14 @@ function isServerError(networkError) {
34
34
  return 'result' in networkError;
35
35
  }
36
36
  function API({
37
- exitOnError = true
37
+ exitOnError = true,
38
+ silenceAuthErrors = false
38
39
  } = {}) {
39
40
  const errorLink = (0, _error.onError)(({
40
41
  networkError,
41
42
  graphQLErrors
42
43
  }) => {
43
- if (networkError && 'statusCode' in networkError && networkError.statusCode === 401) {
44
+ if (!silenceAuthErrors && networkError && 'statusCode' in networkError && networkError.statusCode === 401) {
44
45
  let message = 'You are not authorized to perform this request; please logout with `vip logout`, then try again.';
45
46
  if (isServerError(networkError) && networkError.result?.code === 'token-disabled-inactivity') {
46
47
  message = 'Your token has expired due to inactivity; please log out with `vip logout`, then try again.';
@@ -236,10 +236,17 @@ async function uploadUsingPutObject({
236
236
  cause: err
237
237
  });
238
238
  }
239
- const {
240
- Code,
241
- Message
242
- } = parsedResponse.Error;
239
+ let Code;
240
+ let Message;
241
+ if (parsedResponse?.Error) {
242
+ ({
243
+ Code,
244
+ Message
245
+ } = parsedResponse.Error);
246
+ } else {
247
+ Code = `HTTP Error ${response.status}`;
248
+ Message = response.statusText;
249
+ }
243
250
  throw new Error(`Unable to upload to cloud storage. ${JSON.stringify({
244
251
  Code,
245
252
  Message
@@ -39,27 +39,28 @@ const appQuery = exports.appQuery = `
39
39
  }
40
40
  }
41
41
  }`;
42
- const appQueryFragments = exports.appQueryFragments = `fragment Software on AppEnvironmentSoftwareSettingsSoftware {
42
+ const appQueryFragments = exports.appQueryFragments = (0, _graphqlTag.default)`
43
+ fragment Software on AppEnvironmentSoftwareSettingsSoftware {
43
44
  name
44
45
  slug
45
46
  pinned
46
47
  current {
47
- version
48
- default
49
- deprecated
50
- unstable
51
- compatible
52
- latestRelease
53
- private
48
+ version
49
+ default
50
+ deprecated
51
+ unstable
52
+ compatible
53
+ latestRelease
54
+ private
54
55
  }
55
56
  options {
56
- version
57
- default
58
- deprecated
59
- unstable
60
- compatible
61
- latestRelease
62
- private
57
+ version
58
+ default
59
+ deprecated
60
+ unstable
61
+ compatible
62
+ latestRelease
63
+ private
63
64
  }
64
65
  }
65
66
  `;
@@ -6,7 +6,7 @@ const DEV_ENVIRONMENT_SUBCOMMAND = exports.DEV_ENVIRONMENT_SUBCOMMAND = 'dev-env
6
6
  const DEV_ENVIRONMENT_FULL_COMMAND = exports.DEV_ENVIRONMENT_FULL_COMMAND = `vip ${DEV_ENVIRONMENT_SUBCOMMAND}`;
7
7
  const DEV_ENVIRONMENT_PROMPT_INTRO = exports.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';
8
8
  const DEV_ENVIRONMENT_NOT_FOUND = exports.DEV_ENVIRONMENT_NOT_FOUND = 'Environment not found.';
9
- const DEV_ENVIRONMENT_COMPONENTS = exports.DEV_ENVIRONMENT_COMPONENTS = ['muPlugins', 'appCode'];
9
+ const DEV_ENVIRONMENT_COMPONENTS = exports.DEV_ENVIRONMENT_COMPONENTS = ['appCode', 'muPlugins'];
10
10
  const DEV_ENVIRONMENT_COMPONENTS_WITH_WP = exports.DEV_ENVIRONMENT_COMPONENTS_WITH_WP = ['wordpress', ...DEV_ENVIRONMENT_COMPONENTS];
11
11
  const DEV_ENVIRONMENT_RAW_GITHUB_HOST = exports.DEV_ENVIRONMENT_RAW_GITHUB_HOST = 'raw.githubusercontent.com';
12
12
  const DEV_ENVIRONMENT_WORDPRESS_VERSIONS_URI = exports.DEV_ENVIRONMENT_WORDPRESS_VERSIONS_URI = '/Automattic/vip-container-images/master/wordpress/versions.json';
@@ -36,4 +36,4 @@ const DEV_ENVIRONMENT_DEFAULTS = exports.DEV_ENVIRONMENT_DEFAULTS = {
36
36
  multisite: false,
37
37
  phpVersion: Object.keys(DEV_ENVIRONMENT_PHP_VERSIONS)[0]
38
38
  };
39
- const DEV_ENVIRONMENT_VERSION = exports.DEV_ENVIRONMENT_VERSION = '2.1.3';
39
+ const DEV_ENVIRONMENT_VERSION = exports.DEV_ENVIRONMENT_VERSION = '2.2.2';
@@ -40,6 +40,7 @@ var _shelljs = require("shelljs");
40
40
  var _devEnvironmentConfigurationFile = require("./dev-environment-configuration-file");
41
41
  var _devEnvironmentCore = require("./dev-environment-core");
42
42
  var _devEnvironmentLando = require("./dev-environment-lando");
43
+ var _user = require("../api/user");
43
44
  var _devEnvironment = require("../constants/dev-environment");
44
45
  var _tracker = require("../tracker");
45
46
  var _userError = _interopRequireDefault(require("../user-error"));
@@ -59,7 +60,7 @@ function setIsTTY(val) {
59
60
  }
60
61
  const componentDisplayNames = {
61
62
  wordpress: 'WordPress',
62
- muPlugins: 'vip-go-mu-plugins',
63
+ muPlugins: 'VIP MU Plugins',
63
64
  appCode: 'application code'
64
65
  };
65
66
  const componentDemoNames = {
@@ -164,6 +165,8 @@ function processComponentOptionInput(passedParam, allowLocal) {
164
165
  };
165
166
  }
166
167
  function getOptionsFromAppInfo(appInfo) {
168
+ const integrationsConfig = appInfo.environment?.integrations ?? {};
169
+ const hasES = integrationsConfig['enterprise-search']?.env?.status === 'enabled';
167
170
  return {
168
171
  // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
169
172
  title: appInfo.environment?.name || appInfo.name || '',
@@ -171,7 +174,8 @@ function getOptionsFromAppInfo(appInfo) {
171
174
  multisite: Boolean(appInfo.environment?.isMultisite),
172
175
  mediaRedirectDomain: appInfo.environment?.primaryDomain,
173
176
  php: appInfo.environment?.php ?? '',
174
- wordpress: appInfo.environment?.wordpress ?? ''
177
+ wordpress: appInfo.environment?.wordpress ?? '',
178
+ elasticsearch: hasES
175
179
  };
176
180
  }
177
181
 
@@ -293,7 +297,15 @@ async function processWordPress(preselectedValue, defaultValue) {
293
297
  async function processComponent(component, preselectedValue, defaultValue, suppressPrompts = false) {
294
298
  debug(`processing a component '${component}', with preselected/default - ${preselectedValue}/${defaultValue}`);
295
299
  let result;
296
- const allowLocal = true;
300
+ let allowLocal = true;
301
+ if (component === 'muPlugins') {
302
+ try {
303
+ const currentUser = await (0, _user.getCurrentUserInfo)(true);
304
+ allowLocal = currentUser?.isVIP ?? false;
305
+ } catch (err) {
306
+ allowLocal = false;
307
+ }
308
+ }
297
309
  const defaultObject = defaultValue ? processComponentOptionInput(defaultValue, allowLocal) : null;
298
310
  if (preselectedValue) {
299
311
  result = processComponentOptionInput(preselectedValue, allowLocal);
@@ -538,7 +550,7 @@ async function promptForComponent(component, allowLocal, defaultObject) {
538
550
  debug(modeResult);
539
551
  const messagePrefix = selectMode ? '\t' : `${componentDisplayName} - `;
540
552
  if ('local' === modeResult) {
541
- const directoryPath = await promptForText(`${messagePrefix}What is a path to your local ${componentDisplayName}`, defaultObject?.dir ?? '');
553
+ const directoryPath = await promptForText(`${messagePrefix}What is a path to your local ${componentDisplayName}?`, defaultObject?.dir ?? '');
542
554
  return {
543
555
  mode: modeResult,
544
556
  dir: directoryPath
@@ -27,6 +27,8 @@ var _copyDir = _interopRequireDefault(require("copy-dir"));
27
27
  var _debug = _interopRequireDefault(require("debug"));
28
28
  var _ejs = _interopRequireDefault(require("ejs"));
29
29
  var _enquirer = require("enquirer");
30
+ var _graphql = require("graphql");
31
+ var _utils = require("lando/lib/utils");
30
32
  var _nodeFetch = _interopRequireDefault(require("node-fetch"));
31
33
  var _nodeFs = _interopRequireDefault(require("node:fs"));
32
34
  var _nodePath = _interopRequireDefault(require("node:path"));
@@ -49,8 +51,11 @@ const landoFileName = '.lando.yml';
49
51
  const landoBackupFileName = '.lando.backup.yml';
50
52
  const nginxFileName = 'extra.conf';
51
53
  const instanceDataFileName = 'instance_data.json';
54
+ const integrationsConfigFileName = 'integrations.json';
55
+ const integrationsConfigBackupFileName = 'integrations.json.bak';
52
56
  const uploadPathString = 'uploads';
53
57
  const nginxPathString = 'nginx';
58
+ const integrationsConfigPathString = 'integrations-config';
54
59
  function xdgDataDirectory() {
55
60
  if (_xdgBasedir.default.data) {
56
61
  return _xdgBasedir.default.data;
@@ -92,8 +97,9 @@ async function stopEnvironment(lando, slug) {
92
97
  }
93
98
  await (0, _devEnvironmentLando.landoStop)(lando, instancePath);
94
99
  }
95
- async function createEnvironment(lando, instanceData) {
100
+ async function createEnvironment(lando, instanceData, integrationsConfig) {
96
101
  const slug = instanceData.siteSlug;
102
+ integrationsConfig ??= {};
97
103
  debug('Will process an environment', slug, 'with instanceData for creation: ', instanceData);
98
104
  const instancePath = getEnvironmentPath(slug);
99
105
  debug('Instance path for', slug, 'is:', instancePath);
@@ -103,7 +109,7 @@ async function createEnvironment(lando, instanceData) {
103
109
  }
104
110
  const preProcessedInstanceData = preProcessInstanceData(instanceData);
105
111
  debug('Will create an environment', slug, 'with instanceData: ', preProcessedInstanceData);
106
- await prepareLandoEnv(lando, preProcessedInstanceData, instancePath);
112
+ await prepareLandoEnv(lando, preProcessedInstanceData, instancePath, integrationsConfig);
107
113
  }
108
114
  async function updateEnvironment(lando, instanceData) {
109
115
  const slug = instanceData.siteSlug;
@@ -116,7 +122,7 @@ async function updateEnvironment(lando, instanceData) {
116
122
  }
117
123
  const preProcessedInstanceData = preProcessInstanceData(instanceData);
118
124
  debug('Will create an environment', slug, 'with instanceData: ', preProcessedInstanceData);
119
- await prepareLandoEnv(lando, preProcessedInstanceData, instancePath);
125
+ await prepareLandoEnv(lando, preProcessedInstanceData, instancePath, undefined);
120
126
  }
121
127
  function preProcessInstanceData(instanceData) {
122
128
  const newInstanceData = {
@@ -180,6 +186,10 @@ async function destroyEnvironment(lando, slug, removeFiles) {
180
186
  } else {
181
187
  debug("Lando file doesn't exist, skipping lando destroy.");
182
188
  }
189
+ await _nodeFs.default.promises.rm(_nodePath.default.join(xdgDataDirectory(), 'vip', 'lando', 'compose', (0, _utils.dockerComposify)(slug)), {
190
+ force: true,
191
+ recursive: true
192
+ });
183
193
  if (removeFiles) {
184
194
  await _nodeFs.default.promises.rm(instancePath, {
185
195
  recursive: true
@@ -333,7 +343,28 @@ function writeEnvironmentData(slug, data) {
333
343
  const instanceDataTargetPath = _nodePath.default.join(instancePath, instanceDataFileName);
334
344
  return _nodeFs.default.promises.writeFile(instanceDataTargetPath, JSON.stringify(data, null, 2));
335
345
  }
336
- async function prepareLandoEnv(lando, instanceData, instancePath) {
346
+ async function writeIntegrationsConfig(instancePath, integrationsConfig) {
347
+ const integrationsConfigPath = _nodePath.default.join(instancePath, integrationsConfigPathString);
348
+ const integrationsConfigTargetPath = _nodePath.default.join(integrationsConfigPath, integrationsConfigFileName);
349
+ await _nodeFs.default.promises.mkdir(integrationsConfigPath, {
350
+ recursive: true
351
+ });
352
+ if (integrationsConfig !== undefined) {
353
+ const integrationsConfigBackupTargetPath = _nodePath.default.join(integrationsConfigPath, integrationsConfigBackupFileName);
354
+ try {
355
+ await _nodeFs.default.promises.rename(integrationsConfigTargetPath, integrationsConfigBackupTargetPath);
356
+ console.log(`Backup of ${integrationsConfigFileName} was created in ${integrationsConfigBackupTargetPath}`);
357
+ } catch (err) {
358
+ if ('ENOENT' !== err.code) {
359
+ throw err;
360
+ }
361
+ }
362
+ const configJson = JSON.stringify(integrationsConfig, null, 4);
363
+ await _nodeFs.default.promises.writeFile(integrationsConfigTargetPath, configJson);
364
+ debug(`Integrations configuration file created in ${integrationsConfigTargetPath}`);
365
+ }
366
+ }
367
+ async function prepareLandoEnv(lando, instanceData, instancePath, integrationsConfig) {
337
368
  const templateData = {
338
369
  ...instanceData,
339
370
  domain: lando.config.domain
@@ -365,6 +396,7 @@ async function prepareLandoEnv(lando, instanceData, instancePath) {
365
396
  debug(`Lando file created in ${landoFileTargetPath}`);
366
397
  debug(`Nginx file created in ${nginxFileTargetPath}`);
367
398
  debug(`Instance data file created in ${instanceDataTargetPath}`);
399
+ await writeIntegrationsConfig(instancePath, integrationsConfig);
368
400
  }
369
401
  function getAllEnvironmentNames() {
370
402
  const mainEnvironmentPath = xdgDataDirectory();
@@ -402,6 +434,9 @@ async function getApplicationInformation(appId, envType) {
402
434
  type,
403
435
  branch,
404
436
  isMultisite,
437
+ getIntegrationsDevEnvConfig {
438
+ data
439
+ }
405
440
  primaryDomain {
406
441
  name
407
442
  },
@@ -414,7 +449,7 @@ async function getApplicationInformation(appId, envType) {
414
449
  }
415
450
  }
416
451
  }`;
417
- const queryResult = await (0, _app.default)(appId, fieldsQuery, _software.appQueryFragments);
452
+ const queryResult = await (0, _app.default)(appId, fieldsQuery, (0, _graphql.print)(_software.appQueryFragments));
418
453
  const appData = {};
419
454
  if (queryResult.id) {
420
455
  appData.id = queryResult.id;
@@ -448,7 +483,8 @@ async function getApplicationInformation(appId, envType) {
448
483
  isMultisite: envData.isMultisite,
449
484
  primaryDomain: envData.primaryDomain?.name ?? '',
450
485
  php: envData.softwareSettings?.php?.current.version ?? '',
451
- wordpress: envData.softwareSettings?.wordpress?.current.version ?? ''
486
+ wordpress: envData.softwareSettings?.wordpress?.current.version ?? '',
487
+ integrations: envData.getIntegrationsDevEnvConfig?.data ?? {}
452
488
  };
453
489
  }
454
490
  }
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
 
3
3
  exports.__esModule = true;
4
- exports.reIndexSearch = exports.flushCache = exports.dataCleanup = exports.addAdminUser = void 0;
4
+ exports.reIndexSearch = exports.flushCache = exports.executeQuery = exports.dataCleanup = exports.addAdminUser = void 0;
5
5
  var _devEnvironmentCore = require("./dev-environment-core");
6
6
  const addAdminUser = async (lando, slug, quiet) => {
7
7
  const addUserArg = ['wp', 'dev-env-add-admin', '--username=vipgo', '--password=password', '--skip-plugins', '--skip-themes'].concat(quiet ? ['--quiet'] : []);
@@ -29,4 +29,8 @@ const flushCache = async (lando, slug, quiet) => {
29
29
  const cacheArg = ['wp', 'cache', 'flush', '--skip-plugins', '--skip-themes'].concat(quiet ? ['--quiet'] : []);
30
30
  await (0, _devEnvironmentCore.exec)(lando, slug, cacheArg);
31
31
  };
32
- exports.flushCache = flushCache;
32
+ exports.flushCache = flushCache;
33
+ const executeQuery = async (lando, slug, query) => {
34
+ await (0, _devEnvironmentCore.exec)(lando, slug, ['wp', 'db', 'query', query]);
35
+ };
36
+ exports.executeQuery = executeQuery;
@@ -499,7 +499,6 @@ async function landoExec(lando, instancePath, toolName, args, options) {
499
499
  process.argv = ['0', '1', '3'].concat(args);
500
500
  tool.app = app;
501
501
  tool.name = toolName;
502
- tool.dir = '/';
503
502
  if (options.stdio) {
504
503
  tool.stdio = options.stdio;
505
504
  }
@@ -61,7 +61,9 @@ async function getDockerSocket() {
61
61
  // Try the default location
62
62
  paths.push('/var/run/docker.sock');
63
63
  // Try alternative locations
64
+ paths.push('/run/docker.sock');
64
65
  paths.push((0, _nodePath.join)((0, _nodeOs.homedir)(), '.docker', 'run', 'docker.sock'));
66
+ paths.push((0, _nodePath.join)((0, _nodeOs.homedir)(), '.colima', 'default', 'docker.sock'));
65
67
  paths.push((0, _nodePath.join)((0, _nodeOs.homedir)(), '.orbstack', 'run', 'docker.sock'));
66
68
  for (const socketPath of paths) {
67
69
  try {
@@ -6,7 +6,7 @@ var _graphqlTag = _interopRequireDefault(require("graphql-tag"));
6
6
  var _api = _interopRequireDefault(require("../api"));
7
7
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
8
8
  const IMPORT_MEDIA_CONFIG_QUERY = (0, _graphqlTag.default)`
9
- {
9
+ query MediaImportConfig {
10
10
  mediaImportConfig {
11
11
  fileNameCharCount
12
12
  fileSizeLimitInBytes
@@ -75,7 +75,8 @@ function getReadAndWriteStreams({
75
75
  const searchAndReplace = async (fileName, pairs, {
76
76
  isImport = true,
77
77
  inPlace = false,
78
- output = process.stdout
78
+ output = process.stdout,
79
+ batchMode = false
79
80
  }, binary = null) => {
80
81
  const dumpDetails = await (0, _database.getSqlDumpDetails)(fileName);
81
82
  const isMyDumper = dumpDetails.type === _database.SqlDumpType.MYDUMPER;
@@ -100,7 +101,7 @@ const searchAndReplace = async (fileName, pairs, {
100
101
  // determine all the replacements required
101
102
  const replacements = pairs.flatMap(pair => pair.split(',').map(str => str.trim()));
102
103
  debug('Pairs: ', pairs, 'Replacements: ', replacements);
103
- if (inPlace) {
104
+ if (inPlace && !batchMode) {
104
105
  const approved = await (0, _prompt.confirm)([], 'Are you sure you want to run search and replace on your input file? This operation is not reversible.');
105
106
 
106
107
  // Bail if user does not wish to proceed
package/docs/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  ## Changelog
2
2
 
3
+ ### 3.10.0
4
+
5
+ * VIP-CLI interactive console: Trim leading and ending spaces in command passed in
6
+ * fix(dev-env): remove `lando/compose/env` directory when environment is destroyed
7
+ * chore: update GraphQL types
8
+ * feat: query integrations and their configuration
9
+ * build(deps-dev): bump dockerode from 4.0.2 to 4.0.4
10
+ * build(deps-dev): bump @types/dockerode from 3.3.32 to 3.3.33
11
+ * feat(dev-env): write integrations config when creating an environment
12
+ * build(deps-dev): bump @types/dockerode from 3.3.33 to 3.3.34
13
+ * refactor(dev-env): merge `dev-tools` into `wordpress`
14
+ * feat(dev-env): Improvements to configuration wizard
15
+ * fix: ignore auth failures when querying feature flags
16
+ * feat(dev-env): automatically enable Elasticsearch when Enterprise Search integration is enabled
17
+ * fix(import-sql): progress tracker is incompatible with interactivity
18
+ * fix(dev-env): add support for colima socket
19
+ * build(deps): bump step-security/harden-runner from 2.10.2 to 2.10.4
20
+ * build(deps-dev): bump typescript from 5.7.2 to 5.7.3
21
+ * build(deps-dev): bump the babel group across 1 directory with 3 updates
22
+ * fix(dev-env): make `wp plugin scaffold` work
23
+
24
+ **Full Changelog**: https://github.com/Automattic/vip-cli/compare/3.9.6...3.10.0
25
+
26
+ ### 3.9.6
27
+
28
+ * Adds check for AUTO_INCREMENT attribute in SQL import
29
+
30
+ **Full Changelog**: https://github.com/Automattic/vip-cli/compare/3.9.5...3.9.6
31
+
3
32
  ### 3.9.5
4
33
 
5
34
  * fix: improve error handling and output