@automattic/vip 3.11.0 → 3.12.1

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.
@@ -184,7 +184,7 @@ services:
184
184
  - devtools:/dev-tools
185
185
  - scripts:/scripts
186
186
  initOnly: true
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/'
187
+ entrypoint: /bin/sh -c '/usr/bin/rsync -ac --delete --chown=${LANDO_HOST_USER_ID}:${LANDO_HOST_GROUP_ID} /wp/ /shared/; /usr/bin/rsync -ac --chown=${LANDO_HOST_USER_ID}:${LANDO_HOST_GROUP_ID} --delete /dev-tools-orig/ /dev-tools/'
188
188
  volumes:
189
189
  devtools:
190
190
  scripts:
@@ -194,7 +194,7 @@ services:
194
194
  type: compose
195
195
  services:
196
196
  image: ghcr.io/automattic/vip-container-images/mu-plugins:0.1
197
- command: sh /run.sh
197
+ command: /bin/sh /run.sh
198
198
  volumes:
199
199
  - mu-plugins:/shared
200
200
  - type: volume
@@ -67,7 +67,8 @@ cmd.argv(process.argv, async (arg, opt) => {
67
67
  photon: currentInstanceData.photon,
68
68
  mediaRedirectDomain: currentInstanceData.mediaRedirectDomain,
69
69
  multisite: false,
70
- title: ''
70
+ title: '',
71
+ cron: currentInstanceData.cron
71
72
  };
72
73
  const providedOptions = Object.keys(opt).filter(option => option.length > 1) // Filter out single letter aliases
73
74
  .filter(option => !['debug', 'help', 'slug'].includes(option)); // Filter out options that are not related to instance configuration
@@ -17,6 +17,11 @@ const examples = [
17
17
  usage: 'vip search-replace file.sql --search-replace="from,to"',
18
18
  description: 'Search for every instance of the value "from" in the local input file named "file.sql" and replace it with the value "to".\n' + ' * Results of the operation output to STDOUT by default.'
19
19
  },
20
+ // multiple operations
21
+ {
22
+ usage: 'vip search-replace file.sql --search-replace="from,to" --search-replace="before,after"',
23
+ description: 'Perform a search and replace operation for more than one pair of values in the local input file named "file.sql".'
24
+ },
20
25
  // `in-place` flag
21
26
  {
22
27
  usage: 'vip search-replace file.sql --search-replace="from,to" --in-place',
@@ -110,7 +110,10 @@ class DevEnvImportSQLCommand {
110
110
  let importArg = ['db', '--disable-auto-rehash'].concat(this.options.quiet ? '--silent' : []);
111
111
  const threadCount = Math.max(_os.default.cpus().length - 2, 1);
112
112
  if (dumpDetails.type === _database.SqlDumpType.MYDUMPER) {
113
- importArg = ['db-myloader', '--overwrite-tables', `--source-db=${dumpDetails.sourceDb}`, `--threads=${threadCount}`, '--max-threads-for-schema-creation=10', '--max-threads-for-index-creation=10', '--skip-triggers', '--skip-post', '--innodb-optimize-keys', '--checksum=SKIP', '--metadata-refresh-interval=2000000', '--stream'].concat(this.options.quiet ? ['--verbose=0'] : ['--verbose=3']);
113
+ if (!dumpDetails.sourceDb) {
114
+ console.log(`${_chalk.default.yellow('!')} Unable to identify source DB. Skipping myloader source-db option`);
115
+ }
116
+ importArg = ['db-myloader', '--overwrite-tables', ...(dumpDetails.sourceDb ? [`--source-db=${dumpDetails.sourceDb}`] : []), `--threads=${threadCount}`, '--max-threads-for-schema-creation=10', '--max-threads-for-index-creation=10', '--skip-triggers', '--skip-post', '--innodb-optimize-keys', '--checksum=SKIP', '--metadata-refresh-interval=2000000', '--stream'].concat(this.options.quiet ? ['--verbose=0'] : ['--verbose=3']);
114
117
  }
115
118
  return importArg;
116
119
  }
@@ -39,8 +39,14 @@ process.on('unhandledRejection', uncaughtError);
39
39
  let _opts = {};
40
40
  let alreadyConfirmedDebugAttachment = false;
41
41
 
42
+ /**
43
+ * @param {string[]} argv
44
+ */
42
45
  // eslint-disable-next-line complexity
43
46
  _args.default.argv = async function (argv, cb) {
47
+ if (process.platform !== 'win32' && argv[1]?.endsWith('.js')) {
48
+ argv[1] = argv[1].slice(0, -3);
49
+ }
44
50
  if (process.execArgv.includes('--inspect') && !alreadyConfirmedDebugAttachment) {
45
51
  await (0, _enquirer.prompt)({
46
52
  type: 'confirm',
@@ -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.2.2';
39
+ const DEV_ENVIRONMENT_VERSION = exports.DEV_ENVIRONMENT_VERSION = '2.2.3';
@@ -225,7 +225,8 @@ async function promptForArguments(preselectedOptions, defaultOptions, suppressPr
225
225
  siteSlug: '',
226
226
  mailpit: false,
227
227
  photon: false,
228
- cron: false
228
+ cron: false,
229
+ overrides: preselectedOptions.overrides
229
230
  };
230
231
  const promptLabels = {
231
232
  xdebug: 'XDebug',
@@ -1,11 +1,12 @@
1
1
  "use strict";
2
2
 
3
3
  exports.__esModule = true;
4
- exports.CONFIGURATION_FILE_NAME = void 0;
4
+ exports.CONFIGURATION_TEMPLATE_FILE_NAME = exports.CONFIGURATION_FILE_NAME = void 0;
5
5
  exports.getConfigurationFileOptions = getConfigurationFileOptions;
6
6
  exports.mergeConfigurationFileOptions = mergeConfigurationFileOptions;
7
7
  var _chalk = _interopRequireDefault(require("chalk"));
8
8
  var _debug = _interopRequireDefault(require("debug"));
9
+ var _ejs = _interopRequireDefault(require("ejs"));
9
10
  var _jsYaml = _interopRequireWildcard(require("js-yaml"));
10
11
  var _promises = require("node:fs/promises");
11
12
  var _nodePath = _interopRequireDefault(require("node:path"));
@@ -16,6 +17,7 @@ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e;
16
17
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
17
18
  const debug = (0, _debug.default)('@automattic/vip:bin:dev-environment');
18
19
  const CONFIGURATION_FILE_NAME = exports.CONFIGURATION_FILE_NAME = 'vip-dev-env.yml';
20
+ const CONFIGURATION_TEMPLATE_FILE_NAME = exports.CONFIGURATION_TEMPLATE_FILE_NAME = 'vip-dev-env.yml.ejs';
19
21
  async function getConfigurationFileOptions() {
20
22
  const configurationFile = await findConfigurationFile();
21
23
  if (configurationFile === false) {
@@ -85,6 +87,7 @@ function sanitizeConfiguration(configuration, configurationFilePath) {
85
87
  'media-redirect-domain': configuration['media-redirect-domain']?.toString(),
86
88
  photon: stringToBooleanIfDefined(configuration.photon),
87
89
  cron: stringToBooleanIfDefined(configuration.cron),
90
+ overrides: configuration.overrides?.toString(),
88
91
  meta: configurationMeta
89
92
  };
90
93
 
@@ -96,11 +99,12 @@ function sanitizeConfiguration(configuration, configurationFilePath) {
96
99
  }
97
100
  function adjustRelativePaths(configuration, configurationFilePath) {
98
101
  const configurationDirectory = _nodePath.default.resolve(_nodePath.default.dirname(configurationFilePath));
99
- if (configuration['app-code'] && configuration['app-code'] !== 'image') {
100
- configuration['app-code'] = _nodePath.default.join(configurationDirectory, configuration['app-code']);
101
- }
102
- if (configuration['mu-plugins'] && configuration['mu-plugins'] !== 'image') {
103
- configuration['mu-plugins'] = _nodePath.default.join(configurationDirectory, configuration['mu-plugins']);
102
+ const imageOption = ['demo', 'image'];
103
+ for (const option of ['app-code', 'mu-plugins']) {
104
+ const value = configuration[option];
105
+ if (value && !imageOption.includes(value)) {
106
+ configuration[option] = _nodePath.default.isAbsolute(value) ? value : _nodePath.default.join(configurationDirectory, value);
107
+ }
104
108
  }
105
109
  return configuration;
106
110
  }
@@ -123,7 +127,8 @@ function mergeConfigurationFileOptions(preselectedOptions, configurationFileOpti
123
127
  mailpit: configurationFileOptions.mailpit,
124
128
  mediaRedirectDomain: configurationFileOptions['media-redirect-domain'],
125
129
  photon: configurationFileOptions.photon,
126
- cron: configurationFileOptions.cron
130
+ cron: configurationFileOptions.cron,
131
+ overrides: configurationFileOptions.overrides
127
132
  };
128
133
  const mergedOptions = {};
129
134
  Object.keys(configurationFileInstanceOptions).forEach(key => {
@@ -141,21 +146,59 @@ async function findConfigurationFile() {
141
146
  const rootPath = _nodePath.default.parse(currentPath).root;
142
147
  let depth = 0;
143
148
  const maxDepth = 64;
144
- const pathPromises = [];
149
+ const locations = [];
145
150
  while (currentPath !== rootPath && depth < maxDepth) {
146
- const configurationPath = _nodePath.default.join(currentPath, _devEnvironmentCli.CONFIGURATION_FOLDER, CONFIGURATION_FILE_NAME);
147
- pathPromises.push((0, _promises.readFile)(configurationPath, 'utf8').then(configurationContents => ({
148
- configurationPath,
149
- configurationContents
150
- })));
151
+ locations.push({
152
+ dir: _nodePath.default.join(currentPath, _devEnvironmentCli.CONFIGURATION_FOLDER),
153
+ file: _nodePath.default.join(currentPath, _devEnvironmentCli.CONFIGURATION_FOLDER, CONFIGURATION_TEMPLATE_FILE_NAME),
154
+ template: true
155
+ }, {
156
+ dir: _nodePath.default.join(currentPath, _devEnvironmentCli.CONFIGURATION_FOLDER),
157
+ file: _nodePath.default.join(currentPath, _devEnvironmentCli.CONFIGURATION_FOLDER, CONFIGURATION_FILE_NAME),
158
+ template: false
159
+ }, {
160
+ dir: currentPath,
161
+ file: _nodePath.default.join(currentPath, '.' + CONFIGURATION_TEMPLATE_FILE_NAME),
162
+ template: true
163
+ }, {
164
+ dir: currentPath,
165
+ file: _nodePath.default.join(currentPath, '.' + CONFIGURATION_FILE_NAME),
166
+ template: false
167
+ });
151
168
 
152
169
  // Move up one directory
153
170
  currentPath = _nodePath.default.dirname(currentPath);
154
171
 
155
172
  // Use depth as a sanity check to avoid an infitite loop
156
- depth++;
173
+ ++depth;
174
+ }
175
+ for (const {
176
+ dir,
177
+ file,
178
+ template
179
+ } of locations) {
180
+ try {
181
+ // eslint-disable-next-line no-await-in-loop
182
+ const contents = await (0, _promises.readFile)(file, 'utf8');
183
+ if (template) {
184
+ const rendered = _ejs.default.render(contents, {
185
+ configDir: dir
186
+ });
187
+ return {
188
+ configurationPath: file,
189
+ configurationContents: rendered
190
+ };
191
+ }
192
+ return {
193
+ configurationPath: file,
194
+ configurationContents: contents
195
+ };
196
+ } catch (error) {
197
+ const err = error instanceof Error ? error : new Error('Unknown error');
198
+ debug(`Error reading or rendering file ${file}: ${err.message}`);
199
+ }
157
200
  }
158
- return Promise.any(pathPromises).catch(() => false);
201
+ return false;
159
202
  }
160
203
  const CONFIGURATION_FILE_VERSIONS = ['1'];
161
204
  function getAllConfigurationFileVersions() {
@@ -48,6 +48,7 @@ const debug = (0, _debug.default)('@automattic/vip:bin:dev-environment');
48
48
  const landoFileTemplatePath = _nodePath.default.join(__dirname, '..', '..', '..', 'assets', 'dev-env.lando.template.yml.ejs');
49
49
  const nginxFileTemplatePath = _nodePath.default.join(__dirname, '..', '..', '..', 'assets', 'dev-env.nginx.template.conf.ejs');
50
50
  const landoFileName = '.lando.yml';
51
+ const landoOverridesFileName = '.lando.local.yml';
51
52
  const landoBackupFileName = '.lando.backup.yml';
52
53
  const nginxFileName = 'extra.conf';
53
54
  const instanceDataFileName = 'instance_data.json';
@@ -74,7 +75,7 @@ async function startEnvironment(lando, slug, options) {
74
75
  throw new Error(_devEnvironment.DEV_ENVIRONMENT_NOT_FOUND);
75
76
  }
76
77
  let updated = false;
77
- if (!options.skipWpVersionsCheck) {
78
+ if (!options.skipWpVersionsCheck && process.stdin.isTTY) {
78
79
  updated = await maybeUpdateWordPressImage(lando, slug);
79
80
  }
80
81
  updated = updated || (await maybeUpdateVersion(lando, slug));
@@ -169,6 +170,7 @@ function preProcessInstanceData(instanceData) {
169
170
  // newInstanceData
170
171
  newInstanceData.autologinKey = (0, _uuid.v4)();
171
172
  newInstanceData.version = _devEnvironment.DEV_ENVIRONMENT_VERSION;
173
+ newInstanceData.overrides = instanceData.overrides ?? '';
172
174
  return newInstanceData;
173
175
  }
174
176
  async function destroyEnvironment(lando, slug, removeFiles) {
@@ -373,6 +375,7 @@ async function prepareLandoEnv(lando, instanceData, instancePath, integrationsCo
373
375
  const nginxFile = await _ejs.default.renderFile(nginxFileTemplatePath, templateData);
374
376
  const instanceDataFile = JSON.stringify(instanceData);
375
377
  const landoFileTargetPath = _nodePath.default.join(instancePath, landoFileName);
378
+ const landoOverridesFileTargetPath = _nodePath.default.join(instancePath, landoOverridesFileName);
376
379
  const landoBackupFileTargetPath = _nodePath.default.join(instancePath, landoBackupFileName);
377
380
  const nginxFolderPath = _nodePath.default.join(instancePath, nginxPathString);
378
381
  const nginxFileTargetPath = _nodePath.default.join(nginxFolderPath, nginxFileName);
@@ -397,6 +400,13 @@ async function prepareLandoEnv(lando, instanceData, instancePath, integrationsCo
397
400
  debug(`Nginx file created in ${nginxFileTargetPath}`);
398
401
  debug(`Instance data file created in ${instanceDataTargetPath}`);
399
402
  await writeIntegrationsConfig(instancePath, integrationsConfig);
403
+ if (instanceData.overrides) {
404
+ await _nodeFs.default.promises.writeFile(landoOverridesFileTargetPath, instanceData.overrides);
405
+ } else {
406
+ await _nodeFs.default.promises.rm(landoOverridesFileTargetPath, {
407
+ force: true
408
+ });
409
+ }
400
410
  }
401
411
  function getAllEnvironmentNames() {
402
412
  const mainEnvironmentPath = xdgDataDirectory();
package/docs/CHANGELOG.md CHANGED
@@ -1,5 +1,36 @@
1
1
  ## Changelog
2
2
 
3
+ ### 3.12.0
4
+
5
+ * fix: rsync to skip files based on checksum
6
+ * fix(dev-env): do not show the update prompt when stdin is not a TTY
7
+ * fix(dev-env): handling of absolute paths in configuration files
8
+ * feat(dev-env): overrides for the default configuration
9
+ * build(deps): bump step-security/harden-runner from 2.10.4 to 2.11.0
10
+ * build(deps-dev): bump @types/dockerode from 3.3.34 to 3.3.35
11
+ * feat(dev-env): support templates for dev env configs
12
+ * build(deps-dev): bump @types/node from 22.10.1 to 22.13.9
13
+ * build(deps-dev): bump typescript from 5.7.3 to 5.8.2
14
+ * build(deps): bump uuid from 11.0.3 to 11.1.0
15
+ * fix(dev-env): accept `demo` as an alias of `image`
16
+ * fix(dev-env): cron handling by `vip dev-env update`
17
+ * ci: CodeQL scan for GHA
18
+ * Update/search replace example
19
+ * fix: forcefully strip `.js` from the command
20
+ * build(deps): bump Automattic/vip-actions from e14930c8c34a8ff57b1a99659ba517420027057b to dbd477b39f2122bf594da2d65dc0ea8ab5fc9bf0
21
+ * build(deps-dev): bump the babel group with 2 updates
22
+ * build(deps): bump semver from 7.6.3 to 7.7.1
23
+ * build(deps): bump debug from 4.3.7 to 4.4.0
24
+
25
+ **Full Changelog**: https://github.com/Automattic/vip-cli/compare/3.11.0...3.12.0
26
+
27
+ ### 3.11.0
28
+
29
+ * Add option to skip maintenance mode during SQL import
30
+ * Fix dev-env-sync for mydumper backups
31
+
32
+ **Full Changelog**: https://github.com/Automattic/vip-cli/compare/3.10.0...3.11.0
33
+
3
34
  ### 3.10.0
4
35
 
5
36
  * VIP-CLI interactive console: Trim leading and ending spaces in command passed in