@automattic/vip 3.17.1 → 3.19.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.
Files changed (46) hide show
  1. package/assets/dev-env.lando.template.yml.ejs +1 -1
  2. package/dist/bin/vip-config-envvar-delete.js +1 -0
  3. package/dist/bin/vip-config-envvar-get-all.js +2 -1
  4. package/dist/bin/vip-config-envvar-get.js +1 -0
  5. package/dist/bin/vip-config-envvar-list.js +2 -1
  6. package/dist/bin/vip-config-envvar-set.js +1 -0
  7. package/dist/bin/vip-dev-env-envvar-delete.js +55 -0
  8. package/dist/bin/vip-dev-env-envvar-get-all.js +51 -0
  9. package/dist/bin/vip-dev-env-envvar-get.js +43 -0
  10. package/dist/bin/vip-dev-env-envvar-list.js +46 -0
  11. package/dist/bin/vip-dev-env-envvar-set.js +78 -0
  12. package/dist/bin/vip-dev-env-envvar.js +27 -0
  13. package/dist/bin/vip-dev-env.js +1 -1
  14. package/dist/bin/vip-import-media.js +1 -1
  15. package/dist/bin/vip-import-sql.js +5 -20
  16. package/dist/bin/vip-logs.js +1 -0
  17. package/dist/bin/vip-slowlogs.js +1 -0
  18. package/dist/bin/vip.js +1 -1
  19. package/dist/commands/backup-db.js +2 -5
  20. package/dist/commands/dev-env-sync-sql.js +13 -4
  21. package/dist/commands/phpmyadmin.js +2 -5
  22. package/dist/commands/wp-ssh.js +3 -5
  23. package/dist/lib/analytics/clients/pendo.js +3 -1
  24. package/dist/lib/analytics/clients/tracks.js +0 -1
  25. package/dist/lib/analytics/index.js +0 -1
  26. package/dist/lib/api/http.js +2 -0
  27. package/dist/lib/api/user.js +1 -0
  28. package/dist/lib/backup-storage-availability/backup-storage-availability.js +0 -1
  29. package/dist/lib/cli/format.js +1 -22
  30. package/dist/lib/custom-deploy/custom-deploy.js +2 -2
  31. package/dist/lib/database.js +6 -6
  32. package/dist/lib/dev-environment/dev-environment-cli.js +7 -5
  33. package/dist/lib/dev-environment/dev-environment-core.js +9 -4
  34. package/dist/lib/dev-environment/dev-environment-database.js +4 -0
  35. package/dist/lib/dev-environment/dev-environment-lando.js +37 -17
  36. package/dist/lib/dev-environment/env-vars.js +75 -0
  37. package/dist/lib/keychain/insecure.js +0 -1
  38. package/dist/lib/media-import/config.js +1 -1
  39. package/dist/lib/site-import/status.js +2 -7
  40. package/dist/lib/validations/is-multisite-domain-mapped.js +3 -2
  41. package/dist/lib/validations/line-by-line.js +1 -1
  42. package/docs/CHANGELOG.md +55 -0
  43. package/npm-shrinkwrap.json +3623 -3854
  44. package/package.json +18 -15
  45. package/dist/bin/vip-validate-preflight.js +0 -481
  46. package/dist/bin/vip-validate.js +0 -11
