@automattic/vip 2.37.0-dev.0 → 2.38.0-dev.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 (40) hide show
  1. package/assets/dev-env.lando.template.yml.ejs +1 -1
  2. package/dist/bin/vip-app-deploy.js +251 -0
  3. package/dist/bin/vip-app.js +1 -1
  4. package/dist/bin/vip-dev-env-create.js +2 -3
  5. package/dist/bin/vip-dev-env-destroy.js +1 -1
  6. package/dist/bin/vip-dev-env-exec.js +1 -1
  7. package/dist/bin/vip-dev-env-import-media.js +1 -1
  8. package/dist/bin/vip-dev-env-import-sql.js +1 -1
  9. package/dist/bin/vip-dev-env-info.js +1 -1
  10. package/dist/bin/vip-dev-env-logs.js +1 -1
  11. package/dist/bin/vip-dev-env-shell.js +1 -1
  12. package/dist/bin/vip-dev-env-start.js +1 -1
  13. package/dist/bin/vip-dev-env-stop.js +1 -1
  14. package/dist/bin/vip-dev-env-sync-sql.js +1 -1
  15. package/dist/bin/vip-dev-env-update.js +1 -1
  16. package/dist/bin/vip-import-sql.js +1 -1
  17. package/dist/bin/vip-wp.js +1 -1
  18. package/dist/commands/dev-env-import-sql.js +18 -15
  19. package/dist/commands/dev-env-sync-sql.js +3 -2
  20. package/dist/commands/phpmyadmin.js +112 -2
  21. package/dist/lib/cli/command.js +28 -19
  22. package/dist/lib/cli/exit.js +7 -2
  23. package/dist/lib/cli/format.js +2 -2
  24. package/dist/lib/client-file-uploader.js +22 -13
  25. package/dist/lib/constants/dev-environment.js +2 -10
  26. package/dist/lib/custom-deploy/custom-deploy.js +95 -0
  27. package/dist/lib/dev-environment/dev-environment-cli.js +13 -5
  28. package/dist/lib/dev-environment/dev-environment-configuration-file.js +57 -34
  29. package/dist/lib/dev-environment/dev-environment-core.js +20 -7
  30. package/dist/lib/dev-environment/docker-utils.js +1 -1
  31. package/dist/lib/env.js +2 -1
  32. package/dist/lib/user-error.js +2 -2
  33. package/dist/lib/validations/custom-deploy.js +39 -0
  34. package/dist/lib/validations/line-by-line.js +3 -1
  35. package/dist/lib/validations/sql.js +1 -1
  36. package/docs/CHANGELOG.md +44 -0
  37. package/npm-shrinkwrap.json +274 -628
  38. package/package.json +7 -5
  39. package/schema.gql +20174 -0
  40. package/tsconfig.json +2 -2
@@ -4,50 +4,49 @@ exports.__esModule = true;
4
4
  exports.CONFIGURATION_FILE_NAME = void 0;
5
5
  exports.getConfigurationFileOptions = getConfigurationFileOptions;
6
6
  exports.mergeConfigurationFileOptions = mergeConfigurationFileOptions;
7
- exports.printConfigurationFile = printConfigurationFile;
8
7
  var _chalk = _interopRequireDefault(require("chalk"));
9
8
  var _debug = _interopRequireDefault(require("debug"));
10
- var _fs = require("fs");
11
9
  var _jsYaml = _interopRequireWildcard(require("js-yaml"));
12
10
  var _promises = require("node:fs/promises");
13
11
  var _nodePath = _interopRequireDefault(require("node:path"));
12
+ var _devEnvironmentCli = require("./dev-environment-cli");
14
13
  var exit = _interopRequireWildcard(require("../cli/exit"));
15
14
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
16
15
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
17
16
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
18
17
  const debug = (0, _debug.default)('@automattic/vip:bin:dev-environment');
