@automattic/vip 3.12.2 → 3.14.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.
- package/assets/dev-env.lando.template.yml.ejs +2 -0
- package/dist/bin/vip-config-envvar-delete.js +1 -1
- package/dist/bin/vip-config-envvar-set.js +1 -1
- package/dist/bin/vip-config-envvar.js +4 -4
- package/dist/bin/vip-dev-env-create.js +4 -1
- package/dist/bin/vip-dev-env-start.js +11 -3
- package/dist/bin/vip-dev-env.js +4 -0
- package/dist/bin/vip-sync.js +1 -1
- package/dist/bin/vip-wp.js +9 -0
- package/dist/commands/dev-env-sync-sql.js +3 -2
- package/dist/commands/wp-ssh.js +213 -0
- package/dist/lib/cli/progress.js +1 -1
- package/dist/lib/dev-environment/dev-environment-cli.js +175 -68
- package/dist/lib/dev-environment/dev-environment-core.js +144 -18
- package/dist/lib/dev-environment/dev-environment-lando.js +2 -8
- package/dist/lib/media-import/progress.js +1 -1
- package/dist/lib/validations/sql.js +3 -3
- package/docs/CHANGELOG.md +51 -0
- package/npm-shrinkwrap.json +470 -950
- package/package.json +8 -11
|
@@ -23,6 +23,7 @@ exports.promptForComponent = promptForComponent;
|
|
|
23
23
|
exports.promptForMultisite = promptForMultisite;
|
|
24
24
|
exports.promptForPhpVersion = promptForPhpVersion;
|
|
25
25
|
exports.promptForText = promptForText;
|
|
26
|
+
exports.promptForURL = promptForURL;
|
|
26
27
|
exports.promptForWordPress = promptForWordPress;
|
|
27
28
|
exports.resolvePath = resolvePath;
|
|
28
29
|
exports.resolvePhpVersion = resolvePhpVersion;
|
|
@@ -161,7 +162,7 @@ function processComponentOptionInput(passedParam, allowLocal) {
|
|
|
161
162
|
}
|
|
162
163
|
return {
|
|
163
164
|
mode: 'image',
|
|
164
|
-
tag: param === 'demo' ? undefined : param
|
|
165
|
+
tag: param === 'demo' || param === 'image' ? undefined : param
|
|
165
166
|
};
|
|
166
167
|
}
|
|
167
168
|
function getOptionsFromAppInfo(appInfo) {
|
|
@@ -190,6 +191,13 @@ function getOptionsFromAppInfo(appInfo) {
|
|
|
190
191
|
// eslint-disable-next-line complexity
|
|
191
192
|
async function promptForArguments(preselectedOptions, defaultOptions, suppressPrompts, create) {
|
|
192
193
|
debug('Provided preselected', preselectedOptions, 'and default', defaultOptions);
|
|
194
|
+
let isVIPUser;
|
|
195
|
+
try {
|
|
196
|
+
const currentUser = await (0, _user.getCurrentUserInfo)(true);
|
|
197
|
+
isVIPUser = currentUser?.isVIP ?? false;
|
|
198
|
+
} catch (err) {
|
|
199
|
+
isVIPUser = false;
|
|
200
|
+
}
|
|
193
201
|
if (suppressPrompts) {
|
|
194
202
|
preselectedOptions = {
|
|
195
203
|
...defaultOptions,
|
|
@@ -208,7 +216,7 @@ async function promptForArguments(preselectedOptions, defaultOptions, suppressPr
|
|
|
208
216
|
elasticsearch: false,
|
|
209
217
|
php: preselectedOptions.php ? resolvePhpVersion(preselectedOptions.php) : await promptForPhpVersion(resolvePhpVersion(defaultOptions.php ?? _devEnvironment.DEV_ENVIRONMENT_DEFAULTS.phpVersion)),
|
|
210
218
|
mariadb: preselectedOptions.mariadb ?? defaultOptions.mariadb,
|
|
211
|
-
mediaRedirectDomain: preselectedOptions.mediaRedirectDomain ?? '',
|
|
219
|
+
mediaRedirectDomain: preselectedOptions.mediaRedirectDomain ?? defaultOptions.mediaRedirectDomain ?? '',
|
|
212
220
|
wordpress: {
|
|
213
221
|
mode: 'image',
|
|
214
222
|
tag: ''
|
|
@@ -241,16 +249,19 @@ async function promptForArguments(preselectedOptions, defaultOptions, suppressPr
|
|
|
241
249
|
if (setMediaRedirectDomain) {
|
|
242
250
|
instanceData.mediaRedirectDomain = defaultOptions.mediaRedirectDomain;
|
|
243
251
|
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
252
|
+
} else if (!create && defaultOptions.mediaRedirectDomain) {
|
|
253
|
+
const mediaRedirectPromptText = 'URL to redirect for missing media files ("n" to disable)?';
|
|
254
|
+
const mediaRedirectDomain = await promptForURL(mediaRedirectPromptText, defaultOptions.mediaRedirectDomain);
|
|
255
|
+
instanceData.mediaRedirectDomain = mediaRedirectDomain;
|
|
256
|
+
}
|
|
257
|
+
instanceData.wordpress = await processWordPress(preselectedOptions.wordpress ?? '', defaultOptions.wordpress ?? '');
|
|
258
|
+
instanceData.appCode = await processComponent('appCode', preselectedOptions.appCode ?? '', defaultOptions.appCode ?? '', suppressPrompts, validateAppCodeLocalPath);
|
|
259
|
+
let preselectedMU = preselectedOptions.muPlugins;
|
|
260
|
+
const defaultMU = defaultOptions.muPlugins;
|
|
261
|
+
if (!preselectedMU && !isVIPUser && (!defaultMU || ['demo', 'image'].includes(defaultMU))) {
|
|
262
|
+
preselectedMU = 'image';
|
|
263
|
+
}
|
|
264
|
+
instanceData.muPlugins = await processComponent('muPlugins', preselectedMU ?? '', defaultMU ?? '', suppressPrompts, validateMuPluginsLocalPath);
|
|
254
265
|
debug(`Processing elasticsearch with preselected "%s"`, preselectedOptions.elasticsearch);
|
|
255
266
|
if ('elasticsearch' in preselectedOptions) {
|
|
256
267
|
instanceData.elasticsearch = Boolean(preselectedOptions.elasticsearch);
|
|
@@ -295,48 +306,41 @@ async function processWordPress(preselectedValue, defaultValue) {
|
|
|
295
306
|
debug(result);
|
|
296
307
|
return result;
|
|
297
308
|
}
|
|
298
|
-
async function processComponent(
|
|
299
|
-
debug(`
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
try {
|
|
304
|
-
const currentUser = await (0, _user.getCurrentUserInfo)(true);
|
|
305
|
-
allowLocal = currentUser?.isVIP ?? false;
|
|
306
|
-
} catch (err) {
|
|
307
|
-
allowLocal = false;
|
|
308
|
-
}
|
|
309
|
+
async function processComponent(componentType, preselectedValue, defaultValue, suppressPrompts, localPathValidator) {
|
|
310
|
+
debug(`Processing the '${componentType}' component, with preselected = %s, default = %s`, preselectedValue, defaultValue);
|
|
311
|
+
const defaultObject = defaultValue ? processComponentOptionInput(defaultValue, true) : null;
|
|
312
|
+
if (preselectedValue && !suppressPrompts) {
|
|
313
|
+
console.log('%s Path to your local %s: %s', _chalk.default.green('✓'), componentDisplayNames[componentType], preselectedValue);
|
|
309
314
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
console.log(`${_chalk.default.green('✓')} Path to your local ${componentDisplayNames[component]}: ${preselectedValue}`);
|
|
315
|
-
}
|
|
316
|
-
} else {
|
|
317
|
-
result = await promptForComponent(component, allowLocal, defaultObject);
|
|
315
|
+
let result = preselectedValue ? processComponentOptionInput(preselectedValue, true) : await promptForComponent(componentType, true, defaultObject);
|
|
316
|
+
debug(result);
|
|
317
|
+
if (result.mode === 'local') {
|
|
318
|
+
result = await validateLocalPath(result, localPathValidator, componentType, defaultObject);
|
|
318
319
|
}
|
|
319
320
|
debug(result);
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
321
|
+
return result;
|
|
322
|
+
}
|
|
323
|
+
async function validateLocalPath(config, validator, componentName, defaultObject) {
|
|
324
|
+
while (config.mode === 'local') {
|
|
325
|
+
const resolvedPath = resolvePath(config.dir ?? '');
|
|
326
|
+
config.dir = resolvedPath;
|
|
323
327
|
const {
|
|
324
328
|
result: isPathValid,
|
|
325
329
|
message
|
|
326
|
-
} =
|
|
330
|
+
} = validator(resolvedPath);
|
|
327
331
|
if (isPathValid) {
|
|
328
332
|
break;
|
|
329
333
|
} else if (isStdinTTY) {
|
|
330
334
|
console.log(_chalk.default.yellow('Warning:'), message);
|
|
331
335
|
// eslint-disable-next-line no-await-in-loop
|
|
332
|
-
|
|
336
|
+
config = await promptForComponent(componentName, true, defaultObject);
|
|
333
337
|
} else {
|
|
334
338
|
throw new Error(message);
|
|
335
339
|
}
|
|
336
340
|
}
|
|
337
|
-
return
|
|
341
|
+
return config;
|
|
338
342
|
}
|
|
339
|
-
function
|
|
343
|
+
function validateMuPluginsLocalPath(providedPath) {
|
|
340
344
|
if (!isNonEmptyDirectory(providedPath)) {
|
|
341
345
|
const message = `Provided path "${providedPath}" does not point to a valid or existing directory.`;
|
|
342
346
|
return {
|
|
@@ -344,24 +348,35 @@ function validateLocalPath(component, providedPath) {
|
|
|
344
348
|
message
|
|
345
349
|
};
|
|
346
350
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
351
|
+
return {
|
|
352
|
+
result: true,
|
|
353
|
+
message: ''
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
function validateAppCodeLocalPath(providedPath) {
|
|
357
|
+
if (!isNonEmptyDirectory(providedPath)) {
|
|
358
|
+
const message = `Provided path "${providedPath}" does not point to a valid or existing directory.`;
|
|
359
|
+
return {
|
|
360
|
+
result: false,
|
|
361
|
+
message
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
const files = ['languages', 'plugins', 'themes', 'private', 'images', 'client-mu-plugins', 'vip-config'];
|
|
365
|
+
const missingFiles = [];
|
|
366
|
+
for (const file of files) {
|
|
367
|
+
const filePath = _path.default.resolve(providedPath, file);
|
|
368
|
+
if (!(0, _nodeFs.existsSync)(filePath)) {
|
|
369
|
+
missingFiles.push(file);
|
|
363
370
|
}
|
|
364
371
|
}
|
|
372
|
+
if (missingFiles.length > 0) {
|
|
373
|
+
// eslint-disable-next-line max-len
|
|
374
|
+
const message = `Provided path "${providedPath}" is missing following files/folders: ${missingFiles.join(', ')}. Learn more: https://docs.wpvip.com/wordpress-skeleton/`;
|
|
375
|
+
return {
|
|
376
|
+
result: false,
|
|
377
|
+
message
|
|
378
|
+
};
|
|
379
|
+
}
|
|
365
380
|
return {
|
|
366
381
|
result: true,
|
|
367
382
|
message: ''
|
|
@@ -399,6 +414,36 @@ async function promptForText(message, initial) {
|
|
|
399
414
|
}
|
|
400
415
|
return (result.input || '').trim();
|
|
401
416
|
}
|
|
417
|
+
async function promptForURL(message, initial) {
|
|
418
|
+
let result = {
|
|
419
|
+
input: initial
|
|
420
|
+
};
|
|
421
|
+
if (isStdinTTY) {
|
|
422
|
+
const URLValidator = value => {
|
|
423
|
+
if (!value.trim() || value === 'n') {
|
|
424
|
+
return true;
|
|
425
|
+
}
|
|
426
|
+
try {
|
|
427
|
+
const url = new URL(value);
|
|
428
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
|
429
|
+
return 'value must be a http:// or https:// URL';
|
|
430
|
+
}
|
|
431
|
+
return true;
|
|
432
|
+
} catch {
|
|
433
|
+
return 'value needs to be a valid URL or an empty string';
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
result = await (0, _enquirer.prompt)({
|
|
437
|
+
type: 'input',
|
|
438
|
+
name: 'input',
|
|
439
|
+
message,
|
|
440
|
+
initial,
|
|
441
|
+
validate: URLValidator
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
const retval = result.input.trim();
|
|
445
|
+
return retval === 'n' ? '' : retval;
|
|
446
|
+
}
|
|
402
447
|
const multisiteOptions = ['subdomain', 'subdirectory'];
|
|
403
448
|
async function promptForMultisite(message, initial) {
|
|
404
449
|
// `undefined` is used here only because our tests need overhauling
|
|
@@ -676,30 +721,92 @@ function getEnvTrackingInfo(slug) {
|
|
|
676
721
|
};
|
|
677
722
|
}
|
|
678
723
|
}
|
|
724
|
+
// Map of supported editors and their configurations
|
|
725
|
+
const SUPPORTED_EDITORS = {
|
|
726
|
+
vscode: {
|
|
727
|
+
displayName: 'VS Code',
|
|
728
|
+
candidates: ['code', 'code-insiders', 'codium'],
|
|
729
|
+
workspace: {
|
|
730
|
+
extension: 'code-workspace',
|
|
731
|
+
generator: _devEnvironmentCore.generateVSCodeWorkspace
|
|
732
|
+
}
|
|
733
|
+
},
|
|
734
|
+
cursor: {
|
|
735
|
+
displayName: 'Cursor',
|
|
736
|
+
candidates: ['cursor'],
|
|
737
|
+
workspace: {
|
|
738
|
+
extension: 'code-workspace',
|
|
739
|
+
generator: _devEnvironmentCore.generateVSCodeWorkspace
|
|
740
|
+
}
|
|
741
|
+
},
|
|
742
|
+
phpstorm: {
|
|
743
|
+
displayName: 'PHPStorm',
|
|
744
|
+
candidates: ['phpstorm', 'phpstorm64.exe'],
|
|
745
|
+
errors: {
|
|
746
|
+
notFound: `PHPStorm launcher was not detected in the expected path.\nPlease follow the setup instructions: https://www.jetbrains.com/help/phpstorm/working-with-the-ide-features-from-command-line.html#standalone`
|
|
747
|
+
},
|
|
748
|
+
workspace: {
|
|
749
|
+
extension: 'iml',
|
|
750
|
+
generator: _devEnvironmentCore.generatePHPStormWorkspace
|
|
751
|
+
}
|
|
752
|
+
},
|
|
753
|
+
windsurf: {
|
|
754
|
+
displayName: 'Windsurf',
|
|
755
|
+
candidates: ['windsurf'],
|
|
756
|
+
workspace: {
|
|
757
|
+
extension: 'code-workspace',
|
|
758
|
+
generator: _devEnvironmentCore.generateVSCodeWorkspace
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
};
|
|
762
|
+
|
|
763
|
+
// Helper to get workspace path for any editor
|
|
764
|
+
function getWorkspacePath(slug, editor) {
|
|
765
|
+
const {
|
|
766
|
+
workspace
|
|
767
|
+
} = SUPPORTED_EDITORS[editor];
|
|
768
|
+
if (editor === 'phpstorm') {
|
|
769
|
+
return _path.default.join((0, _devEnvironmentCore.getEnvironmentPath)(slug), '.idea', `${slug}.${workspace.extension}`);
|
|
770
|
+
}
|
|
771
|
+
return _path.default.join((0, _devEnvironmentCore.getEnvironmentPath)(slug), `${slug}.${workspace.extension}`);
|
|
772
|
+
}
|
|
679
773
|
function postStart(slug, options) {
|
|
680
|
-
|
|
681
|
-
|
|
774
|
+
let editorType;
|
|
775
|
+
if (options.editor) {
|
|
776
|
+
const editor = options.editor.toLowerCase();
|
|
777
|
+
if (!Object.keys(SUPPORTED_EDITORS).includes(editor)) {
|
|
778
|
+
throw new Error(`Invalid editor specified. Supported editors are: ${Object.keys(SUPPORTED_EDITORS).join(', ')}`);
|
|
779
|
+
}
|
|
780
|
+
editorType = editor;
|
|
781
|
+
} else if (options.vscode) {
|
|
782
|
+
editorType = 'vscode';
|
|
783
|
+
}
|
|
784
|
+
if (editorType) {
|
|
785
|
+
launchEditor(slug, editorType);
|
|
682
786
|
}
|
|
683
787
|
}
|
|
684
|
-
const
|
|
685
|
-
const
|
|
788
|
+
const launchEditor = (slug, type) => {
|
|
789
|
+
const editorConfig = SUPPORTED_EDITORS[type];
|
|
790
|
+
const workspacePath = getWorkspacePath(slug, type);
|
|
686
791
|
if ((0, _nodeFs.existsSync)(workspacePath)) {
|
|
687
|
-
console.log('
|
|
792
|
+
console.log('Project already exists, skipping creation.');
|
|
688
793
|
} else {
|
|
689
|
-
|
|
690
|
-
console.log(
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
794
|
+
editorConfig.workspace.generator(slug);
|
|
795
|
+
console.log(`${editorConfig.displayName} project generated`);
|
|
796
|
+
}
|
|
797
|
+
console.log(`Project file location:\n${workspacePath}\n`);
|
|
798
|
+
const executable = findExecutable(editorConfig.candidates);
|
|
799
|
+
if (executable) {
|
|
800
|
+
// For PHPStorm, pass the environment home folder instead of the workspace path
|
|
801
|
+
const launchPath = type === 'phpstorm' ? (0, _devEnvironmentCore.getEnvironmentPath)(slug) : workspacePath;
|
|
802
|
+
(0, _child_process.spawn)(executable, [launchPath], {
|
|
695
803
|
shell: process.platform === 'win32'
|
|
696
804
|
});
|
|
697
805
|
} else {
|
|
698
|
-
console.log(
|
|
806
|
+
console.log(editorConfig.errors?.notFound || `${editorConfig.displayName} was not detected in the expected path.`);
|
|
699
807
|
}
|
|
700
808
|
};
|
|
701
|
-
const
|
|
702
|
-
const candidates = ['code', 'code-insiders', 'codium'];
|
|
809
|
+
const findExecutable = candidates => {
|
|
703
810
|
for (const candidate of candidates) {
|
|
704
811
|
const result = (0, _shelljs.which)(candidate);
|
|
705
812
|
if (result) {
|
|
@@ -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,
|
|
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 =
|
|
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
|
-
|
|
553
|
-
|
|
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/
|
|
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
|
-
|
|
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.
|