@@ -17,7 +17,6 @@ const client_info = {
17
17
  /* eslint-enable camelcase */
18
18
 
19
19
  class Analytics {
20
- clients;
21
20
  constructor(clients) {
22
21
  this.clients = clients;
23
22
  }
@@ -29,6 +29,8 @@ var _default = async (path, options = {}) => {
29
29
  url = `${_api.API_HOST}${path}`;
30
30
  }
31
31
  const authToken = await _token.default.get();
32
+
33
+ // TODO: remove this cast once the typings are fixed
32
34
  const proxyAgent = (0, _proxyAgent.createProxyAgent)(url);
33
35
  debug('running fetch', url);
34
36
  return (0, _nodeFetch.default)(url, {
@@ -10,6 +10,7 @@ const QUERY_CURRENT_USER = (0, _graphqlTag.default)`
10
10
  me {
11
11
  id
12
12
  displayName
13
+ trackingUserId
13
14
  isVIP
14
15
  organizationRoles {
15
16
  nodes {
@@ -13,7 +13,6 @@ var _format = require("../cli/format");
13
13
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
14
14
  const oneGiBInBytes = 1024 * 1024 * 1024;
15
15
  class BackupStorageAvailability {
16
- archiveSize;
17
16
  constructor(archiveSize) {
18
17
  this.archiveSize = archiveSize;
19
18
  }
@@ -10,8 +10,6 @@ exports.formatEnvironment = formatEnvironment;
10
10
  exports.formatMetricBytes = void 0;
11
11
  exports.formatSearchReplaceValues = formatSearchReplaceValues;
12
12
  exports.getGlyphForStatus = getGlyphForStatus;
13
- exports.isJson = isJson;
14
- exports.isJsonObject = isJsonObject;
15
13
  exports.keyValue = keyValue;
16
14
  exports.requoteArgs = requoteArgs;
17
15
  exports.table = table;
@@ -108,26 +106,7 @@ function keyValue(values) {
108
106
  return lines.join('\n');
109
107
  }
110
108
  function requoteArgs(args) {
111
- return args.map(arg => {
112
- if (arg.includes('--') && arg.includes('=') && arg.includes(' ')) {
113
- return arg.replace(/"/g, '\\"').replace(/^--([^=]*)=(.*)$/, '--$1="$2"');
114
- }
115
- if (arg.includes(' ') && !isJsonObject(arg)) {
116
- return `"${arg.replace(/"/g, '\\"')}"`;
117
- }
118
- return arg;
119
- });
120
- }
121
- function isJsonObject(str) {
122
- return typeof str === 'string' && str.trim().startsWith('{') && isJson(str);
123
- }
124
- function isJson(str) {
125
- try {
126
- JSON.parse(str);
127
- return true;
128
- } catch (error) {
129
- return false;
130
- }
109
+ return args.map(arg => `"${arg.replace(/"/g, '\\"')}"`);
131
110
  }
132
111
  function capitalize(str) {
133
112
  if (typeof str !== 'string' || !str.length) {
@@ -48,7 +48,7 @@ async function validateCustomDeployKey(app, env) {
48
48
  throw new Error('Not found');
49
49
  }
50
50
  return result.data?.validateCustomDeployAccess;
51
- } catch (error) {
51
+ } catch {
52
52
  exit.withError(`Unauthorized: Invalid or non-existent custom deploy key for environment.`);
53
53
  }
54
54
  }
@@ -93,7 +93,7 @@ async function validateFile(appId, envId, fileMeta) {
93
93
  }
94
94
  try {
95
95
  await (0, _clientFileUploader.checkFileAccess)(fileName);
96
- } catch (err) {
96
+ } catch {
97
97
  await track('deploy_app_command_error', {
98
98
  error_type: 'appfile-unreadable'
99
99
  });
@@ -28,15 +28,15 @@ const getSqlDumpDetails = async filePath => {
28
28
  crlfDelay: Infinity
29
29
  });
30
30
  let isMyDumper = false;
31
- let sourceDB = '';
31
+ let sourceDB;
32
32
  let currentLineNumber = 0;