19
- const CONFIGURATION_FILE_NAME = exports.CONFIGURATION_FILE_NAME = '.vip-dev-env.yml';
18
+ const CONFIGURATION_FILE_NAME = exports.CONFIGURATION_FILE_NAME = 'vip-dev-env.yml';
20
19
  async function getConfigurationFileOptions() {
21
- const configurationFilePath = _nodePath.default.join(process.cwd(), CONFIGURATION_FILE_NAME);
22
- let configurationFileContents = '';
23
- const fileExists = await (0, _promises.access)(configurationFilePath, _fs.constants.R_OK).then(() => true).catch(() => false);
24
- if (fileExists) {
25
- debug('Reading configuration file from:', configurationFilePath);
26
- configurationFileContents = await (0, _promises.readFile)(configurationFilePath, 'utf8');
27
- } else {
20
+ const configurationFile = await findConfigurationFile();
21
+ if (configurationFile === false) {
28
22
  return {};
29
23
  }
24
+ const {
25
+ configurationPath,
26
+ configurationContents
27
+ } = configurationFile;
30
28
  let configurationFromFile = {};
31
29
  try {
32
- configurationFromFile = _jsYaml.default.load(configurationFileContents, {
30
+ configurationFromFile = _jsYaml.default.load(configurationContents, {
33
31
  // Only allow strings, arrays, and objects to be parsed from configuration file
34
32
  // This causes number-looking values like `php: 8.1` to be parsed directly into strings
35
33
  schema: _jsYaml.FAILSAFE_SCHEMA
36
34
  });
37
35
  } catch (err) {
38
- const messageToShow = `Configuration file ${_chalk.default.grey(CONFIGURATION_FILE_NAME)} could not be loaded:\n` + err.toString();
36
+ const messageToShow = `Configuration file ${_chalk.default.grey(configurationPath)} could not be loaded:\n` + err.toString();
39
37
  exit.withError(messageToShow);
40
38
  }
41
39
  try {
42
- const configuration = sanitizeConfiguration(configurationFromFile);
40
+ let configuration = sanitizeConfiguration(configurationFromFile, configurationPath);
41
+ configuration = adjustRelativePaths(configuration, configurationPath);
43
42
  debug('Sanitized configuration from file:', configuration);
44
43
  return configuration;
45
44
  } catch (err) {
46
45
  exit.withError(err instanceof Error ? err : new Error('Unknown error'));
47
46
  }
48
47
  }
49
- function sanitizeConfiguration(configuration) {
50
- const genericConfigurationError = `Configuration file ${_chalk.default.grey(CONFIGURATION_FILE_NAME)} is available but ` + `couldn't be loaded. Ensure there is a ${_chalk.default.cyan('configuration-version')} and ${_chalk.default.cyan('slug')} ` + `configured. For example:\n\n${_chalk.default.grey(getConfigurationFileExample())}`;
48
+ function sanitizeConfiguration(configuration, configurationFilePath) {
49
+ const genericConfigurationError = `Configuration file ${_chalk.default.grey(configurationFilePath)} is available but ` + `couldn't be loaded. Ensure there is a ${_chalk.default.cyan('configuration-version')} and ${_chalk.default.cyan('slug')} ` + `configured. For example:\n\n${_chalk.default.grey(getConfigurationFileExample())}`;
51
50
  if (Array.isArray(configuration) || typeof configuration !== 'object') {
52
51
  throw new Error(genericConfigurationError);
53
52
  }
@@ -57,7 +56,7 @@ function sanitizeConfiguration(configuration) {
57
56
  }
58
57
  const validVersions = getAllConfigurationFileVersions().map(ver => _chalk.default.cyan(ver)).join(', ');
59
58
  if (!isValidConfigurationFileVersion(version.toString())) {
60
- throw new Error(`Configuration file ${_chalk.default.grey(CONFIGURATION_FILE_NAME)} has an invalid ` + `${_chalk.default.cyan('configuration-version')} key. Update to a supported version. For example:\n\n` + _chalk.default.grey(getConfigurationFileExample()) + `\nSupported configuration versions: ${validVersions}.\n`);
59
+ throw new Error(`Configuration file ${_chalk.default.grey(configurationFilePath)} has an invalid ` + `${_chalk.default.cyan('configuration-version')} key. Update to a supported version. For example:\n\n` + _chalk.default.grey(getConfigurationFileExample()) + `\nSupported configuration versions: ${validVersions}.\n`);
61
60
  }
62
61
  const stringToBooleanIfDefined = value => {
63
62
  if (typeof value !== 'string' || !['true', 'false'].includes(value)) {
@@ -65,6 +64,9 @@ function sanitizeConfiguration(configuration) {
65
64
  }
66
65
  return value === 'true';
67
66
  };
67
+ const configurationMeta = {
68
+ 'configuration-path': configurationFilePath
69
+ };
68
70
  const sanitizedConfiguration = {
69
71
  version: version.toString(),
70
72
  // eslint-disable-next-line @typescript-eslint/no-base-to-string
@@ -81,7 +83,8 @@ function sanitizeConfiguration(configuration) {
81
83
  xdebug: stringToBooleanIfDefined(configuration.xdebug),
82
84
  mailpit: stringToBooleanIfDefined(configuration.mailpit ?? configuration.mailhog),
83
85
  'media-redirect-domain': configuration['media-redirect-domain']?.toString(),
84
- photon: stringToBooleanIfDefined(configuration.photon)
86
+ photon: stringToBooleanIfDefined(configuration.photon),
87
+ meta: configurationMeta
85
88
  };
86
89
 
87
90
  // Remove undefined values
@@ -90,6 +93,16 @@ function sanitizeConfiguration(configuration) {
90
93
  key => sanitizedConfiguration[key] === undefined && delete sanitizedConfiguration[key]);
91
94
  return sanitizedConfiguration;
92
95
  }
96
+ function adjustRelativePaths(configuration, configurationFilePath) {
97
+ const configurationDirectory = _nodePath.default.resolve(_nodePath.default.dirname(configurationFilePath));
98
+ if (configuration['app-code'] && configuration['app-code'] !== 'image') {
99
+ configuration['app-code'] = _nodePath.default.join(configurationDirectory, configuration['app-code']);
100
+ }
101
+ if (configuration['mu-plugins'] && configuration['mu-plugins'] !== 'image') {
102
+ configuration['mu-plugins'] = _nodePath.default.join(configurationDirectory, configuration['mu-plugins']);
103
+ }
104
+ return configuration;
105
+ }
93
106
  function mergeConfigurationFileOptions(preselectedOptions, configurationFileOptions) {
94
107
  // configurationFileOptions holds different parameters than present in
95
108
  // preselectedOptions like "slug", and friendly-named parameters (e.g.
@@ -121,21 +134,28 @@ function mergeConfigurationFileOptions(preselectedOptions, configurationFileOpti
121
134
  });
122
135
  return mergedOptions;
123
136
  }
124
- function printConfigurationFile(configurationOptions) {
125
- const isConfigurationFileEmpty = Object.keys(configurationOptions).length === 0;
126
- if (isConfigurationFileEmpty) {
127
- return;
128
- }
137
+ async function findConfigurationFile() {
138
+ let currentPath = process.cwd();
139
+ const rootPath = _nodePath.default.parse(currentPath).root;
140
+ let depth = 0;
141
+ const maxDepth = 64;
142
+ const pathPromises = [];
143
+ while (currentPath !== rootPath && depth < maxDepth) {
144
+ const configurationPath = _nodePath.default.join(currentPath, _devEnvironmentCli.CONFIGURATION_FOLDER, CONFIGURATION_FILE_NAME);
145
+ pathPromises.push((0, _promises.readFile)(configurationPath, 'utf8').then(configurationContents => ({
146
+ configurationPath,
147
+ configurationContents
148
+ })));
149
+
150
+ // Move up one directory
151
+ currentPath = _nodePath.default.dirname(currentPath);
129
152
 
130
- // Customized formatter because Lando's printTable() automatically uppercases keys
131
- // which may be confusing for YAML configuration
132
- const settingLines = [];
133
- for (const [key, value] of Object.entries(configurationOptions)) {
134
- settingLines.push(`${_chalk.default.cyan(key)}: ${String(value)}`);
153
+ // Use depth as a sanity check to avoid an infitite loop
154
+ depth++;
135
155
  }
136
- console.log(settingLines.join('\n') + '\n');
156
+ return Promise.any(pathPromises).catch(() => false);
137
157
  }
138
- const CONFIGURATION_FILE_VERSIONS = ['0.preview-unstable'];
158
+ const CONFIGURATION_FILE_VERSIONS = ['1'];
139
159
  function getAllConfigurationFileVersions() {
140
160
  return CONFIGURATION_FILE_VERSIONS;
141
161
  }
@@ -148,13 +168,16 @@ function isValidConfigurationFileVersion(version) {
148
168
  function getConfigurationFileExample() {
149
169
  return `configuration-version: ${getLatestConfigurationFileVersion()}
150
170
  slug: dev-site
171
+ title: Dev Site
151
172
  php: 8.0
152
- wordpress: 6.0
153
- app-code: ./site-code
173
+ wordpress: 6.2
174
+ app-code: ../
154
175
  mu-plugins: image
155
176
  multisite: false
156
- phpmyadmin: true
157
- elasticsearch: true
158
- xdebug: true
177
+ phpmyadmin: false
178
+ elasticsearch: false
179
+ xdebug: false
180
+ mailpit: false
181
+ photon: false
159
182
  `;
160
183
  }
@@ -29,7 +29,6 @@ var _ejs = _interopRequireDefault(require("ejs"));
29
29
  var _enquirer = require("enquirer");
30
30
  var _nodeFetch = _interopRequireDefault(require("node-fetch"));
31
31
  var _nodeFs = _interopRequireDefault(require("node:fs"));
32
- var _nodeOs = _interopRequireDefault(require("node:os"));
33
32
  var _nodePath = _interopRequireDefault(require("node:path"));
34
33
  var _semver = _interopRequireDefault(require("semver"));
35
34
  var _uuid = require("uuid");
@@ -53,7 +52,13 @@ const instanceDataFileName = 'instance_data.json';
53
52
  const uploadPathString = 'uploads';
54
53
  const nginxPathString = 'nginx';
55
54
  function xdgDataDirectory() {
56
- return _xdgBasedir.default.data?.length ? _xdgBasedir.default.data : _nodeOs.default.tmpdir();
55
+ if (_xdgBasedir.default.data) {
56
+ return _xdgBasedir.default.data;
57
+ }
58
+
59
+ // This should not happen. If it does, this means that the system was unable to find user's home directory.
60
+ // If so, this does not leave us many options as to where to store the data.
61
+ throw new Error('Unable to determine data directory.');
57
62
  }
58
63
  async function startEnvironment(lando, slug, options) {
59
64
  debug('Will start an environment', slug);
@@ -273,13 +278,17 @@ function readEnvironmentData(slug) {
273
278
  instanceDataString = _nodeFs.default.readFileSync(instanceDataTargetPath, 'utf8');
274
279
  } catch (error) {
275
280
  const err = error;
276
- throw new _userError.default(`There was an error reading file "${instanceDataTargetPath}": ${err.message}.`);
281
+ throw new _userError.default(`There was an error reading file "${instanceDataTargetPath}": ${err.message}.`, {
282
+ cause: error
283
+ });
277
284
  }
278
285
  try {
279
286
  instanceData = JSON.parse(instanceDataString);
280
287
  } catch (error) {
281
288
  const err = error;
282
- throw new _userError.default(`There was an error parsing file "${instanceDataTargetPath}": ${err.message}. You may need to recreate the environment.`);
289
+ throw new _userError.default(`There was an error parsing file "${instanceDataTargetPath}": ${err.message}. You may need to recreate the environment.`, {
290
+ cause: error
291
+ });
283
292
  }
284
293
 
285
294
  /**
@@ -334,10 +343,14 @@ async function prepareLandoEnv(instanceData, instancePath) {
334
343
  await _nodeFs.default.promises.mkdir(nginxFolderPath, {
335
344
  recursive: true
336
345
  });
337
- const landoFileExists = await _nodeFs.default.promises.stat(landoFileTargetPath).catch(() => false);
338
- if (landoFileExists) {
339
- await _nodeFs.default.promises.copyFile(landoFileTargetPath, landoBackupFileTargetPath);
346
+ try {
347
+ await _nodeFs.default.promises.rename(landoFileTargetPath, landoBackupFileTargetPath);
340
348
  console.log(`Backup of ${landoFileName} was created in ${landoBackupFileTargetPath}`);
349
+ } catch (err) {
350
+ // If the file doesn't exist, that's fine. Otherwise, throw the error.
351
+ if ('ENOENT' !== err.code) {
352
+ throw err;
353
+ }
341
354
  }
342
355
  await Promise.all([_nodeFs.default.promises.writeFile(landoFileTargetPath, landoFile), _nodeFs.default.promises.writeFile(nginxFileTargetPath, nginxFile), _nodeFs.default.promises.writeFile(instanceDataTargetPath, instanceDataFile)]);
343
356
  debug(`Lando file created in ${landoFileTargetPath}`);
@@ -79,7 +79,7 @@ async function getDockerSocket() {
79
79
  async function getEngineConfig(dockerHost) {
80
80
  const opts = {};
81
81
  if (dockerHost.startsWith('tcp://')) {
82
- const split = /(?:tcp:\/\/)?(.*?):(\d+)/g.exec(dockerHost);
82
+ const split = /^(?:tcp:\/\/)([^:]+):(\d+)/g.exec(dockerHost);
83
83
  if (split && split.length === 3) {
84
84
  opts.host = split[1];
85
85
  opts.port = split[2];
package/dist/lib/env.js CHANGED
@@ -11,7 +11,8 @@ const app = {
11
11
  };
12
12
  const os = {
13
13
  name: (0, _nodeOs.platform)(),
14
- version: (0, _nodeOs.release)()
14
+ version: (0, _nodeOs.release)(),
15
+ arch: (0, _nodeOs.arch)()
15
16
  };
16
17
  const node = {
17
18
  version: process.version
@@ -3,8 +3,8 @@
3
3
  exports.__esModule = true;
4
4
  exports.default = void 0;
5
5
  class UserError extends Error {
6
- constructor(message) {
7
- super(message);
6
+ constructor(message, options) {
7
+ super(message, options);
8
8
  this.name = 'UserError';
9
9
  }
10
10
  }
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+
3
+ exports.__esModule = true;
4
+ exports.validateDeployFileExt = validateDeployFileExt;
5
+ exports.validateFilename = validateFilename;
6
+ var _path = _interopRequireDefault(require("path"));
7
+ var exit = _interopRequireWildcard(require("../../lib/cli/exit"));
8
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
9
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
10
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11
+ /**
12
+ * Check if a file has a valid extension
13
+ *
14
+ * @param {string} filename The file extension
15
+ * @returns {boolean} True if the extension is valid
16
+ */
17
+ function validateDeployFileExt(filename) {
18
+ let ext = _path.default.extname(filename).toLowerCase();
19
+ if (ext === '.gz' && _path.default.extname(_path.default.basename(filename, ext)) === '.tar') {
20
+ ext = '.tar.gz';
21
+ }
22
+ if (!['.zip', '.tar.gz', '.tgz'].includes(ext)) {
23
+ exit.withError('Invalid file extension. Please provide a .zip, .tar.gz, or a .tgz file.');
24
+ }
25
+ }
26
+
27
+ /**
28
+ * Check if a file has a valid name
29
+ * @param {string} filename The file name
30
+ * @returns {boolean} True if the filename is valid
31
+ */
32
+ function validateFilename(filename) {
33
+ const re = /^[a-z0-9\-_.]+$/i;
34
+
35
+ // Exits if filename contains anything outside a-z A-Z - _ .
36
+ if (!re.test(filename)) {
37
+ exit.withError('Error: The characters used in the name of a file for custom deploys are limited to [0-9,a-z,A-Z,-,_,.]');
38
+ }
39
+ }
@@ -36,7 +36,9 @@ async function fileLineValidations(appId, envId, fileName, validations, searchRe
36
36
  });
37
37
  });
38
38
  readInterface.on('error', err => {
39
- throw new Error(` Error validating input file: ${err.toString()}`);
39
+ throw new Error(`Error validating input file: ${err.toString()}`, {
40
+ cause: err
41
+ });
40
42
  });
41
43
 
42
44
  // Block until the processing completes
@@ -262,7 +262,7 @@ const checks = {
262
262
  siteHomeUrl: {
263
263
  matcher: "'(siteurl|home)',\\s?'(.*?)'",
264
264
  matchHandler: (lineNumber, results) => ({
265
- text: results[1]
265
+ text: results[1] + ' ' + results[2]
266
266
  }),
267
267
  outputFormatter: infoCheckFormatter,
268
268
  results: [],
package/docs/CHANGELOG.md CHANGED
@@ -1,5 +1,49 @@
1
1
  ## Changelog
2
2
 
3
+ ### 2.37.0
4
+
5
+ - build(deps-dev): bump the babel group with 2 updates
6
+ - build(deps): bump @automattic/vip-search-replace from 1.1.1 to 1.1.2
7
+ - build(deps): bump Automattic/vip-actions from 0.2.0 to 0.3.0
8
+ - build(deps): bump github/codeql-action from 2 to 3
9
+ - BYOR: Add `vip app deploy`
10
+ - build(deps-dev): bump eslint from 8.55.0 to 8.56.0
11
+ - Dev-env: Remove 8.0 & 7.4 from available PHP versions
12
+ - build(deps): bump open from 9.1.0 to 10.0.0
13
+ - fix(dev-env): use the latest nginx image
14
+ - build(deps-dev): bump dockerode from 4.0.0 to 4.0.1
15
+ - build(deps): bump open from 10.0.0 to 10.0.1
16
+ - build(deps-dev): bump dockerode from 4.0.1 to 4.0.2
17
+ - build(deps): bump open from 10.0.1 to 10.0.2
18
+ - fix(validate-sql): Display matched site-urls
19
+ - build(deps): bump socket.io-client from 4.7.2 to 4.7.3
20
+ - build(deps-dev): bump the babel group with 2 updates
21
+ - build(deps): bump open from 10.0.2 to 10.0.3
22
+ - feat: show detailed error information in debug mode
23
+ - build(deps-dev): bump the babel group with 1 update
24
+ - fix: issues found by SonarCloud
25
+ - build(deps-dev): bump @types/node from 18.19.5 to 18.19.6
26
+ - chore(deps): fix CVE-2023-26159 in follow-redirects
27
+ - build(deps): bump Automattic/vip-actions from 0.3.0 to 0.5.0
28
+ - build(deps): bump @automattic/vip-search-replace from 1.1.2 to 1.1.3
29
+ - fix: Properly propagate quotes
30
+ - fix(--format=json): Do not print header if specified format is json
31
+ - feat(dev-env): add quiet mode for "import sql"
32
+ - build(deps): bump socket.io-client from 4.7.3 to 4.7.4
33
+ - fix: regex vulnerable to super-linear runtime
34
+ - ci: add CodeQL workflow
35
+ - ci: remove outdated CodeQL workflow
36
+ - BYOR: Change manual-deploy to custom-deploy
37
+ - build(deps-dev): bump @types/node from 18.19.6 to 18.19.8
38
+ - build(deps-dev): bump nock from 13.4.0 to 13.5.0
39
+ - fix(dev-env): CWE-367 in `prepareLandoEnv()`
40
+ - fix(dev-env): CWE-367 in `getConfigurationFileOptions()`
41
+ - fix(dev-env): CWE-377, CWE-378 originating from `xdgDataDirectory()`
42
+ - build(deps): bump Automattic/vip-actions from 0.5.0 to 0.6.0
43
+
44
+
45
+ **Full Changelog**: https://github.com/Automattic/vip-cli/compare/2.36.3...v2.37.0
46
+
3
47
  ### 2.36.3
4
48
 
5
49
  - build(deps): bump @json2csv/plainjs from 7.0.3 to 7.0.4