@automattic/vip 3.12.1 → 3.13.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.
@@ -6,6 +6,7 @@ exports.destroyEnvironment = destroyEnvironment;
6
6
  exports.doesEnvironmentExist = doesEnvironmentExist;
7
7
  exports.exec = exec;
8
8
  exports.fetchVersionList = fetchVersionList;
9
+ exports.generatePHPStormWorkspace = generatePHPStormWorkspace;
9
10
  exports.generateVSCodeWorkspace = generateVSCodeWorkspace;
10
11
  exports.getAllEnvironmentNames = getAllEnvironmentNames;
11
12
  exports.getApplicationInformation = getApplicationInformation;
@@ -23,7 +24,6 @@ exports.stopEnvironment = stopEnvironment;
23
24
  exports.updateEnvironment = updateEnvironment;
24
25
  exports.writeEnvironmentData = writeEnvironmentData;
25
26
  var _chalk = _interopRequireDefault(require("chalk"));
26
- 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");
@@ -31,6 +31,7 @@ var _graphql = require("graphql");
31
31
  var _utils = require("lando/lib/utils");
32
32
  var _nodeFetch = _interopRequireDefault(require("node-fetch"));
33
33
  var _nodeFs = _interopRequireDefault(require("node:fs"));
34
+ var _promises = require("node:fs/promises");
34
35
  var _nodePath = _interopRequireDefault(require("node:path"));
35
36
  var _semver = _interopRequireDefault(require("semver"));
36
37
  var _uuid = require("uuid");