33
33
  for await (const line of readLine) {
34
34
  if (line === '') {
35
35
  continue;
36
36
  }
37
- const metadataMatch = line.match(/^-- metadata.header /);
38
- const sourceDBMatch = line.match(/^-- (.*)-schema-create.sql/) ?? [];
39
- const sourceDBName = sourceDBMatch[1];
37
+ const metadataMatch = /^-- metadata.header /.exec(line);
38
+ const sourceDBMatch = /^-- (.*)-schema-create.sql/.exec(line);
39
+ const sourceDBName = sourceDBMatch?.[1];
40
40
  if (metadataMatch && !isMyDumper) {
41
41
  isMyDumper = true;
42
42
  }
@@ -87,13 +87,13 @@ const getSqlFileStreamFromCompressedFile = async filePath => {
87
87
  throw new Error('Not a supported compressed file');
88
88
  };
89
89
  const fixMyDumperTransform = () => {
90
+ const regex = /^-- ([^ ]+) \d+$/;
90
91
  return new _nodeStream.Transform({
91
92
  transform(chunk, _encoding, callback) {
92
93
  const chunkString = chunk.toString();
93
94
  const lineEnding = chunkString.includes('\r\n') ? '\r\n' : '\n';
94
- const regex = /^-- ([^ ]+) [0-9]+$/;
95
95
  const lines = chunk.toString().split(lineEnding).map(line => {
96
- const match = line.match(regex);
96
+ const match = regex.exec(line);
97
97
  if (!match) {
98
98
  return line;
99
99
  }
@@ -252,7 +252,7 @@ async function promptForArguments(preselectedOptions, defaultOptions, suppressPr
252
252
  if (setMediaRedirectDomain) {
253
253
  instanceData.mediaRedirectDomain = defaultOptions.mediaRedirectDomain;
254
254
  }
255
- } else if (!create && defaultOptions.mediaRedirectDomain) {
255
+ } else if (!create && defaultOptions.mediaRedirectDomain && !preselectedOptions.mediaRedirectDomain) {
256
256
  const mediaRedirectPromptText = 'URL to redirect for missing media files ("n" to disable)?';
257
257
  const mediaRedirectDomain = await promptForURL(mediaRedirectPromptText, defaultOptions.mediaRedirectDomain);
258
258
  instanceData.mediaRedirectDomain = mediaRedirectDomain;
@@ -300,11 +300,13 @@ async function processWordPress(preselectedValue, defaultValue) {
300
300
  if (versions.length) {
301
301
  versions.sort((before, after) => before.tag < after.tag ? 1 : -1);
302
302
  const match = versions.find(({
303
+ prerelease,
303
304
  tag
304
- }) => tag === result.tag);
305
- if (typeof match === 'undefined') {
305
+ }) => result.tag === 'latest' ? !prerelease : tag === result.tag);
306
+ if (match === undefined) {
306
307
  throw new _userError.default(`Unknown or unsupported WordPress version: ${result.tag}.`);
307
308
  }
309
+ result.tag = match.tag;
308
310
  }
309
311
  debug(result);
310
312
  return result;
@@ -677,7 +679,7 @@ function processVersionOption(value) {
677
679
  const phpVersionsSupported = Object.keys(_devEnvironment.DEV_ENVIRONMENT_PHP_VERSIONS).join(', ');
678
680
  function addDevEnvConfigurationOptions(command) {
679
681
  // We leave the third parameter to undefined on some because the defaults are handled in preProcessInstanceData()
680
- return command.option('wordpress', 'Manage the version of WordPress. Accepts a string value for major versions (6.x). Defaults to the most recent version of WordPress.', undefined, processVersionOption).option(['u', 'mu-plugins'], 'Manage the source for VIP MU plugins. Accepts "demo" (default) for a read-only image of the staging branch, or a path to a built copy of VIP MU plugins on the local machine.').option('app-code', 'Manage the source for application code. Accepts "demo" (default) for a read-only image of WordPress VIP skeleton application code, or a path to a VIP formatted application repo on the local machine.').option('phpmyadmin', 'Enable or disable phpMyAdmin, disabled by default. Accepts "y" (default value) to enable or "n" to disable. When enabled, refer to the value of "PHPMYADMIN URLS" in the information output for a local environment for the URL to access phpMyAdmin.', undefined, processBooleanOption).option('xdebug', 'Enable or disable XDebug, disabled by default. Accepts "y" (default value) to enable or "n" to disable.', undefined, processBooleanOption).option('xdebug_config', 'Override some default configuration settings for Xdebug. Accepts a string value that is assigned to the XDEBUG_CONFIG environment variable.').option('elasticsearch', 'Enable or disable Elasticsearch (required by Enterprise Search), disabled by default. Accepts "y" (default value) to enable or "n" to disable.', undefined, processBooleanOption).option(['r', 'media-redirect-domain'], 'Configure media files to be proxied from a VIP Platform environment. Accepts a string value for the primary domain of the VIP Platform environment or "n" to disable the media proxy.', undefined, processMediaRedirectDomainOption).option('php', `Manage the version of PHP. Accepts a string value for minor versions: ${phpVersionsSupported}`, undefined, processVersionOption).option('cron', 'Enable or disable cron, disabled by default. Accepts "y" (default value) to enable or "n" to disable.', undefined, processBooleanOption).option(['A', 'mailpit'], 'Enable or disable Mailpit, disabled by default. Accepts "y" (default value) to enable or "n" to disable.', undefined, processBooleanOption).option(['H', 'photon'], 'Enable or disable Photon, disabled by default. Accepts "y" (default value) to enable or "n" to disable.', undefined, processBooleanOption);
682
+ return command.option('wordpress', 'Manage the version of WordPress. Accepts a string value for major versions (6.x) or "latest". Defaults to the recommended version of WordPress for development.', undefined, processVersionOption).option(['u', 'mu-plugins'], 'Manage the source for VIP MU plugins. Accepts "demo" (default) for a read-only image of the staging branch, or a path to a built copy of VIP MU plugins on the local machine.').option('app-code', 'Manage the source for application code. Accepts "demo" (default) for a read-only image of WordPress VIP skeleton application code, or a path to a VIP formatted application repo on the local machine.').option('phpmyadmin', 'Enable or disable phpMyAdmin, disabled by default. Accepts "y" (default value) to enable or "n" to disable. When enabled, refer to the value of "PHPMYADMIN URLS" in the information output for a local environment for the URL to access phpMyAdmin.', undefined, processBooleanOption).option('xdebug', 'Enable or disable XDebug, disabled by default. Accepts "y" (default value) to enable or "n" to disable.', undefined, processBooleanOption).option('xdebug_config', 'Override some default configuration settings for Xdebug. Accepts a string value that is assigned to the XDEBUG_CONFIG environment variable.').option('elasticsearch', 'Enable or disable Elasticsearch (required by Enterprise Search), disabled by default. Accepts "y" (default value) to enable or "n" to disable.', undefined, processBooleanOption).option(['r', 'media-redirect-domain'], 'Configure media files to be proxied from a VIP Platform environment. Accepts a string value for the primary domain of the VIP Platform environment or "n" to disable the media proxy.', undefined, processMediaRedirectDomainOption).option('php', `Manage the version of PHP. Accepts a string value for minor versions: ${phpVersionsSupported}`, undefined, processVersionOption).option('cron', 'Enable or disable cron, disabled by default. Accepts "y" (default value) to enable or "n" to disable.', undefined, processBooleanOption).option(['A', 'mailpit'], 'Enable or disable Mailpit, disabled by default. Accepts "y" (default value) to enable or "n" to disable.', undefined, processBooleanOption).option(['H', 'photon'], 'Enable or disable Photon, disabled by default. Accepts "y" (default value) to enable or "n" to disable.', undefined, processBooleanOption);
681
683
  }
682
684
 
683
685
  /**
@@ -736,7 +738,7 @@ function getEnvTrackingInfo(slug) {
736
738
  }
737
739
  result.php = result.php.replace(/^[^:]+:/, '');
738
740
  return result;
739
- } catch (err) {
741
+ } catch {
740
742
  return {
741
743
  slug
742
744
  };
@@ -82,6 +82,10 @@ async function startEnvironment(lando, slug, options) {
82
82
  updated = await maybeUpdateWordPressImage(lando, slug);
83
83
  }
84
84
  updated = updated || (await maybeUpdateVersion(lando, slug));
85
+ const proxyContainer = await (0, _devEnvironmentLando.getProxyContainer)(lando);
86
+ if (!proxyContainer?.State.Running) {
87
+ await (0, _devEnvironmentLando.removeProxyCache)(lando);
88
+ }
85
89
  if (options.skipRebuild && !updated) {
86
90
  await (0, _devEnvironmentLando.landoStart)(lando, instancePath);
87
91
  } else {
@@ -265,12 +269,12 @@ function exec(lando, slug, args, options = {}) {
265
269
  return (0, _devEnvironmentLando.landoExec)(lando, instancePath, command, commandArgs, options);
266
270
  }
267
271
  async function doesEnvironmentExist(instancePath) {
268
- debug('Will check for environment at', instancePath);
272
+ debug('Will check for environment at %s', instancePath);
269
273
  const file = _nodePath.default.join(instancePath, instanceDataFileName);
270
274
  try {
271
275
  const stats = await _nodeFs.default.promises.stat(file);
272
276
  return stats.isFile();
273
- } catch (err) {
277
+ } catch {
274
278
  return false;
275
279
  }
276
280
  }
@@ -688,6 +692,7 @@ async function maybeUpdateVersion(lando, slug) {
688
692
  */
689
693
  function fetchVersionList() {
690
694
  const url = `https://${_devEnvironment.DEV_ENVIRONMENT_RAW_GITHUB_HOST}${_devEnvironment.DEV_ENVIRONMENT_WORDPRESS_VERSIONS_URI}`;
695
+ // TODO: remove this cast once the typings are fixed
691
696
  const proxyAgent = (0, _proxyAgent.createProxyAgent)(url);
692
697
  return (0, _nodeFetch.default)(url, {
693
698
  agent: proxyAgent ?? undefined
@@ -708,7 +713,8 @@ async function isVersionListExpired(cacheFile, ttl) {
708
713
  } = await _nodeFs.default.promises.stat(cacheFile);
709
714
  expire.setSeconds(expire.getSeconds() + ttl);
710
715
  return Number(new Date()) > Number(expire);
711
- } catch (err) {
716
+ } catch (error) {
717
+ debug(`Error checking version list expiration for cache file ${cacheFile}:`, error);
712
718
  return true;
713
719
  }
714
720
  }
@@ -840,7 +846,6 @@ function getVSCodeWorkspacePath(slug) {
840
846
  function generatePHPStormWorkspace(slug) {
841
847
  debug('Generating PHPStorm Workspace');
842
848
  const location = getEnvironmentPath(slug);
843
- // const location = location;
844
849
  const instanceData = readEnvironmentData(slug);
845
850
  const pathMappings = generatePathMappings(location, instanceData);
846
851
 
@@ -2,8 +2,11 @@
2
2
 
3
3
  exports.__esModule = true;
4
4
  exports.reIndexSearch = exports.generatePassword = exports.flushCache = exports.executeQuery = exports.dataCleanup = exports.addAdminUser = void 0;
5
+ var _debug = _interopRequireDefault(require("debug"));
5
6
  var _nodeCrypto = require("node:crypto");
6
7
  var _devEnvironmentCore = require("./dev-environment-core");
8
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
9
+ const debug = (0, _debug.default)('@automattic/vip:bin:dev-environment');
7
10
  const generatePassword = () => {
8
11
  const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_';
9
12
  const passwordLength = 12;
@@ -36,6 +39,7 @@ const dataCleanup = async (lando, slug, quiet) => {
36
39
  } catch (error) {
37
40
  // This must not be a fatal error
38
41
  console.log('WARNING: data cleanup failed.');
42
+ debug('Error during data cleanup:', error);
39
43
  }
40
44
  };
41
45
  exports.dataCleanup = dataCleanup;
@@ -3,6 +3,7 @@
3
3
  exports.__esModule = true;
4
4
  exports.bootstrapLando = bootstrapLando;
5
5
  exports.checkEnvHealth = checkEnvHealth;
6
+ exports.getProxyContainer = getProxyContainer;
6
7
  exports.isEnvUp = isEnvUp;
7
8
  exports.landoDestroy = landoDestroy;
8
9
  exports.landoExec = landoExec;
@@ -12,6 +13,7 @@ exports.landoRebuild = landoRebuild;
12
13
  exports.landoShell = landoShell;
13
14
  exports.landoStart = landoStart;
14
15
  exports.landoStop = landoStop;
16
+ exports.removeProxyCache = removeProxyCache;
15
17
  exports.validateDockerInstalled = validateDockerInstalled;
16
18
  var _chalk = _interopRequireDefault(require("chalk"));
17
19
  var _debug = _interopRequireDefault(require("debug"));
@@ -63,7 +65,7 @@ async function getLandoConfig() {
63
65
  await (0, _promises2.mkdir)(fakeHomeDir, {
64
66
  recursive: true
65
67
  });
66
- } catch (err) {
68
+ } catch {
67
69
  // Ignore
68
70
  }
69
71
  const config = {
@@ -110,7 +112,8 @@ async function regenerateLandofile(lando, instancePath) {
110
112
  const backup = `${landoFile}.${now}`;
111
113
  await (0, _promises2.rename)(landoFile, backup);
112
114
  console.warn(_chalk.default.yellow('Backed up %s to %s'), landoFile, backup);
113
- } catch (err) {
115
+ } catch (error) {
116
+ debug(`Failed to backup lando file ${landoFile}:`, error);
114
117
  // Rename failed - possibly the file does not exist. Silently ignoring.
115
118
  }
116
119
  const slug = _nodePath.default.basename(instancePath);
@@ -246,6 +249,17 @@ async function getBridgeNetwork(lando) {
246
249
  return null;
247
250
  }
248
251
  }
252
+ async function removeProxyCache(lando) {
253
+ const {
254
+ userConfRoot,
255
+ proxyCache
256
+ } = lando.config;
257
+ try {
258
+ await (0, _promises2.unlink)(_nodePath.default.join(userConfRoot, 'cache', proxyCache ?? 'proxyCache'));
259
+ } catch {
260
+ // Swallow
261
+ }
262
+ }
249
263
  async function cleanUpLandoProxy(lando) {
250
264
  const network = await getBridgeNetwork(lando);
251
265
  if (network?.Containers && Object.keys(network.Containers).length === 1) {
@@ -257,6 +271,7 @@ async function cleanUpLandoProxy(lando) {
257
271
  } catch (err) {
258
272
  debug('Error removing proxy container: %s', err.message);
259
273
  }
274
+ await removeProxyCache(lando);
260
275
  }
261
276
  }
262
277
  async function landoStop(lando, instancePath) {
@@ -395,7 +410,7 @@ async function tryResolveDomains(urls) {
395
410
  const domains = [...new Set(urls.filter(url => url.toLowerCase().startsWith('http')).map(url => {
396
411
  try {
397
412
  return new URL(url).hostname;
398
- } catch (err) {
413
+ } catch {
399
414
  return undefined;
400
415
  }
401
416
  }).filter(domain => domain !== undefined))];
@@ -525,6 +540,20 @@ async function landoShell(lando, instancePath, service, user, command) {
525
540
  _app: app
526
541
  });
527
542
  }
543
+ async function getProxyContainer(lando) {
544
+ const proxyContainerName = lando.config.proxyContainer;
545
+ const {
546
+ docker
547
+ } = lando.engine;
548
+ const containers = await docker.listContainers({
549
+ all: true
550
+ });
551
+ if (containers.some(container => container.Names.includes(`/${proxyContainerName}`))) {
552
+ const container = docker.getContainer(proxyContainerName);
553
+ return container.inspect();
554
+ }
555
+ return null;
556
+ }
528
557
 
529
558
  /**
530
559
  * Sometimes the proxy network seems to disapper leaving only orphant stopped proxy container.
@@ -535,21 +564,12 @@ async function landoShell(lando, instancePath, service, user, command) {
535
564
  * can safely add a network and a new proxy container.
536
565
  */
537
566
  async function ensureNoOrphantProxyContainer(lando) {
538
- const proxyContainerName = lando.config.proxyContainer;
539
- const docker = lando.engine.docker;
540
- const containers = await docker.listContainers({
541
- all: true
542
- });
543
- const proxyContainerExists = containers.some(container => container.Names.includes(`/${proxyContainerName}`));
544
- if (!proxyContainerExists) {
545
- return;
546
- }
547
- const proxyContainer = docker.getContainer(proxyContainerName);
548
- const status = await proxyContainer.inspect();
549
- if (status.State.Running) {
550
- return;
567
+ const proxyContainer = await getProxyContainer(lando);
568
+ if (proxyContainer && !proxyContainer.State.Running) {
569
+ const containerName = proxyContainer.Name.startsWith('/') ? proxyContainer.Name.slice(1) : proxyContainer.Name;
570
+ const container = lando.engine.docker.getContainer(containerName);
571
+ await container.remove();
551
572
  }
552
- await proxyContainer.remove();
553
573
  }
554
574
  function validateDockerInstalled(lando) {
555
575
  const {
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+
3
+ exports.__esModule = true;
4
+ exports.getEnvFilePath = getEnvFilePath;
5
+ exports.parseEnvValue = parseEnvValue;
6
+ exports.preparseEnvData = preparseEnvData;
7
+ exports.quoteEnvValue = quoteEnvValue;
8
+ exports.readEnvFile = readEnvFile;
9
+ exports.updateEnvFile = updateEnvFile;
10
+ var _promises = require("node:fs/promises");
11
+ var _nodePath = require("node:path");
12
+ var _devEnvironmentCore = require("./dev-environment-core");
13
+ var _devEnvironment = require("../constants/dev-environment");
14
+ function preparseEnvData(data) {
15
+ return data.split(/\r?\n/).map(line => line.trim()).filter(line => line && !line.startsWith('#'));
16
+ }
17
+ function parseEnvValue(value) {
18
+ if (value.startsWith('"') && value.endsWith('"')) {
19
+ return value.slice(1, -1).replace(/\\(["$\\nrt])/g, (match, char) => {
20
+ switch (char) {
21
+ case '"':
22
+ case '$':
23
+ case '\\':
24
+ return char;
25
+ case 'n':
26
+ return '\n';
27
+ case 'r':
28
+ return '\r';
29
+ case 't':
30
+ return '\t';
31
+ default:
32
+ return match;
33
+ }
34
+ });
35
+ }
36
+ if (value.startsWith("'") && value.endsWith("'")) {
37
+ return value.slice(1, -1).replace(/\\'/g, "'");
38
+ }
39
+ return value;
40
+ }
41
+ function quoteEnvValue(value) {
42
+ return `"${value.replace(/[\\"$\n\r\t]/g, match => {
43
+ switch (match) {
44
+ case '\\':
45
+ case '"':
46
+ case '$':
47
+ return '\\' + match;
48
+ case '\n':
49
+ return '\\n';
50
+ case '\r':
51
+ return '\\r';
52
+ case '\t':
53
+ return '\\t';
54
+ default:
55
+ return match;
56
+ }
57
+ })}"`;
58
+ }
59
+ async function getEnvFilePath(slug, checkEnvExists = true) {
60
+ const environmentPath = (0, _devEnvironmentCore.getEnvironmentPath)(slug);
61
+ if (checkEnvExists && !(await (0, _devEnvironmentCore.doesEnvironmentExist)(environmentPath))) {
62
+ throw new Error(_devEnvironment.DEV_ENVIRONMENT_NOT_FOUND);
63
+ }
64
+ return (0, _nodePath.join)(environmentPath, '.env');
65
+ }
66
+ async function readEnvFile(slug) {
67
+ const name = await getEnvFilePath(slug);
68
+ const content = await (0, _promises.readFile)(name, 'utf-8');
69
+ return preparseEnvData(content);
70
+ }
71
+ async function updateEnvFile(slug, content) {
72
+ const name = await getEnvFilePath(slug, false);
73
+ await (0, _promises.writeFile)(`${name}.tmp`, content, 'utf-8');
74
+ await (0, _promises.rename)(`${name}.tmp`, name);
75
+ }
@@ -5,7 +5,6 @@ exports.default = void 0;
5
5
  var _configstore = _interopRequireDefault(require("configstore"));
6
6
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
7
7
  class Insecure {
8
- file;
9
8
  configstore;
10
9
  constructor(file) {
11
10
  this.file = file;
@@ -21,5 +21,5 @@ async function getMediaImportConfig() {
21
21
  variables: {},
22
22
  fetchPolicy: 'network-only'
23
23
  });
24
- return response?.data?.mediaImportConfig;
24
+ return response?.data?.mediaImportConfig ?? null;
25
25
  }
@@ -289,14 +289,9 @@ ${maybeExitPrompt}
289
289
  importInProgress,
290
290
  importStepProgress
291
291
  });
292
- let jobCreationTime;
293
- try {
294
- jobCreationTime = new Date(createdAt ?? '').getTime();
295
- } catch (err) {
296
- debug('Unable to parse createdAt to a Date');
297
- }
292
+ const jobCreationTime = new Date(createdAt ?? '').getTime();
298
293
  let failedImportStep;
299
- if (jobCreationTime && (importStepProgress?.started_at ?? 0) * 1000 >= jobCreationTime) {
294
+ if (!isNaN(jobCreationTime) && (importStepProgress?.started_at ?? 0) * 1000 >= jobCreationTime) {
300
295
  // The contents of the `import_progress` meta are pertinent to the most recent import job
301
296
  failedImportStep = importStepProgress?.steps?.find(step => step?.result === 'failed' && 1000 * (step.started_at ?? 0) > new Date(createdAt ?? '').getTime());
302
297
  }
@@ -20,8 +20,9 @@ const getPrimaryDomainFromSQL = statements => {
20
20
  if (!statements.length) {
21
21
  return '';
22
22
  }
23
- const SQL_WP_SITE_DOMAINS_REGEX = /\(1,'(.*?)'/s;
24
- const matches = statements[0]?.join('').replace(/\s/g, '').match(SQL_WP_SITE_DOMAINS_REGEX);
23
+ const SQL_WP_SITE_DOMAINS_REGEX = /\(1,'([^']+)'/s;
24
+ const normalizedSql = statements[0]?.join('').replace(/\s/g, '') ?? '';
25
+ const matches = SQL_WP_SITE_DOMAINS_REGEX.exec(normalizedSql);
25
26
  return matches ? matches[1] : '';
26
27
  };
27
28
 
@@ -15,7 +15,7 @@ async function getReadInterface(filename) {
15
15
  let fd;
16
16
  try {
17
17
  fd = await (0, _promises.open)(filename);
18
- } catch (err) {
18
+ } catch {
19
19
  exit.withError('The file at the provided path is either missing or not readable. Please check the input and try again.');
20
20
  }
21
21
  return (0, _nodeReadline.createInterface)({
package/docs/CHANGELOG.md CHANGED
@@ -1,5 +1,60 @@
1
1
  ## Changelog
2
2
 
3
+ ### 3.19.0
4
+
5
+ * build(deps-dev): bump the testing group across 1 directory with 4 updates
6
+ * New develop release: 3.18.1-dev.0 by @gi
7
+ * build(deps-dev): bump the testing group with 3 updates
8
+ * Remove preflight as part of the harmonia deprecation
9
+ * Add changelog workflow
10
+ * Changelogs: Update `vip-actions`
11
+ * build(deps-dev): bump @types/dockerode from 3.3.41 to 3.3.42
12
+ * build(deps-dev): bump the testing group with 2 updates
13
+ * build(deps): bump step-security/harden-runner from 2.12.1 to 2.12.2
14
+ * build(deps-dev): bump @babel/core from 7.27.4 to 7.27.7 in the babel group
15
+ * build(deps-dev): bump the babel group with 3 updates
16
+ * build(deps-dev): bump @types/shelljs from 0.8.16 to 0.8.17
17
+ * build(deps-dev): bump the testing group with 3 updates
18
+ * chore(deps): update Lando to fix PLTFRM-1195
19
+ * build(deps): bump socks-proxy-agent from 5.0.1 to 8.0.5
20
+ * build(deps): bump https-proxy-agent from 5.0.1 to 7.0.6
21
+ * build(deps-dev): bump @types/node from 24.0.3 to 24.0.10
22
+ * feat(dev-env): allow `latest` as WP version
23
+ * feat(dev-env): add some logging to `DevEnvSyncSQLCommand`
24
+ * Update to the description of the `--wordpress` option for `dev-env`
25
+ * build(deps-dev): bump @types/node from 24.0.10 to 24.0.12
26
+ * fix: drop superfluous `as unknown` casts
27
+ * fix: exception handling-related code smells
28
+ * fix: Promise rejection is not caught by `try`
29
+ * fix: RegExp-related issues
30
+ * fix: fields that are only assigned in the constructor should be "readonly"
31
+ * fix(dev-env): make URL extraction less error-prone
32
+
33
+ **Full Changelog**: https://github.com/Automattic/vip-cli/compare/3.18.0...3.19.0
34
+
35
+ ### 3.18.0
36
+
37
+ * fix: properly quote parameters for `vip wp`
38
+ * build(deps-dev): bump dockerode from 4.0.6 to 4.0.7
39
+ * build(deps-dev): bump @types/node from 22.15.29 to 22.15.30
40
+ * build(deps-dev): bump @types/dockerode from 3.3.39 to 3.3.40
41
+ * build(deps-dev): bump @types/node from 22.15.30 to 24.0.0
42
+ * build(deps): bump step-security/harden-runner from 2.12.0 to 2.12.1
43
+ * build(deps-dev): bump @types/node from 24.0.0 to 24.0.1
44
+ * fix(dev-env): clear proxy cache before startup and after shutdown
45
+ * feat(dev-env): add `vip dev-env envvar` commands
46
+ * chore(deps): update `lando` to the latest version
47
+ * Remove MD5 requirement restrictions for URL imports in vip-import-sql
48
+ * build(deps-dev): bump @types/node from 24.0.1 to 24.0.3
49
+ * build(deps-dev): bump @types/dockerode from 3.3.40 to 3.3.41
50
+ * chore(deps): update `brace-expansion` to fix CVE-2025-5889
51
+ * Bump ES to 8.18.2
52
+ * fix: suppress prompt for media redirect with config files
53
+ * Add changelog section to PR template
54
+ * Update/Org salesforceId
55
+
56
+ **Full Changelog**: https://github.com/Automattic/vip-cli/compare/3.17.1...3.18.0
57
+
3
58
  ### 3.17.1
4
59
 
5
60
  * chore: Update Git Hash Checker Github Action