@automattic/vip 3.19.0 → 3.19.2

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.
@@ -43,9 +43,13 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
43
43
  await (0, _tracker.trackEvent)('app_command_success');
44
44
 
45
45
  // Clone the read-only response object so we can modify it
46
- const clonedResponse = Object.assign({}, res);
46
+ const clonedResponse = {
47
+ ...res
48
+ };
47
49
  clonedResponse.environments = clonedResponse.environments.map(env => {
48
- const clonedEnv = Object.assign({}, env);
50
+ const clonedEnv = {
51
+ ...env
52
+ };
49
53
  clonedEnv.name = (0, _command.getEnvIdentifier)(env);
50
54
 
51
55
  // Use the short version of git commit hash
@@ -13,17 +13,14 @@ const exampleUsage = 'vip dev-env info';
13
13
  const usage = 'vip dev-env info';
14
14
  const examples = [{
15
15
  usage: `${exampleUsage} --slug=example-site`,
16
- description: 'Retrieve basic information about the local environment named "example-site".'
17
- }, {
18
- usage: `${exampleUsage} --slug=example-site --extended`,
19
- description: 'Retrieve a larger amount of information about the local environment named "example-site".'
16
+ description: 'Retrieve information about the local environment named "example-site".'
20
17
  }, {
21
18
  usage: `${exampleUsage} --all`,
22
- description: 'Retrieve basic information about all local environments.'
19
+ description: 'Retrieve information about all local environments.'
23
20
  }];
24
21
  (0, _command.default)({
25
22
  usage
26
- }).option('slug', 'A unique name for a local environment. Default is "vip-local".', undefined, _devEnvironmentCli.processSlug).option('all', 'Retrieve information about all local environments.').option('extended', 'Retrieve a larger amount of information.').examples(examples).argv(process.argv, async (arg, opt) => {
23
+ }).option('slug', 'A unique name for a local environment. Default is "vip-local".', undefined, _devEnvironmentCli.processSlug).option('all', 'Retrieve information about all local environments.').option('extended', 'Deprecated, not used.').examples(examples).argv(process.argv, async (arg, opt) => {
27
24
  let trackingInfo;
28
25
  let slug;
29
26
  const lando = await (0, _devEnvironmentLando.bootstrapLando)();
@@ -41,7 +38,7 @@ const examples = [{
41
38
  debug('Args: ', arg, 'Options: ', opt);
42
39
  try {
43
40
  const options = {
44
- extended: Boolean(opt.extended),
41
+ extended: true,
45
42
  suppressWarnings: true
46
43
  };
47
44
  if (opt.all) {
@@ -4,7 +4,6 @@
4
4
  var _devEnvSyncSql = require("../commands/dev-env-sync-sql");
5
5
  var _command = _interopRequireDefault(require("../lib/cli/command"));
6
6
  var _devEnvironmentCli = require("../lib/dev-environment/dev-environment-cli");
7
- var _devEnvironmentCore = require("../lib/dev-environment/dev-environment-core");
8
7
  var _devEnvironmentLando = require("../lib/dev-environment/dev-environment-lando");
9
8
  var _tracker = require("../lib/tracker");
10
9
  var _userError = _interopRequireDefault(require("../lib/user-error"));
@@ -58,8 +57,8 @@ const appQuery = `
58
57
  });
59
58
  await trackerFn('execute');
60
59
  const lando = await (0, _devEnvironmentLando.bootstrapLando)();
61
- const envPath = (0, _devEnvironmentCore.getEnvironmentPath)(slug);
62
- if (!(await (0, _devEnvironmentLando.isEnvUp)(lando, envPath)) && !opt.force) {
60
+ const isUp = (await Promise.all([(0, _devEnvironmentLando.isContainerRunning)(lando, slug, 'php'), (0, _devEnvironmentLando.isContainerRunning)(lando, slug, 'database')])).every(Boolean);
61
+ if (!isUp && !opt.force) {
63
62
  await trackerFn('env_not_running_error', {
64
63
  errorMessage: 'Environment was not running'
65
64
  });
@@ -103,8 +103,8 @@ function isValidMd5(md5) {
103
103
  }
104
104
 
105
105
  /**
106
- * @param {AppForImport} app
107
- * @param {EnvForImport} env
106
+ * @param {import('../lib/site-import/db-file-import').AppForImport} app
107
+ * @param {import('../lib/site-import/db-file-import').EnvForImport} env
108
108
  * @param {string} fileNameOrURL
109
109
  * @param {boolean} isUrl
110
110
  * @param {string|null} md5
@@ -367,7 +367,7 @@ const displayPlaybook = ({
367
367
  }
368
368
  }
369
369
  };
370
- void (0, _command.default)({
370
+ (0, _command.default)({
371
371
  appContext: true,
372
372
  appQuery,
373
373
  envContext: true,
@@ -8,7 +8,6 @@
8
8
  exports.__esModule = true;
9
9
  exports.vipImportValidateFilesCmd = vipImportValidateFilesCmd;
10
10
  var _chalk = _interopRequireDefault(require("chalk"));
11
- var _url = _interopRequireDefault(require("url"));
12
11
  var _command = _interopRequireDefault(require("../lib/cli/command"));
13
12
  var _config = require("../lib/media-import/config");
14
13
  var _tracker = require("../lib/tracker");
@@ -24,9 +23,7 @@ async function vipImportValidateFilesCmd(arg = []) {
24
23
  *
25
24
  * Manipulating the file path/name to extract the folder name
26
25
  */
27
- const folder = arg.join(); // File comes in as an array as part of the args- turn it into a string
28
- arg = _url.default.parse(folder); // Then parse the file to its URL parts
29
- const filePath = arg.path; // Extract the path of the file
26
+ const filePath = arg.join(); // File comes in as an array as part of the args- turn it into a string
30
27
 
31
28
  if (!(await (0, _vipImportValidateFiles.isDirectory)(filePath))) {
32
29
  console.error(_chalk.default.red('✕ Error:'), 'The given path is not a directory. Provide a valid directory path.');
@@ -10,6 +10,7 @@ var _socket2 = _interopRequireDefault(require("socket.io-stream"));
10
10
  var _stream = require("stream");
11
11
  var _wpSsh = require("../commands/wp-ssh");
12
12
  var _api = _interopRequireWildcard(require("../lib/api"));
13
+ var _app = require("../lib/app");
13
14
  var _command = _interopRequireWildcard(require("../lib/cli/command"));
14
15
  var exit = _interopRequireWildcard(require("../lib/cli/exit"));
15
16
  var _format = require("../lib/cli/format");
@@ -20,7 +21,7 @@ var _tracker = require("../lib/tracker");
20
21
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
21
22
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
22
23
  const debug = (0, _debug.default)('@automattic/vip:wp');
23
- const appQuery = `id, name,
24
+ const appQuery = `id, name, typeId,
24
25
  organization {
25
26
  id
26
27
  name
@@ -103,7 +104,6 @@ const bindStreamEvents = ({
103
104
  if (!isSubShell) {
104
105
  subShellRl.close();
105
106
  process.exit();
106
- return;
107
107
  }
108
108
  subShellRl.resume();
109
109
  subShellRl.prompt();
@@ -131,6 +131,14 @@ const getTokenForCommand = async (appId, envId, command) => {
131
131
  }
132
132
  });
133
133
  };
134
+
135
+ /**
136
+ * Returns the error so it can be caught by the `socket.on('error')`.
137
+ *
138
+ * @param {Error} err
139
+ * @returns {Error}
140
+ */
141
+ const onSocketError = err => err;
134
142
  const launchCommandAndGetStreams = async ({
135
143
  socket,
136
144
  guid,
@@ -162,10 +170,7 @@ const launchCommandAndGetStreams = async ({
162
170
  socket.close();
163
171
  exit.withError(`Cancel received from server: ${message}`);
164
172
  });
165
- (0, _socket2.default)(socket).on('error', err => {
166
- // This returns the error so it can be catched by the socket.on('error')
167
- return err;
168
- });
173
+ (0, _socket2.default)(socket).off('error', onSocketError).on('error', onSocketError);
169
174
  socket.on('error', err => {
170
175
  if (err === 'Rate limit exceeded') {
171
176
  console.log(_chalk.default.red('\nError:'), 'Rate limit exceeded: Please wait a moment and try again.');
@@ -262,10 +267,17 @@ const bindReconnectEvents = ({
262
267
 
263
268
  // create a new input stream so that we can still catch things like SIGINT while reconnecting
264
269
  if (currentJob.stdinStream) {
265
- process.stdin.unpipe(currentJob.stdinStream);
270
+ unpipeStreamsFromProcess({
271
+ stdin: currentJob.stdinStream,
272
+ stdout: currentJob.stdoutStream
273
+ });
266
274
  }
267
- process.stdin.pipe(_socket2.default.createStream());
275
+ currentJob.stdinStream = _socket2.default.createStream();
268
276
  currentJob.stdoutStream = _socket2.default.createStream();
277
+ pipeStreamsToProcess({
278
+ stdin: currentJob.stdinStream,
279
+ stdout: currentJob.stdoutStream
280
+ });
269
281
  bindStreamEvents({
270
282
  subShellRl,
271
283
  isSubShell,
@@ -306,6 +318,7 @@ const examples = [{
306
318
  const {
307
319
  id: appId,
308
320
  name: appName,
321
+ typeId: appTypeId,
309
322
  organization: {
310
323
  id: orgId
311
324
  }
@@ -314,6 +327,9 @@ const examples = [{
314
327
  id: envId,
315
328
  type: envName
316
329
  } = opts.env;
330
+ if ((0, _app.isAppNodejs)(appTypeId)) {
331
+ exit.withError('WP-CLI commands are not supported on Node.js environments.');
332
+ }
317
333
 
318
334
  /* eslint-disable camelcase */
319
335
  const commonTrackingParams = {
@@ -55,7 +55,8 @@ class DevEnvImportSQLCommand {
55
55
  } = this.options;
56
56
  const resolvedPath = await (0, _devEnvironmentCore.resolveImportPath)(this.slug, this.fileName, searchReplace, inPlace);
57
57
  if (!this.options.skipValidate) {
58
- if (!(await (0, _devEnvironmentLando.isEnvUp)(lando, (0, _devEnvironmentCore.getEnvironmentPath)(this.slug)))) {
58
+ const isUp = (await Promise.all([(0, _devEnvironmentLando.isContainerRunning)(lando, this.slug, 'php'), (0, _devEnvironmentLando.isContainerRunning)(lando, this.slug, 'database')])).every(Boolean);
59
+ if (!isUp) {
59
60
  throw new _userError.default('Environment needs to be started first');
60
61
  }
61
62
  const expectedDomain = `${this.slug}.${lando.config.domain}`;
@@ -138,12 +138,12 @@ class PhpMyAdminCommand {
138
138
  const {
139
139
  default: open
140
140
  } = await import('open');
141
- void open(url, {
141
+ return open(url, {
142
142
  wait: false
143
143
  });
144
144
  }
145
145
  async getStatus() {
146
- return await getPhpMyAdminStatus(this.app.id, this.env.id);
146
+ return getPhpMyAdminStatus(this.app.id, this.env.id);
147
147
  }
148
148
  async maybeEnablePhpMyAdmin() {
149
149
  const status = await this.getStatus();
@@ -29,8 +29,6 @@ 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
34
32
  const proxyAgent = (0, _proxyAgent.createProxyAgent)(url);
35
33
  debug('running fetch', url);
36
34
  return (0, _nodeFetch.default)(url, {
@@ -316,21 +316,19 @@ _args.default.argv = async function (argv, cb) {
316
316
  }
317
317
 
318
318
  // Negotiate flag values
319
- switch (_opts.module) {
320
- case 'import-media':
321
- if ([true, 'true', 'yes'].includes(options.saveErrorLog)) {
322
- options.saveErrorLog = 'true';
323
- } else if ([false, 'false', 'no'].includes(options.saveErrorLog)) {
324
- options.saveErrorLog = 'false';
325
- } else {
326
- options.saveErrorLog = 'prompt';
327
- }
328
- break;
319
+ if (_opts.module === 'import-media') {
320
+ if ([true, 'true', 'yes'].includes(options.saveErrorLog)) {
321
+ options.saveErrorLog = 'true';
322
+ } else if ([false, 'false', 'no'].includes(options.saveErrorLog)) {
323
+ options.saveErrorLog = 'false';
324
+ } else {
325
+ options.saveErrorLog = 'prompt';
326
+ }
329
327
  }
330
328
 
331
329
  // Prompt for confirmation if necessary
332
330
  if (_opts.requireConfirm && !options.force) {
333
- /** @type {Tuple[]} */
331
+ /** @type {import('./format').Tuple[]} */
334
332
  const info = [];
335
333
  if (options.app) {
336
334
  info.push({
@@ -2,19 +2,38 @@
2
2
 
3
3
  exports.__esModule = true;
4
4
  exports.default = void 0;
5
+ exports.loadConfigFile = loadConfigFile;
5
6
  var _debug = _interopRequireDefault(require("debug"));
7
+ var _nodeFs = require("node:fs");
8
+ var _nodePath = _interopRequireDefault(require("node:path"));
6
9
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
10
+ // I don't like using synchronous versions, but until we migrate to ESM, we have to.
11
+
7
12
  const debug = (0, _debug.default)('@automattic/vip:lib:cli:config');
8
- let configFromFile;
9
- try {
13
+ function loadConfigFile() {
14
+ const paths = [
10
15
  // Get `local` config first; this will only exist in dev as it's npmignore-d.
11
- // eslint-disable-next-line @typescript-eslint/no-var-requires
12
- configFromFile = require('../../../config/config.local.json');
13
- debug('Loaded config data from config.local.json');
14
- } catch {
15
- // Fall back to `publish` config file.
16
- // eslint-disable-next-line @typescript-eslint/no-var-requires
17
- configFromFile = require('../../../config/config.publish.json');
18
- debug('Loaded config data from config.publish.json');
16
+ _nodePath.default.join(__dirname, '../../../config/config.local.json'), _nodePath.default.join(__dirname, '../../../config/config.publish.json')];
17
+ for (const filePath of paths) {
18
+ try {
19
+ const data = (0, _nodeFs.readFileSync)(filePath, 'utf-8');
20
+ debug(`Found config file at ${filePath}`);
21
+ return JSON.parse(data);
22
+ } catch (err) {
23
+ if (!(err instanceof Error) || !('code' in err) || err.code !== 'ENOENT') {
24
+ debug(`Error reading config file at ${filePath}:`, err);
25
+ }
26
+ }
27
+ }
28
+ return null;
29
+ }
30
+ const configFromFile = loadConfigFile();
31
+ if (null === configFromFile) {
32
+ // This should not happen because `config/config.publish.json` is always present.
33
+ console.error('FATAL ERROR: Could not find a valid configuration file');
34
+ process.exit(1);
19
35
  }
20
- var _default = exports.default = configFromFile;
36
+
37
+ // Without this, TypeScript will export `configFromFile` as `Config | null`
38
+ const exportedConfig = configFromFile;
39
+ var _default = exports.default = exportedConfig;
@@ -91,14 +91,10 @@ function keyValue(values) {
91
91
  value
92
92
  } of values) {
93
93
  let formattedValue;
94
- switch (key.toLowerCase() // NOSONAR
95
- ) {
96
- case 'environment':
97
- formattedValue = formatEnvironment(value);
98
- break;
99
- default:
100
- formattedValue = value;
101
- break;
94
+ if (key.toLowerCase() === 'environment') {
95
+ formattedValue = formatEnvironment(value);
96
+ } else {
97
+ formattedValue = value;
102
98
  }
103
99
  lines.push(`+ ${key}: ${formattedValue}`);
104
100
  }
@@ -315,7 +315,8 @@ const formatSoftwareSettings = (softwareSetting, includes, format) => {
315
315
  if (includes.includes('available_versions')) {
316
316
  result.available_versions = _optionsForVersion(softwareSetting).filter(option => !option.deprecated).map(option => option.value);
317
317
  if (format !== 'json') {
318
- result.available_versions = result.available_versions.sort().join(',');
318
+ result.available_versions.sort((lhs, rhs) => lhs.localeCompare(rhs));
319
+ result.available_versions = result.available_versions.join(',');
319
320
  }
320
321
  }
321
322
  return result;
@@ -155,8 +155,6 @@ function preProcessInstanceData(instanceData) {
155
155
  // We need to make sure the redirect is an absolute path
156
156
  newInstanceData.mediaRedirectDomain = `https://${instanceData.mediaRedirectDomain}`;
157
157
  }
158
-
159
- // isNaN supports only number in TypeScript, actually, because isNaN('123') returns false despite being a string
160
158
  if (isNaN(instanceData.wordpress.tag)) {
161
159
  newInstanceData.wordpress.tag = 'trunk';
162
160
  }
@@ -692,7 +690,6 @@ async function maybeUpdateVersion(lando, slug) {
692
690
  */
693
691
  function fetchVersionList() {
694
692
  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
696
693
  const proxyAgent = (0, _proxyAgent.createProxyAgent)(url);
697
694
  return (0, _nodeFetch.default)(url, {
698
695
  agent: proxyAgent ?? undefined
@@ -4,6 +4,7 @@ exports.__esModule = true;
4
4
  exports.bootstrapLando = bootstrapLando;
5
5
  exports.checkEnvHealth = checkEnvHealth;
6
6
  exports.getProxyContainer = getProxyContainer;
7
+ exports.isContainerRunning = isContainerRunning;
7
8
  exports.isEnvUp = isEnvUp;
8
9
  exports.landoDestroy = landoDestroy;
9
10
  exports.landoExec = landoExec;
@@ -594,4 +595,18 @@ function validateDockerInstalled(lando) {
594
595
  throw new Error(`docker-compose version ${compose} is not supported. Please upgrade to version 2.0.0 or higher - https://docs.docker.com/compose/install/`);
595
596
  }
596
597
  }
598
+ }
599
+ async function isContainerRunning(lando, slug, serviceName) {
600
+ const envPath = (0, _devEnvironmentCore.getEnvironmentPath)(slug);
601
+ const app = await getLandoApplication(lando, envPath);
602
+ const {
603
+ docker
604
+ } = app.engine;
605
+ const containers = await docker.listContainers({
606
+ filters: {
607
+ label: [`com.docker.compose.project=${app.project}`, `com.docker.compose.service=${serviceName}`],
608
+ status: ['running']
609
+ }
610
+ });
611
+ return containers.length > 0;
597
612
  }
@@ -5,17 +5,18 @@ exports.default = void 0;
5
5
  var _debug = _interopRequireDefault(require("debug"));
6
6
  var _insecure = _interopRequireDefault(require("./keychain/insecure"));
7
7
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
8
- let exportValue;
8
+ let keychain;
9
9
  const debug = (0, _debug.default)('@automattic/vip:keychain');
10
10
  try {
11
11
  // Try using Secure keychain ("keytar") first
12
12
  // eslint-disable-next-line @typescript-eslint/no-var-requires
13
13
  const Secure = require('./keychain/secure');
14
- exportValue = new Secure();
14
+ keychain = new Secure();
15
15
  } catch (error) {
16
16
  debug('Cannot use Secure keychain; falling back to Insecure keychain (Details: %o)', error);
17
17
 
18
18
  // Fallback to Insecure keychain if we can't
19
- exportValue = new _insecure.default('vip-go-cli');
19
+ keychain = new _insecure.default('vip-go-cli');
20
20
  }
21
- var _default = exports.default = exportValue;
21
+ const exportedKeychain = keychain;
22
+ var _default = exports.default = exportedKeychain;
@@ -44,7 +44,7 @@ async function fileLineValidations(appId, envId, fileName, validations, searchRe
44
44
  await new Promise(resolve => readInterface.on('close', resolve));
45
45
  readInterface.close();
46
46
  return Promise.all(validations.map(validation => {
47
- if (Object.prototype.hasOwnProperty.call(validation, 'postLineExecutionProcessing') && typeof validation.postLineExecutionProcessing === 'function') {
47
+ if (Object.hasOwn(validation, 'postLineExecutionProcessing') && typeof validation.postLineExecutionProcessing === 'function') {
48
48
  return validation.postLineExecutionProcessing({
49
49
  fileName,
50
50
  isImport,
package/docs/CHANGELOG.md CHANGED
@@ -1,5 +1,42 @@
1
1
  ## Changelog
2
2
 
3
+ ### 3.19.2
4
+
5
+ * Improve environment checks when importing or syncing local databases to prevent common errors.
6
+ * Remove outdated options and clarify usage instructions for environment info commands.
7
+ * Prevent certain unsupported commands from running in incompatible environments.
8
+ * Upgrade internal components for better compatibility and future support.
9
+
10
+ * refactor(dev-env): deprecate `extended` option in `vip dev-env info`
11
+ * build(deps-dev): bump typescript from 5.8.3 to 5.9.2
12
+ * build(deps): bump @automattic/vip-search-replace from 1.1.3 to 2.0.0
13
+ * Do not allow WP-CLI commands to be run from Node Apps
14
+ * build(deps-dev): bump @types/node from 24.1.0 to 24.2.0
15
+ * refactor(dev-env): use `isContainerRunning` for import and sync commands
16
+
17
+ **Full Changelog**: https://github.com/Automattic/vip-cli/compare/3.19.1...3.19.2
18
+
19
+ ### 3.19.1
20
+
21
+ * build(deps-dev): bump @types/node from 24.0.12 to 24.0.13
22
+ * build(deps): bump open from 10.1.2 to 10.2.0
23
+ * build(deps-dev): bump @types/node from 24.0.13 to 24.0.14
24
+ * build(deps): bump step-security/harden-runner from 2.12.2 to 2.13.0
25
+ * ci: add AI Changelog workflow
26
+ * build(deps-dev): bump @types/node from 24.0.14 to 24.0.15
27
+ * build(deps-dev): bump @types/node from 24.0.15 to 24.1.0
28
+ * build(deps-dev): bump the testing group with 3 updates
29
+ * security: update `form-data` to fix CVE-2025-7783
30
+ * fix(wp): resource leak during reconnection
31
+ * fix(wp): EventEmitter memory leak for `error` listeners
32
+ * refactor: remove deprecated `url.parse()` in favor of WHATWG URL API
33
+ * ci: simplify changelog generation workflow
34
+ * chore(deps): replace `socket.io-stream` with a fork
35
+ * fix: code smells detected by SonarCloud
36
+ * chore(deps): update Lando
37
+
38
+ **Full Changelog**: https://github.com/Automattic/vip-cli/compare/3.19.0...3.19.1
39
+
3
40
  ### 3.19.0
4
41
 
5
42
  * build(deps-dev): bump the testing group across 1 directory with 4 updates