@@ -69,11 +70,13 @@ function xdgDataDirectory() {
69
70
  async function startEnvironment(lando, slug, options) {
70
71
  debug('Will start an environment', slug);
71
72
  const instancePath = getEnvironmentPath(slug);
72
- debug('Instance path for', slug, 'is:', instancePath);
73
+ debug('Instance path for %s is %s', slug, instancePath);
73
74
  const environmentExists = _nodeFs.default.existsSync(instancePath);
74
75
  if (!environmentExists) {
75
76
  throw new Error(_devEnvironment.DEV_ENVIRONMENT_NOT_FOUND);
76
77
  }
78
+ const envFilePath = _nodePath.default.join(instancePath, '.env');
79
+ _nodeFs.default.appendFileSync(envFilePath, '');
77
80
  let updated = false;
78
81
  if (!options.skipWpVersionsCheck && process.stdin.isTTY) {
79
82
  updated = await maybeUpdateWordPressImage(lando, slug);
@@ -98,7 +101,7 @@ async function stopEnvironment(lando, slug) {
98
101
  }
99
102
  await (0, _devEnvironmentLando.landoStop)(lando, instancePath);
100
103
  }
101
- async function createEnvironment(lando, instanceData, integrationsConfig) {
104
+ async function createEnvironment(lando, instanceData, integrationsConfig, envVars) {
102
105
  const slug = instanceData.siteSlug;
103
106
  integrationsConfig ??= {};
104
107
  debug('Will process an environment', slug, 'with instanceData for creation: ', instanceData);
@@ -110,7 +113,7 @@ async function createEnvironment(lando, instanceData, integrationsConfig) {
110
113
  }
111
114
  const preProcessedInstanceData = preProcessInstanceData(instanceData);
112
115
  debug('Will create an environment', slug, 'with instanceData: ', preProcessedInstanceData);
113
- await prepareLandoEnv(lando, preProcessedInstanceData, instancePath, integrationsConfig);
116
+ await prepareLandoEnv(lando, preProcessedInstanceData, instancePath, integrationsConfig, envVars);
114
117
  }
115
118
  async function updateEnvironment(lando, instanceData) {
116
119
  const slug = instanceData.siteSlug;
@@ -123,7 +126,7 @@ async function updateEnvironment(lando, instanceData) {
123
126
  }
124
127
  const preProcessedInstanceData = preProcessInstanceData(instanceData);
125
128
  debug('Will create an environment', slug, 'with instanceData: ', preProcessedInstanceData);
126
- await prepareLandoEnv(lando, preProcessedInstanceData, instancePath, undefined);
129
+ await prepareLandoEnv(lando, preProcessedInstanceData, instancePath, undefined, undefined);
127
130
  }
128
131
  function preProcessInstanceData(instanceData) {
129
132
  const newInstanceData = {
@@ -366,7 +369,7 @@ async function writeIntegrationsConfig(instancePath, integrationsConfig) {
366
369
  debug(`Integrations configuration file created in ${integrationsConfigTargetPath}`);
367
370
  }
368
371
  }
369
- async function prepareLandoEnv(lando, instanceData, instancePath, integrationsConfig) {
372
+ async function prepareLandoEnv(lando, instanceData, instancePath, integrationsConfig, envVars) {
370
373
  const templateData = {
371
374
  ...instanceData,
372
375
  domain: lando.config.domain
@@ -380,6 +383,7 @@ async function prepareLandoEnv(lando, instanceData, instancePath, integrationsCo
380
383
  const nginxFolderPath = _nodePath.default.join(instancePath, nginxPathString);
381
384
  const nginxFileTargetPath = _nodePath.default.join(nginxFolderPath, nginxFileName);
382
385
  const instanceDataTargetPath = _nodePath.default.join(instancePath, instanceDataFileName);
386
+ const envFilePath = _nodePath.default.join(instancePath, '.env');
383
387
  await _nodeFs.default.promises.mkdir(instancePath, {
384
388
  recursive: true
385
389
  });
@@ -396,6 +400,15 @@ async function prepareLandoEnv(lando, instanceData, instancePath, integrationsCo
396
400
  }
397
401
  }
398
402
  await Promise.all([_nodeFs.default.promises.writeFile(landoFileTargetPath, landoFile), _nodeFs.default.promises.writeFile(nginxFileTargetPath, nginxFile), _nodeFs.default.promises.writeFile(instanceDataTargetPath, instanceDataFile)]);
403
+ if (envVars !== undefined) {
404
+ const env = [];
405
+ Object.entries(envVars ?? {}).forEach(([key]) => {
406
+ env.push(`VIP_ENV_VAR_${key}=`);
407
+ });
408
+ await _nodeFs.default.promises.writeFile(envFilePath, env.join('\n'));
409
+ } else {
410
+ await _nodeFs.default.promises.appendFile(envFilePath, '');
411
+ }
399
412
  debug(`Lando file created in ${landoFileTargetPath}`);
400
413
  debug(`Nginx file created in ${nginxFileTargetPath}`);
401
414
  debug(`Instance data file created in ${instanceDataTargetPath}`);
@@ -444,6 +457,11 @@ async function getApplicationInformation(appId, envType) {
444
457
  type,
445
458
  branch,
446
459
  isMultisite,
460
+ environmentVariables {
461
+ nodes {
462
+ name
463
+ }
464
+ }
447
465
  getIntegrationsDevEnvConfig {
448
466
  data
449
467
  }
@@ -486,6 +504,12 @@ async function getApplicationInformation(appId, envType) {
486
504
  envData = environments.find(candidateEnv => candidateEnv.type === env);
487
505
  }
488
506
  if (envData) {
507
+ const envVars = {};
508
+ envData.environmentVariables?.nodes?.forEach(envvar => {
509
+ if (envvar?.name) {
510
+ envVars[envvar.name] = '';
511
+ }
512
+ });
489
513
  appData.environment = {
490
514
  name: envData.name,
491
515
  branch: envData.branch,
@@ -494,7 +518,8 @@ async function getApplicationInformation(appId, envType) {
494
518
  primaryDomain: envData.primaryDomain?.name ?? '',
495
519
  php: envData.softwareSettings?.php?.current.version ?? '',
496
520
  wordpress: envData.softwareSettings?.wordpress?.current.version ?? '',
497
- integrations: envData.getIntegrationsDevEnvConfig?.data ?? {}
521
+ integrations: envData.getIntegrationsDevEnvConfig?.data ?? {},
522
+ envVars
498
523
  };
499
524
  }
500
525
  }
@@ -536,7 +561,7 @@ async function importMediaPath(slug, filePath) {
536
561
  if (!(await doesEnvironmentExist(environmentPath))) {
537
562
  throw new Error(_devEnvironment.DEV_ENVIRONMENT_NOT_FOUND);
538
563
  }
539
- const files = _nodeFs.default.readdirSync(resolvedPath);
564
+ const files = await (0, _promises.readdir)(resolvedPath);
540
565
  if (files.includes(uploadPathString)) {
541
566
  const confirm = await (0, _enquirer.prompt)({
542
567
  type: 'confirm',
@@ -549,8 +574,15 @@ async function importMediaPath(slug, filePath) {
549
574
  }
550
575
  const uploadsPath = _nodePath.default.join(environmentPath, uploadPathString);
551
576
  console.log(`${_chalk.default.yellow('-')} Started copying files`);
552
- _copyDir.default.sync(resolvedPath, uploadsPath);
553
- console.log(`${_chalk.default.green('✓')} Files successfully copied to ${uploadsPath}.`);
577
+ try {
578
+ await (0, _promises.cp)(resolvedPath, uploadsPath, {
579
+ recursive: true
580
+ });
581
+ console.log(`${_chalk.default.green('✓')} Files successfully copied to ${uploadsPath}.`);
582
+ } catch (error) {
583
+ console.error(`${_chalk.default.red('✗')} Error copying files to ${uploadsPath}.`);
584
+ throw error;
585
+ }
554
586
  }
555
587
 
556
588
  /**
@@ -768,17 +800,27 @@ function generateVSCodeWorkspace(slug) {
768
800
  path: instanceData.appCode.dir
769
801
  });
770
802
  }
803
+
804
+ // Create debug configuration
805
+ const debugConfig = {
806
+ name: `Debug ${slug}`,
807
+ type: 'php',
808
+ request: 'launch',
809
+ port: 9003,
810
+ pathMappings
811
+ };
812
+
813
+ // Check if running under WSL and add hostname if needed
814
+ // This is to allow xdebug to work when running under WSL
815
+ if (process.env?.WSL_DISTRO_NAME) {
816
+ debug('WSL detected, adding hostname to debug configuration');
817
+ debugConfig.hostname = '0.0.0.0';
818
+ }
771
819
  const workspace = {
772
820
  folders,
773
821
  launch: {
774
822
  version: '0.2.0',
775
- configurations: [{
776
- name: `Debug ${slug}`,
777
- type: 'php',
778
- request: 'launch',
779
- port: 9003,
780
- pathMappings
781
- }]
823
+ configurations: [debugConfig]
782
824
  }
783
825
  };
784
826
  _nodeFs.default.writeFileSync(workspacePath, JSON.stringify(workspace, null, 2));
@@ -796,7 +838,7 @@ const generatePathMappings = (location, instanceData) => {
796
838
  pathMappings['/wp/wp-content/plugins'] = _nodePath.default.resolve(instanceData.appCode.dir, 'plugins');
797
839
  pathMappings['/wp/wp-content/private'] = _nodePath.default.resolve(instanceData.appCode.dir, 'private');
798
840
  pathMappings['/wp/wp-content/themes'] = _nodePath.default.resolve(instanceData.appCode.dir, 'themes');
799
- pathMappings['/wp/wp-content/vip-config'] = _nodePath.default.resolve(instanceData.appCode.dir, 'vip-config');
841
+ pathMappings['/wp/vip-config'] = _nodePath.default.resolve(instanceData.appCode.dir, 'vip-config');
800
842
  }
801
843
  pathMappings['/wp'] = _nodePath.default.resolve(location, 'wordpress');
802
844
  return pathMappings;
@@ -805,4 +847,88 @@ function getVSCodeWorkspacePath(slug) {
805
847
  const location = getEnvironmentPath(slug);
806
848
  const workspacePath = _nodePath.default.join(location, `${slug}.code-workspace`);
807
849
  return workspacePath;
850
+ }
851
+
852
+ /**
853
+ * Generates PHPStorm project configuration including debug settings
854
+ *
855
+ * @param {string} slug - The slug of the environment to generate PHPStorm config for
856
+ * @return {string} Project directory path
857
+ */
858
+ function generatePHPStormWorkspace(slug) {
859
+ debug('Generating PHPStorm Workspace');
860
+ const location = getEnvironmentPath(slug);
861
+ // const location = location;
862
+ const instanceData = readEnvironmentData(slug);
863
+ const pathMappings = generatePathMappings(location, instanceData);
864
+
865
+ // Create .idea directory
866
+ _nodeFs.default.mkdirSync(_nodePath.default.join(location, '.idea', 'runConfigurations'), {
867
+ recursive: true
868
+ });
869
+
870
+ // Generate workspace.xml
871
+ const workspaceXml = `<?xml version="1.0" encoding="UTF-8"?>
872
+ <project version="4">
873
+ <component name="PhpWorkspaceProjectConfiguration">
874
+ <include_path>
875
+ <path value="$PROJECT_DIR$/wordpress" />
876
+ ${instanceData?.muPlugins?.dir ? `<path value="${instanceData.muPlugins.dir}" />` : ''}
877
+ ${instanceData?.appCode?.dir ? `<path value="${instanceData.appCode.dir}" />` : ''}
878
+ </include_path>
879
+ </component>
880
+ <component name="PhpDebugGeneral" listening_started="true" />
881
+ <component name="PhpDebugXdebugSettings">
882
+ <debug_server_list>
883
+ <server host="localhost" port="9003" />
884
+ </debug_server_list>
885
+ <path_mappings>
886
+ ${Object.entries(pathMappings).map(([serverPath, localPath]) => ` <mapping local-root="${serverPath}" remote-root="$PROJECT_DIR$/${_nodePath.default.relative(location, localPath)}" />`).join('\n')}
887
+ </path_mappings>
888
+ </component>
889
+ <component name="PhpServers">
890
+ <servers>
891
+ <server host="localhost" id="${(0, _uuid.v4)()}" name="localhost" use_path_mappings="true">
892
+ <path_mappings>
893
+ <mapping local-root="$PROJECT_DIR$/wordpress" remote-root="/wp" />
894
+ ${Object.entries(pathMappings).map(([serverPath, localPath]) => `<mapping local-root="${localPath}" remote-root="${serverPath}" />`).join('\n')}
895
+ </path_mappings>
896
+ </server>
897
+ </servers>
898
+ </component>
899
+ <component name="WordPressConfiguration" enabled="true">
900
+ <wordpressPath>$PROJECT_DIR$/wordpress</wordpressPath>
901
+ </component>
902
+ </project>`;
903
+ _nodeFs.default.writeFileSync(_nodePath.default.join(location, '.idea', 'workspace.xml'), workspaceXml);
904
+ const xdebugXml = `<component name="ProjectRunConfigurationManager">
905
+ <configuration default="false" name="VIP Debug" type="PhpRemoteDebugRunConfigurationType" factoryName="PHP Remote Debug" nameIsGenerated="true" filter_connections="NOT_FILTER" server_name="localhost" session_id="XDEBUG">
906
+ <method v="2" />
907
+ </configuration>
908
+ </component>
909
+ `;
910
+ _nodeFs.default.writeFileSync(_nodePath.default.join(location, '.idea', 'runConfigurations', 'vip-xdebug.xml'), xdebugXml);
911
+ const projectXml = `<?xml version="1.0" encoding="UTF-8"?>
912
+ <project version="4">
913
+ <component name="ProjectModuleManager">
914
+ <modules>
915
+ <module fileurl="file://$PROJECT_DIR$/.idea/${slug}.iml" filepath="$PROJECT_DIR$/.idea/${slug}.iml" />
916
+ </modules>
917
+ </component>
918
+ </project>
919
+ `;
920
+ _nodeFs.default.writeFileSync(_nodePath.default.join(location, '.idea', 'modules.xml'), projectXml);
921
+ const modulesXml = `<?xml version="1.0" encoding="UTF-8"?>
922
+ <module type="WEB_MODULE" version="4">
923
+ <component name="NewModuleRootManager">
924
+ <content url="file://$MODULE_DIR$/${_nodePath.default.relative(location, instanceData?.appCode?.dir ?? '')}" />
925
+ <content url="file://$MODULE_DIR$/${_nodePath.default.relative(location, instanceData?.muPlugins?.dir ?? '')}" />
926
+ <content url="file://$MODULE_DIR$" />
927
+ <orderEntry type="sourceFolder" forTests="false" />
928
+ <orderEntry type="inheritedJdk" />
929
+ </component>
930
+ </module>
931
+ `;
932
+ _nodeFs.default.writeFileSync(_nodePath.default.join(location, '.idea', slug + '.iml'), modulesXml);
933
+ return location;
808
934
  }
@@ -44,9 +44,7 @@ const debug = (0, _debug.default)(DEBUG_KEY);
44
44
  async function getLandoConfig() {
45
45
  // The path will be smth like `yarn/global/node_modules/lando/lib/lando.js`; we need the path up to `lando` (inclusive)
46
46
  const landoPath = (0, _nodePath.dirname)((0, _nodePath.dirname)(require.resolve('lando')));
47
- // The path will be smth like `yarn/global/node_modules/@lando/compose/index.js`; we need the path up to `@lando` (inclusive)
48
- const atLandoPath = (0, _nodePath.dirname)((0, _nodePath.dirname)(require.resolve('@lando/compose')));
49
- debug(`Getting Lando config, using paths '${landoPath}' and '${atLandoPath}' for plugins`);
47
+ debug(`Getting Lando config, using paths '${landoPath}' for plugins`);
50
48
  const isLandoDebugSelected = _debug.default.enabled(DEBUG_KEY);
51
49
  const isAllDebugSelected = _debug.default.enabled('"*"');
52
50
  let logLevelConsole;
@@ -75,11 +73,7 @@ async function getLandoConfig() {
75
73
  landoFile: '.lando.yml',
76
74
  preLandoFiles: ['.lando.base.yml', '.lando.dist.yml', '.lando.upstream.yml'],
77
75
  postLandoFiles: ['.lando.local.yml'],
78
- pluginDirs: [landoPath, {
79
- path: atLandoPath,
80
- subdir: '.',
81
- namespace: '@lando'
82
- }],
76
+ pluginDirs: [landoPath],
83
77
  disablePlugins: [],
84
78
  proxyName: 'vip-dev-env-proxy',
85
79
  userConfRoot: landoDir,
@@ -2,7 +2,7 @@
2
2
 
3
3
  exports.__esModule = true;
4
4
  exports.MediaImportProgressTracker = void 0;
5
- var _singleLineLog = require("single-line-log");
5
+ var _singleLineLog = require("@wwa/single-line-log");
6
6
  var _format = require("../../lib/cli/format");
7
7
  var _status = require("../../lib/media-import/status");
8
8
  const PRINT_INTERVAL = process.env.DEBUG ? 5000 : 200; // How often the report is printed. Mainly affects the "spinner" animation.
@@ -6,8 +6,8 @@ exports.currentUserCanImportForApp = currentUserCanImportForApp;
6
6
  exports.isSupportedApp = exports.isImportingBlockedBySync = void 0;
7
7
  var _fileSize = require("../../lib/constants/file-size");
8
8
  var _vipgo = require("../../lib/constants/vipgo");
9
- const SQL_IMPORT_FILE_SIZE_LIMIT = exports.SQL_IMPORT_FILE_SIZE_LIMIT = 100 * _fileSize.GB_IN_BYTES;
10
- const SQL_IMPORT_FILE_SIZE_LIMIT_LAUNCHED = exports.SQL_IMPORT_FILE_SIZE_LIMIT_LAUNCHED = 5 * _fileSize.GB_IN_BYTES;
9
+ const SQL_IMPORT_FILE_SIZE_LIMIT = exports.SQL_IMPORT_FILE_SIZE_LIMIT = 200 * _fileSize.GB_IN_BYTES;
10
+ const SQL_IMPORT_FILE_SIZE_LIMIT_LAUNCHED = exports.SQL_IMPORT_FILE_SIZE_LIMIT_LAUNCHED = 10 * _fileSize.GB_IN_BYTES;
11
11
  function currentUserCanImportForApp(app) {
12
12
  // TODO: implement
13
13
  return Boolean(app);
@@ -4,9 +4,9 @@ exports.__esModule = true;
4
4
  exports.validate = exports.staticSqlValidations = exports.getTableNames = void 0;
5
5
  exports.validateFilename = validateFilename;
6
6
  exports.validateImportFileExtension = void 0;
7
+ var _singleLineLog = require("@wwa/single-line-log");
7
8
  var _chalk = _interopRequireDefault(require("chalk"));
8
- var _path = _interopRequireDefault(require("path"));
9
- var _singleLineLog = require("single-line-log");
9
+ var _nodePath = _interopRequireDefault(require("node:path"));
10
10
  var exit = _interopRequireWildcard(require("../../lib/cli/exit"));
11
11
  var _clientFileUploader = require("../../lib/client-file-uploader");
12
12
  var _tracker = require("../../lib/tracker");
@@ -33,7 +33,7 @@ function formatRecommendation(message) {
33
33
  * @returns {boolean} True if the extension is valid
34
34
  */
35
35
  const validateImportFileExtension = filename => {
36
- const ext = _path.default.extname(filename).toLowerCase();
36
+ const ext = _nodePath.default.extname(filename).toLowerCase();
37
37
  if (!['.sql', '.gz'].includes(ext)) {
38
38
  exit.withError('Invalid file extension. Please provide a .sql or .gz file.');
39
39
  }
package/docs/CHANGELOG.md CHANGED
@@ -1,5 +1,56 @@
1
1
  ## Changelog
2
2
 
3
+ ### 3.13.0
4
+
5
+ * build(deps): bump Automattic/vip-actions from dbd477b39f2122bf594da2d65dc0ea8ab5fc9bf0 to bb2853ecb6cd7dba2efe589ebb10cf930759fe69
6
+ * feat(dev-env): add initial support for `.env` files
7
+ * feat(dev-env): Refactor IDE support: add Cursor, PHPStorm and Windsurf
8
+ * build(deps-dev): bump @automattic/eslint-plugin-wpvip from 0.13.0 to 0.13.1
9
+ * build(deps): bump shelljs from 0.9.1 to 0.9.2
10
+ * fix(dev-env): disallow running dev-env as `root`
11
+ * chore(deps): update `lando` to `540a5e0`
12
+ * fix(dev-env): handling of large media files in `vip dev-env import media`
13
+ * build(deps-dev): bump @types/node from 22.13.10 to 22.13.11
14
+ * build(deps-dev): bump the babel group with 2 updates
15
+ * build(deps): bump Automattic/vip-actions from bb2853ecb6cd7dba2efe589ebb10cf930759fe69 to 1137b91acf0f5ea4e0db044bcf14ceabed9b068f
16
+ * build(deps-dev): bump @types/semver from 7.5.8 to 7.7.0
17
+ * build(deps-dev): bump @types/node from 22.13.11 to 22.13.14
18
+ * fix(dev-env): `mu-plugins` prompt logic
19
+ * build(deps-dev): bump dockerode from 4.0.4 to 4.0.5
20
+ * fix(dev-env): handling of `mediaRedirectDomain` in `vip dev-env update`
21
+ * chore: update GraphQL types
22
+ * chore: remove overrides for `@lando/compose`
23
+ * build(deps): bump step-security/harden-runner from 2.11.0 to 2.11.1
24
+ * build(deps-dev): bump @types/dockerode from 3.3.35 to 3.3.37
25
+ * build(deps): bump actions/dependency-review-action from 4.5.0 to 4.6.0
26
+ * build(deps-dev): bump @types/node from 22.13.14 to 22.14.0
27
+ * build(deps-dev): bump typescript from 5.8.2 to 5.8.3
28
+ * build(deps-dev): bump @types/node from 22.14.0 to 22.14.1
29
+ * chore(deps): update `lando`
30
+ * chore(deps): replace `single-line-log` with a fork
31
+ * Clarifying descriptions of options for `vip config envvar`
32
+ * build(deps-dev): bump @types/dockerode from 3.3.37 to 3.3.38
33
+ * build(deps): bump open from 10.1.0 to 10.1.1
34
+ * build(deps): bump @automattic/vip-go-preflight-checks from 2.0.18 to 2.0.19
35
+ * fix(dev-env): strip trailing slash in URLs when doing sync
36
+
37
+ **Full Changelog**: https://github.com/Automattic/vip-cli/compare/3.12.2...3.13.0
38
+
39
+ ### 3.12.2
40
+
41
+ * build(deps-dev): bump @babel/core from 7.26.9 to 7.26.10
42
+ * Increase SQL import file size limit to 200G
43
+
44
+ **Full Changelog**: https://github.com/Automattic/vip-cli/compare/3.12.1...3.12.2
45
+
46
+ ### 3.12.1
47
+
48
+ * build(deps): bump shelljs from 0.8.5 to 0.9.1
49
+ * build(deps-dev): bump @types/node from 22.13.9 to 22.13.10
50
+ * Only use myloader source-db when provided
51
+
52
+ **Full Changelog**: https://github.com/Automattic/vip-cli/compare/3.12.0...3.12.1
53
+
3
54
  ### 3.12.0
4
55
 
5
56
  * fix: rsync to skip files based on checksum