@automattic/vip 3.23.2 → 3.23.4-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.
@@ -49,7 +49,9 @@ cmd.argv(process.argv, async (arg, opt) => {
49
49
  if (hasConfiguration) {
50
50
  slug = await (0, _devEnvironmentCli.getEnvironmentName)(environmentNameOptions);
51
51
  }
52
- const lando = await (0, _devEnvironmentLando.bootstrapLando)();
52
+ const lando = await (0, _devEnvironmentLando.bootstrapLando)({
53
+ logFile: (0, _devEnvironmentCli.getDevEnvLogFile)(slug)
54
+ });
53
55
  (0, _devEnvironmentCli.validateDependencies)(lando);
54
56
  debug('Args: ', arg, 'Options: ', opt);
55
57
  const trackingInfo = {
@@ -23,7 +23,9 @@ const examples = [{
23
23
  usage
24
24
  }).option('slug', 'A unique name for a local environment. Default is "vip-local".', undefined, _devEnvironmentCli.processSlug).option('soft', 'Preserve an environment’s configuration files; allows an environment to be regenerated with the start command.').examples(examples).argv(process.argv, async (arg, opt) => {
25
25
  const slug = await (0, _devEnvironmentCli.getEnvironmentName)(opt);
26
- const lando = await (0, _devEnvironmentLando.bootstrapLando)();
26
+ const lando = await (0, _devEnvironmentLando.bootstrapLando)({
27
+ logFile: (0, _devEnvironmentCli.getDevEnvLogFile)(slug)
28
+ });
27
29
  (0, _devEnvironmentCli.validateDependencies)(lando);
28
30
  const trackingInfo = (0, _devEnvironmentCli.getEnvTrackingInfo)(slug);
29
31
  await (0, _tracker.trackEvent)('dev_env_destroy_command_execute', trackingInfo);
@@ -25,7 +25,9 @@ const examples = [{
25
25
  usage
26
26
  }).option('slug', 'A unique name for a local environment. Default is "vip-local".', undefined, _devEnvironmentCli.processSlug).option('force', 'Skip validation for a local environment to be in a running state.', undefined, _devEnvironmentCli.processBooleanOption).option('quiet', 'Suppress informational messages.', undefined, _devEnvironmentCli.processBooleanOption).examples(examples).argv(process.argv, async (unmatchedArgs, opt) => {
27
27
  const slug = await (0, _devEnvironmentCli.getEnvironmentName)(opt);
28
- const lando = await (0, _devEnvironmentLando.bootstrapLando)();
28
+ const lando = await (0, _devEnvironmentLando.bootstrapLando)({
29
+ logFile: (0, _devEnvironmentCli.getDevEnvLogFile)(slug)
30
+ });
29
31
  (0, _devEnvironmentCli.validateDependencies)(lando);
30
32
  const trackingInfo = (0, _devEnvironmentCli.getEnvTrackingInfo)(slug);
31
33
  await (0, _tracker.trackEvent)('dev_env_exec_command_execute', trackingInfo);
@@ -23,16 +23,18 @@ const examples = [{
23
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) => {
24
24
  let trackingInfo;
25
25
  let slug;
26
- const lando = await (0, _devEnvironmentLando.bootstrapLando)();
27
26
  if (opt.all) {
28
27
  trackingInfo = {
29
28
  all: true
30
29
  };
31
- slug = '';
30
+ slug = undefined;
32
31
  } else {
33
32
  slug = await (0, _devEnvironmentCli.getEnvironmentName)(opt);
34
33
  trackingInfo = (0, _devEnvironmentCli.getEnvTrackingInfo)(slug);
35
34
  }
35
+ const lando = await (0, _devEnvironmentLando.bootstrapLando)({
36
+ logFile: (0, _devEnvironmentCli.getDevEnvLogFile)(slug)
37
+ });
36
38
  (0, _devEnvironmentCli.validateDependencies)(lando);
37
39
  await (0, _tracker.trackEvent)('dev_env_info_command_execute', trackingInfo);
38
40
  debug('Args: ', arg, 'Options: ', opt);
@@ -16,7 +16,9 @@ const examples = [{
16
16
  (0, _command.default)({
17
17
  usage
18
18
  }).examples(examples).argv(process.argv, async () => {
19
- const lando = await (0, _devEnvironmentLando.bootstrapLando)();
19
+ const lando = await (0, _devEnvironmentLando.bootstrapLando)({
20
+ logFile: (0, _devEnvironmentCli.getDevEnvLogFile)()
21
+ });
20
22
  lando.events.constructor.prototype.setMaxListeners(1024);
21
23
  (0, _devEnvironmentCli.validateDependencies)(lando);
22
24
  const trackingInfo = {
@@ -25,7 +25,9 @@ const examples = [{
25
25
  usage
26
26
  }).option('slug', 'A unique name for a local environment. Default is "vip-local".', undefined, _devEnvironmentCli.processSlug).option(['f', 'follow'], 'Continually output logs as they are generated.').option('service', 'Restrict to a single service.').examples(examples).argv(process.argv, async (arg, opt) => {
27
27
  const slug = await (0, _devEnvironmentCli.getEnvironmentName)(opt);
28
- const lando = await (0, _devEnvironmentLando.bootstrapLando)();
28
+ const lando = await (0, _devEnvironmentLando.bootstrapLando)({
29
+ logFile: (0, _devEnvironmentCli.getDevEnvLogFile)(slug)
30
+ });
29
31
  (0, _devEnvironmentCli.validateDependencies)(lando);
30
32
  const trackingInfo = (0, _devEnvironmentCli.getEnvTrackingInfo)(slug);
31
33
  await (0, _tracker.trackEvent)('dev_env_logs_command_execute', trackingInfo);
@@ -34,7 +34,9 @@ const examples = [{
34
34
  }).option('soft', 'Preserve an environment’s configuration files; allows an environment to be regenerated with the start command.').option('force', 'Skip confirmation.').examples(examples).argv(process.argv, async (arg, opt) => {
35
35
  debug('Args: ', arg, 'Options: ', opt);
36
36
  const allEnvNames = (0, _devEnvironmentCore.getAllEnvironmentNames)();
37
- const lando = await (0, _devEnvironmentLando.bootstrapLando)();
37
+ const lando = await (0, _devEnvironmentLando.bootstrapLando)({
38
+ logFile: (0, _devEnvironmentCli.getDevEnvLogFile)()
39
+ });
38
40
  if (allEnvNames.length === 0) {
39
41
  console.log('No environments to purge!');
40
42
  return;
@@ -57,7 +57,9 @@ function getCommand(args) {
57
57
  usage
58
58
  }).option('slug', 'A unique name for a local environment. Default is "vip-local".', undefined, _devEnvironmentCli.processSlug).option('root', 'Create with root privileges.').option('service', 'Restrict to a single service.').examples(examples).argv(process.argv, async (args, opt) => {
59
59
  const slug = await (0, _devEnvironmentCli.getEnvironmentName)(opt);
60
- const lando = await (0, _devEnvironmentLando.bootstrapLando)();
60
+ const lando = await (0, _devEnvironmentLando.bootstrapLando)({
61
+ logFile: (0, _devEnvironmentCli.getDevEnvLogFile)(slug)
62
+ });
61
63
  (0, _devEnvironmentCli.validateDependencies)(lando);
62
64
  const trackingInfo = (0, _devEnvironmentCli.getEnvTrackingInfo)(slug);
63
65
  await (0, _tracker.trackEvent)('dev_env_shell_command_execute', trackingInfo);
@@ -34,7 +34,9 @@ const examples = [{
34
34
  usage
35
35
  }).option('slug', 'A unique name for a local environment. Default is "vip-local".', undefined, _devEnvironmentCli.processSlug).option('skip-rebuild', 'Only start services that are not in a running state.').option(['w', 'skip-wp-versions-check'], 'Skip the prompt to update WordPress; occurs if the last major release version is not configured.').option('vscode', 'Generate a Visual Studio Code Workspace file (deprecated, use --editor=vscode instead).').option('editor', 'Generate a workspace file for the specified editor (supports: vscode, cursor, windsurf, phpstorm).').examples(examples).argv(process.argv, async (arg, opt) => {
36
36
  const slug = await (0, _devEnvironmentCli.getEnvironmentName)(opt);
37
- const lando = await (0, _devEnvironmentLando.bootstrapLando)();
37
+ const lando = await (0, _devEnvironmentLando.bootstrapLando)({
38
+ logFile: (0, _devEnvironmentCli.getDevEnvLogFile)(slug)
39
+ });
38
40
  (0, _devEnvironmentCli.validateDependencies)(lando);
39
41
  const startProcessing = new Date();
40
42
  const trackingInfo = (0, _devEnvironmentCli.getEnvTrackingInfo)(slug);
@@ -22,24 +22,30 @@ const examples = [{
22
22
  (0, _command.default)({
23
23
  usage
24
24
  }).option('slug', 'A unique name for a local environment. Default is "vip-local".', undefined, _devEnvironmentCli.processSlug).option('all', 'Stop all local environments.').examples(examples).argv(process.argv, async (arg, opt) => {
25
- const lando = await (0, _devEnvironmentLando.bootstrapLando)();
26
- (0, _devEnvironmentCli.validateDependencies)(lando);
27
25
  debug('Args: ', arg, 'Options: ', opt);
28
26
 
29
27
  /** @type {Record< string, unknown >} */
30
28
  let trackingInfo;
31
29
  /** @type {string[]} */
32
30
  let environments;
31
+ /** @type {string|undefined} */
32
+ let logSlug;
33
33
  if (opt.all) {
34
34
  trackingInfo = {
35
35
  all: true
36
36
  };
37
37
  environments = (0, _devEnvironmentCore.getAllEnvironmentNames)();
38
+ logSlug = undefined;
38
39
  } else {
39
40
  const slug = await (0, _devEnvironmentCli.getEnvironmentName)(opt);
40
41
  trackingInfo = (0, _devEnvironmentCli.getEnvTrackingInfo)(slug);
41
42
  environments = [slug];
43
+ logSlug = slug;
42
44
  }
45
+ const lando = await (0, _devEnvironmentLando.bootstrapLando)({
46
+ logFile: (0, _devEnvironmentCli.getDevEnvLogFile)(logSlug)
47
+ });
48
+ (0, _devEnvironmentCli.validateDependencies)(lando);
43
49
  await (0, _tracker.trackEvent)('dev_env_stop_command_execute', trackingInfo);
44
50
  for (const slug of environments) {
45
51
  try {
@@ -70,7 +70,9 @@ const appQuery = `
70
70
  multisite: env.isMultisite
71
71
  });
72
72
  await trackerFn('execute');
73
- const lando = await (0, _devEnvironmentLando.bootstrapLando)();
73
+ const lando = await (0, _devEnvironmentLando.bootstrapLando)({
74
+ logFile: (0, _devEnvironmentCli.getDevEnvLogFile)(slug)
75
+ });
74
76
  const isUp = (await Promise.all([(0, _devEnvironmentLando.isContainerRunning)(lando, slug, 'php'), (0, _devEnvironmentLando.isContainerRunning)(lando, slug, 'database')])).every(Boolean);
75
77
  if (!isUp && !opt.force) {
76
78
  await trackerFn('env_not_running_error', {
@@ -31,7 +31,9 @@ const cmd = (0, _command.default)({
31
31
  cmd.examples(examples);
32
32
  cmd.argv(process.argv, async (arg, opt) => {
33
33
  const slug = await (0, _devEnvironmentCli.getEnvironmentName)(opt);
34
- const lando = await (0, _devEnvironmentLando.bootstrapLando)();
34
+ const lando = await (0, _devEnvironmentLando.bootstrapLando)({
35
+ logFile: (0, _devEnvironmentCli.getDevEnvLogFile)(slug)
36
+ });
35
37
  (0, _devEnvironmentCli.validateDependencies)(lando);
36
38
  const trackingInfo = (0, _devEnvironmentCli.getEnvTrackingInfo)(slug);
37
39
  await (0, _tracker.trackEvent)('dev_env_update_command_execute', trackingInfo);
@@ -131,6 +131,7 @@ Are you sure you want to import the contents of the URL?
131
131
  sourceIsLocal = true;
132
132
  const fileMeta = await (0, _clientFileUploader.getFileMeta)(fileNameOrURL);
133
133
  fileMeta.fileName = fileNameOrURL;
134
+ let lastProgress = '';
134
135
  const {
135
136
  fileMeta: {
136
137
  basename
@@ -141,8 +142,15 @@ Are you sure you want to import the contents of the URL?
141
142
  app,
142
143
  env,
143
144
  fileMeta,
144
- progressCallback: percentage => console.log(`Upload progress: ${percentage}%`)
145
+ progressCallback: percentage => {
146
+ if (percentage === lastProgress) {
147
+ return;
148
+ }
149
+ lastProgress = percentage;
150
+ process.stdout.write(`\rUpload progress: ${percentage} `);
151
+ }
145
152
  });
153
+ process.stdout.write('\n');
146
154
 
147
155
  // small debug info to keep variables used
148
156
  debug('Uploaded file basename:', basename);
@@ -24,7 +24,9 @@ class DevEnvImportSQLCommand {
24
24
  this.slug = slug;
25
25
  }
26
26
  async run() {
27
- const lando = await (0, _devEnvironmentLando.bootstrapLando)();
27
+ const lando = await (0, _devEnvironmentLando.bootstrapLando)({
28
+ logFile: (0, _devEnvironmentCli.getDevEnvLogFile)(this.slug)
29
+ });
28
30
  (0, _devEnvironmentCli.validateDependencies)(lando);
29
31
  (0, _sql.validateImportFileExtension)(this.fileName);
30
32
  const dumpDetails = await (0, _database.getSqlDumpDetails)(this.fileName);
@@ -440,30 +440,39 @@ _args.default.argv = async function (argv, cb) {
440
440
  break;
441
441
  }
442
442
  case 'import-media':
443
- info.push({
444
- key: 'Archive URL',
445
- value: _chalk.default.blue.underline(this.sub)
446
- });
447
- options.overwriteExistingFiles = Object.hasOwn(options, 'overwriteExistingFiles') && Boolean(options.overwriteExistingFiles) && !['false', 'no'].includes(options.overwriteExistingFiles);
448
- info.push({
449
- key: 'Overwrite any existing files',
450
- value: options.overwriteExistingFiles ? '✅ Yes' : `${_chalk.default.red('x')} No`
451
- });
452
- options.importIntermediateImages = Object.hasOwn(options, 'importIntermediateImages') && Boolean(options.importIntermediateImages) && !['false', 'no'].includes(options.importIntermediateImages);
453
- info.push({
454
- key: 'Import intermediate image files',
455
- value: options.importIntermediateImages ? '✅ Yes' : `${_chalk.default.red('x')} No`
456
- });
457
- options.exportFileErrorsToJson = Object.hasOwn(options, 'exportFileErrorsToJson') && Boolean(options.exportFileErrorsToJson) && !['false', 'no'].includes(options.exportFileErrorsToJson);
458
- info.push({
459
- key: 'Export any file errors encountered to a JSON file instead of a plain text file.',
460
- value: options.exportFileErrorsToJson ? '✅ Yes' : `${_chalk.default.red('x')} No`
461
- });
462
- info.push({
463
- key: 'Download file-error logs?',
464
- value: options.saveErrorLog
465
- });
466
- break;
443
+ {
444
+ const isUrl = this.sub && (String(this.sub).startsWith('http://') || String(this.sub).startsWith('https://'));
445
+ const archiveLabel = isUrl ? 'Archive URL' : 'Archive Path';
446
+ info.push({
447
+ key: archiveLabel,
448
+ value: _chalk.default.blue.underline(this.sub)
449
+ });
450
+
451
+ // Update confirmation message if it's a local path
452
+ if (!isUrl && 'string' === typeof _opts.requireConfirm) {
453
+ message = message.replaceAll('the URL', 'the path');
454
+ }
455
+ options.overwriteExistingFiles = Object.hasOwn(options, 'overwriteExistingFiles') && Boolean(options.overwriteExistingFiles) && !['false', 'no'].includes(options.overwriteExistingFiles);
456
+ info.push({
457
+ key: 'Overwrite any existing files',
458
+ value: options.overwriteExistingFiles ? '✅ Yes' : `${_chalk.default.red('x')} No`
459
+ });
460
+ options.importIntermediateImages = Object.hasOwn(options, 'importIntermediateImages') && Boolean(options.importIntermediateImages) && !['false', 'no'].includes(options.importIntermediateImages);
461
+ info.push({
462
+ key: 'Import intermediate image files',
463
+ value: options.importIntermediateImages ? ' Yes' : `${_chalk.default.red('x')} No`
464
+ });
465
+ options.exportFileErrorsToJson = Object.hasOwn(options, 'exportFileErrorsToJson') && Boolean(options.exportFileErrorsToJson) && !['false', 'no'].includes(options.exportFileErrorsToJson);
466
+ info.push({
467
+ key: 'Export any file errors encountered to a JSON file instead of a plain text file.',
468
+ value: options.exportFileErrorsToJson ? '✅ Yes' : `${_chalk.default.red('x')} No`
469
+ });
470
+ info.push({
471
+ key: 'Download file-error logs?',
472
+ value: options.saveErrorLog
473
+ });
474
+ break;
475
+ }
467
476
  default:
468
477
  }
469
478
  const skipPrompt = _opts.skipConfirmPrompt || false;
@@ -4,6 +4,9 @@ exports.__esModule = true;
4
4
  exports.DEFAULT_SLUG = exports.CONFIGURATION_FOLDER = void 0;
5
5
  exports.addDevEnvConfigurationOptions = addDevEnvConfigurationOptions;
6
6
  exports.ensureValidPathsInOptions = ensureValidPathsInOptions;
7
+ exports.formatDevEnvLogSlug = formatDevEnvLogSlug;
8
+ exports.formatDevEnvLogTimestamp = formatDevEnvLogTimestamp;
9
+ exports.getDevEnvLogFile = getDevEnvLogFile;
7
10
  exports.getEnvTrackingInfo = getEnvTrackingInfo;
8
11
  exports.getEnvironmentName = getEnvironmentName;
9
12
  exports.getEnvironmentStartCommand = getEnvironmentStartCommand;
@@ -666,6 +669,16 @@ function processSlug(value) {
666
669
  // eslint-disable-next-line @typescript-eslint/no-base-to-string
667
670
  return (value ?? '').toString().toLowerCase();
668
671
  }
672
+ function formatDevEnvLogTimestamp(date) {
673
+ return date.toISOString().replace(/\..*$/, '').replace(/[-:]/g, '').replace('T', '-');
674
+ }
675
+ function formatDevEnvLogSlug(slug) {
676
+ return slug.replace(/[^a-z0-9_-]+/gi, '-').toLowerCase();
677
+ }
678
+ function getDevEnvLogFile(slug) {
679
+ const slugPart = slug ? formatDevEnvLogSlug(slug) : 'all';
680
+ return `vip-dev-env-${slugPart}-${formatDevEnvLogTimestamp(new Date())}.log`;
681
+ }
669
682
  function processVersionOption(value) {
670
683
  if (typeof value === 'string' || typeof value === 'number') {
671
684
  if (!isNaN(value) && Number(value) % 1 === 0) {
@@ -22,14 +22,17 @@ var _bootstrap = require("lando/lib/bootstrap");
22
22
  var _lando = _interopRequireDefault(require("lando/lib/lando"));
23
23
  var _utils = _interopRequireDefault(require("lando/plugins/lando-core/lib/utils"));
24
24
  var _build = _interopRequireDefault(require("lando/plugins/lando-tooling/lib/build"));
25
+ var _nodeChild_process = require("node:child_process");
25
26
  var _promises = require("node:dns/promises");
26
27
  var _promises2 = require("node:fs/promises");
27
28
  var _nodeOs = require("node:os");
28
29
  var _nodePath = _interopRequireWildcard(require("node:path"));
30
+ var _nodeUtil = require("node:util");
29
31
  var _semver = require("semver");
30
32
  var _devEnvironmentCore = require("./dev-environment-core");
31
33
  var _dockerUtils = require("./docker-utils");
32
34
  var _devEnvironment = require("../constants/dev-environment");
35
+ var _env = _interopRequireDefault(require("../env"));
33
36
  var _userError = _interopRequireDefault(require("../user-error"));
34
37
  var _xdgData = require("../xdg-data");
35
38
  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); }
@@ -39,11 +42,117 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
39
42
  */
40
43
  const DEBUG_KEY = '@automattic/vip:bin:dev-environment';
41
44
  const debug = (0, _debug.default)(DEBUG_KEY);
45
+ const execFileAsync = (0, _nodeUtil.promisify)(_nodeChild_process.execFile);
46
+ const bannerLabelWidth = 18;
47
+ let logPathRegistered = false;
48
+ let resolvedLogPath = null;
49
+ const getLogFilePath = config => {
50
+ if (config.logFile) {
51
+ return _nodePath.default.isAbsolute(config.logFile) ? config.logFile : _nodePath.default.join(config.logDir ?? '', config.logFile);
52
+ }
53
+ if (config.logDir && config.logName) {
54
+ return _nodePath.default.join(config.logDir, `${config.logName}.log`);
55
+ }
56
+ return null;
57
+ };
58
+ const registerLogPathOutput = config => {
59
+ if (logPathRegistered) {
60
+ return;
61
+ }
62
+ resolvedLogPath = getLogFilePath(config);
63
+ if (!resolvedLogPath) {
64
+ return;
65
+ }
66
+ logPathRegistered = true;
67
+ process.once('exit', () => {
68
+ if (resolvedLogPath && process.stdout.isTTY) {
69
+ console.log(`\n ${formatBannerLine(_chalk.default.cyan('COMMAND LOG FILE'.padEnd(bannerLabelWidth)), resolvedLogPath)}`);
70
+ }
71
+ });
72
+ };
73
+ const formatBytes = bytes => {
74
+ const gb = bytes / 1024 ** 3;
75
+ return `${gb.toFixed(1)} GB`;
76
+ };
77
+ const isRecord = value => typeof value === 'object' && value !== null;
78
+ const isDockerPluginInfo = value => isRecord(value);
79
+ const getDockerVersions = async config => {
80
+ const dockerBin = config.dockerBin ?? '';
81
+ const composeBin = config.composeBin ?? '';
82
+ let engine = 'unknown';
83
+ let compose = 'unknown';
84
+ let composePlugin = 'unknown';
85
+ try {
86
+ if (dockerBin) {
87
+ const {
88
+ stdout
89
+ } = await execFileAsync(dockerBin, ['info', '--format', 'json']);
90
+ const dockerData = JSON.parse(stdout);
91
+ if (isRecord(dockerData)) {
92
+ const serverVersion = dockerData.ServerVersion;
93
+ if (typeof serverVersion === 'string') {
94
+ engine = serverVersion;
95
+ }
96
+ const clientInfo = dockerData.ClientInfo;
97
+ if (isRecord(clientInfo)) {
98
+ const plugins = clientInfo.Plugins;
99
+ if (Array.isArray(plugins)) {
100
+ const composePluginEntry = plugins.find(plugin => isDockerPluginInfo(plugin) && plugin.Name === 'compose');
101
+ if (composePluginEntry && typeof composePluginEntry.Version === 'string') {
102
+ composePlugin = composePluginEntry.Version;
103
+ }
104
+ }
105
+ }
106
+ }
107
+ }
108
+ } catch (error) {
109
+ debug('Failed to read docker version info: %O', error);
110
+ }
111
+ try {
112
+ if (composeBin) {
113
+ const {
114
+ stdout
115
+ } = await execFileAsync(composeBin, ['version', '--short']);
116
+ compose = stdout.trim() || compose;
117
+ }
118
+ } catch (error) {
119
+ debug('Failed to read docker-compose version info: %O', error);
120
+ }
121
+ return {
122
+ engine,
123
+ compose,
124
+ composePlugin
125
+ };
126
+ };
127
+ const formatBannerLine = (label, value) => `${label.padEnd(bannerLabelWidth)} ${value}`;
128
+ const writeLogBanner = async config => {
129
+ const logFilePath = getLogFilePath(config);
130
+ if (!logFilePath) {
131
+ return;
132
+ }
133
+ try {
134
+ const existing = await (0, _promises2.stat)(logFilePath);
135
+ if (existing.size > 0) {
136
+ return;
137
+ }
138
+ } catch {
139
+ // File does not exist yet.
140
+ }
141
+ await (0, _promises2.mkdir)(_nodePath.default.dirname(logFilePath), {
142
+ recursive: true
143
+ });
144
+ const dockerVersions = await getDockerVersions(config);
145
+ const command = process.argv.slice(1).join(' ');
146
+ const bannerLines = ['=== VIP Dev Env Log ===', formatBannerLine('COMMAND', command), formatBannerLine('OS', `${_env.default.os.name} ${_env.default.os.version} ${_env.default.os.arch}`), formatBannerLine('NODE', _env.default.node.version), formatBannerLine('VIP-CLI', _env.default.app.version), formatBannerLine('DOCKER ENGINE', dockerVersions.engine), formatBannerLine('DOCKER COMPOSE', dockerVersions.compose), formatBannerLine('COMPOSE PLUGIN', dockerVersions.composePlugin), formatBannerLine('DOCKER BIN', config.dockerBin ?? 'unknown'), formatBannerLine('COMPOSE BIN', config.composeBin ?? 'unknown'), formatBannerLine('RAM', formatBytes((0, _nodeOs.totalmem)())), formatBannerLine('CPU', String((0, _nodeOs.cpus)().length)), '===', '', ''];
147
+ await (0, _promises2.writeFile)(logFilePath, bannerLines.join('\n'), {
148
+ flag: 'a'
149
+ });
150
+ };
42
151
 
43
152
  /**
44
153
  * @return {Promise<LandoConfig>} Lando configuration
45
154
  */
46
- async function getLandoConfig() {
155
+ async function getLandoConfig(options = {}) {
47
156
  // The path will be smth like `yarn/global/node_modules/lando/lib/lando.js`; we need the path up to `lando` (inclusive)
48
157
  const landoPath = (0, _nodePath.dirname)((0, _nodePath.dirname)(require.resolve('lando')));
49
158
  debug(`Getting Lando config, using paths '${landoPath}' for plugins`);
@@ -60,6 +169,7 @@ async function getLandoConfig() {
60
169
  const vipDir = _nodePath.default.join((0, _xdgData.xdgData)(), 'vip');
61
170
  const landoDir = _nodePath.default.join(vipDir, 'lando');
62
171
  const fakeHomeDir = _nodePath.default.join(landoDir, 'home');
172
+ const logDir = _nodePath.default.join(landoDir, 'logs');
63
173
  try {
64
174
  await (0, _promises2.mkdir)(fakeHomeDir, {
65
175
  recursive: true
@@ -69,6 +179,9 @@ async function getLandoConfig() {
69
179
  }
70
180
  const config = {
71
181
  logLevelConsole,
182
+ logDir,
183
+ logFile: options.logFile ?? 'vip-dev-env.log',
184
+ logName: options.logName ?? 'vip-dev-env',
72
185
  configSources: [_nodePath.default.join(landoDir, 'config.yml')],
73
186
  landoFile: '.lando.yml',
74
187
  preLandoFiles: ['.lando.base.yml', '.lando.dist.yml', '.lando.upstream.yml'],
@@ -159,17 +272,25 @@ async function getLandoApplication(lando, instancePath) {
159
272
  debug('getLandoApplication() took %d ms', duration);
160
273
  }
161
274
  }
162
- async function bootstrapLando() {
275
+ async function bootstrapLando(options = {}) {
163
276
  const started = new Date();
164
277
  try {
278
+ if (process.env.DEBUG && !process.env.DEBUG.includes('-winston:*')) {
279
+ process.env.DEBUG = `${process.env.DEBUG},-winston:*`;
280
+ }
165
281
  const socket = await (0, _dockerUtils.getDockerSocket)();
166
- const config = await getLandoConfig();
282
+ const config = await getLandoConfig(options);
167
283
  debug('Docker socket: %j', socket);
168
284
  if (socket) {
169
285
  config.engineConfig = await (0, _dockerUtils.getEngineConfig)(socket);
170
286
  debug('Engine config: %j', config.engineConfig);
171
287
  }
288
+ registerLogPathOutput(config);
289
+ await writeLogBanner(config);
172
290
  const lando = new _lando.default(config);
291
+ _debug.default.log = (message, ...args) => {
292
+ lando.log.debug(message, ...args);
293
+ };
173
294
  lando.events.once('pre-engine-build', async data => {
174
295
  const instanceData = (0, _devEnvironmentCore.readEnvironmentData)(data.name);
175
296
  let registryResolvable = false;