@automattic/vip 2.27.0-dev3 → 2.27.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/CHANGELOG.md +14 -0
- package/CONTRIBUTING.md +2 -0
- package/assets/dev-env.lando.template.yml.ejs +22 -5
- package/dist/bin/vip-dev-env-create.js +15 -4
- package/dist/bin/vip-dev-env-destroy.js +1 -1
- package/dist/bin/vip-dev-env-exec.js +1 -1
- package/dist/bin/vip-dev-env-import-media.js +1 -1
- package/dist/bin/vip-dev-env-import-sql.js +2 -1
- package/dist/bin/vip-dev-env-info.js +1 -1
- package/dist/bin/vip-dev-env-logs.js +55 -0
- package/dist/bin/vip-dev-env-shell.js +83 -0
- package/dist/bin/vip-dev-env-start.js +1 -1
- package/dist/bin/vip-dev-env-stop.js +1 -1
- package/dist/bin/vip-dev-env-update.js +17 -6
- package/dist/bin/vip-dev-env.js +1 -1
- package/dist/commands/dev-env-import-sql.js +2 -2
- package/dist/commands/dev-env-sync-sql.js +2 -6
- package/dist/lib/dev-environment/dev-environment-cli.js +17 -9
- package/dist/lib/dev-environment/dev-environment-configuration-file.js +165 -0
- package/dist/lib/dev-environment/dev-environment-core.js +13 -0
- package/dist/lib/dev-environment/dev-environment-lando.js +35 -6
- package/npm-shrinkwrap.json +1388 -99
- package/package.json +4 -3
- package/automattic-vip-2.27.0-dev3.tgz +0 -0
- package/dist/bin/vip-dev-env-sync-sql.js +0 -59
- package/dist/bin/vip-dev-env-sync.js +0 -21
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.CONFIGURATION_FILE_NAME = void 0;
|
|
7
|
+
exports.getConfigurationFileOptions = getConfigurationFileOptions;
|
|
8
|
+
exports.mergeConfigurationFileOptions = mergeConfigurationFileOptions;
|
|
9
|
+
exports.printConfigurationFile = printConfigurationFile;
|
|
10
|
+
var _debug = _interopRequireDefault(require("debug"));
|
|
11
|
+
var _fs = _interopRequireDefault(require("fs"));
|
|
12
|
+
var _path = _interopRequireDefault(require("path"));
|
|
13
|
+
var _chalk = _interopRequireDefault(require("chalk"));
|
|
14
|
+
var _jsYaml = _interopRequireWildcard(require("js-yaml"));
|
|
15
|
+
var exit = _interopRequireWildcard(require("../cli/exit"));
|
|
16
|
+
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
17
|
+
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
18
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
19
|
+
/**
|
|
20
|
+
*
|
|
21
|
+
* @format
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* External dependencies
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Internal dependencies
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
const debug = (0, _debug.default)('@automattic/vip:bin:dev-environment');
|
|
33
|
+
const CONFIGURATION_FILE_NAME = '.vip-dev-env.yml';
|
|
34
|
+
exports.CONFIGURATION_FILE_NAME = CONFIGURATION_FILE_NAME;
|
|
35
|
+
async function getConfigurationFileOptions() {
|
|
36
|
+
const configurationFilePath = _path.default.join(process.cwd(), CONFIGURATION_FILE_NAME);
|
|
37
|
+
let configurationFileContents = '';
|
|
38
|
+
const fileExists = await _fs.default.promises.access(configurationFilePath, _fs.default.R_OK).then(() => true).catch(() => false);
|
|
39
|
+
if (fileExists) {
|
|
40
|
+
debug('Reading configuration file from:', configurationFilePath);
|
|
41
|
+
configurationFileContents = await _fs.default.promises.readFile(configurationFilePath, 'utf8');
|
|
42
|
+
} else {
|
|
43
|
+
return {};
|
|
44
|
+
}
|
|
45
|
+
let configurationFromFile = {};
|
|
46
|
+
try {
|
|
47
|
+
configurationFromFile = _jsYaml.default.load(configurationFileContents, {
|
|
48
|
+
// Only allow strings, arrays, and objects to be parsed from configuration file
|
|
49
|
+
// This causes number-looking values like `php: 8.1` to be parsed directly into strings
|
|
50
|
+
schema: _jsYaml.FAILSAFE_SCHEMA
|
|
51
|
+
});
|
|
52
|
+
} catch (err) {
|
|
53
|
+
const messageToShow = `Configuration file ${_chalk.default.grey(CONFIGURATION_FILE_NAME)} could not be loaded:\n` + err.toString();
|
|
54
|
+
exit.withError(messageToShow);
|
|
55
|
+
}
|
|
56
|
+
const configuration = await sanitizeConfiguration(configurationFromFile).catch(async ({
|
|
57
|
+
message
|
|
58
|
+
}) => {
|
|
59
|
+
exit.withError(message);
|
|
60
|
+
return {};
|
|
61
|
+
});
|
|
62
|
+
debug('Sanitized configuration from file:', configuration);
|
|
63
|
+
return configuration;
|
|
64
|
+
}
|
|
65
|
+
async function sanitizeConfiguration(configuration) {
|
|
66
|
+
const genericConfigurationError = `Configuration file ${_chalk.default.grey(CONFIGURATION_FILE_NAME)} is available but ` + `couldn't be loaded. Ensure there is a ${_chalk.default.cyan('configuration-version')} and ${_chalk.default.cyan('slug')} ` + `configured. For example:\n\n${_chalk.default.grey(getConfigurationFileExample())}`;
|
|
67
|
+
if (Array.isArray(configuration) || typeof configuration !== 'object') {
|
|
68
|
+
throw new Error(genericConfigurationError);
|
|
69
|
+
} else if (configuration['configuration-version'] === undefined || configuration.slug === undefined) {
|
|
70
|
+
throw new Error(genericConfigurationError);
|
|
71
|
+
}
|
|
72
|
+
const validVersions = getAllConfigurationFileVersions().map(version => _chalk.default.cyan(version)).join(', ');
|
|
73
|
+
if (!isValidConfigurationFileVersion(configuration['configuration-version'])) {
|
|
74
|
+
throw new Error(`Configuration file ${_chalk.default.grey(CONFIGURATION_FILE_NAME)} has an invalid ` + `${_chalk.default.cyan('configuration-version')} key. Update to a supported version. For example:\n\n` + _chalk.default.grey(getConfigurationFileExample()) + `\nSupported configuration versions: ${validVersions}.\n`);
|
|
75
|
+
}
|
|
76
|
+
const stringToBooleanIfDefined = value => {
|
|
77
|
+
if (value === undefined || !['true', 'false'].includes(value)) {
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
return value === 'true';
|
|
81
|
+
};
|
|
82
|
+
const sanitizedConfiguration = {
|
|
83
|
+
'configuration-version': configuration['configuration-version'],
|
|
84
|
+
slug: configuration.slug,
|
|
85
|
+
title: configuration.title,
|
|
86
|
+
multisite: stringToBooleanIfDefined(configuration.multisite),
|
|
87
|
+
php: configuration.php,
|
|
88
|
+
wordpress: configuration.wordpress,
|
|
89
|
+
'mu-plugins': configuration['mu-plugins'],
|
|
90
|
+
'app-code': configuration['app-code'],
|
|
91
|
+
elasticsearch: stringToBooleanIfDefined(configuration.elasticsearch),
|
|
92
|
+
phpmyadmin: stringToBooleanIfDefined(configuration.phpmyadmin),
|
|
93
|
+
xdebug: stringToBooleanIfDefined(configuration.xdebug),
|
|
94
|
+
mailhog: stringToBooleanIfDefined(configuration.mailhog)
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// Remove undefined values
|
|
98
|
+
Object.keys(sanitizedConfiguration).forEach(key => sanitizedConfiguration[key] === undefined && delete sanitizedConfiguration[key]);
|
|
99
|
+
return sanitizedConfiguration;
|
|
100
|
+
}
|
|
101
|
+
function mergeConfigurationFileOptions(preselectedOptions, configurationFileOptions) {
|
|
102
|
+
// configurationFileOptions holds different parameters than present in
|
|
103
|
+
// preselectedOptions like "slug", and friendly-named parameters (e.g.
|
|
104
|
+
// 'app-code' vs 'appCode'). Selectively merge configurationFileOptions
|
|
105
|
+
// parameters into preselectedOptions.
|
|
106
|
+
const configurationFileInstanceOptions = {
|
|
107
|
+
title: configurationFileOptions.title,
|
|
108
|
+
multisite: configurationFileOptions.multisite,
|
|
109
|
+
php: configurationFileOptions.php,
|
|
110
|
+
wordpress: configurationFileOptions.wordpress,
|
|
111
|
+
muPlugins: configurationFileOptions['mu-plugins'],
|
|
112
|
+
appCode: configurationFileOptions['app-code'],
|
|
113
|
+
elasticsearch: configurationFileOptions.elasticsearch,
|
|
114
|
+
phpmyadmin: configurationFileOptions.phpmyadmin,
|
|
115
|
+
xdebug: configurationFileOptions.xdebug,
|
|
116
|
+
mailhog: configurationFileOptions.mailhog
|
|
117
|
+
};
|
|
118
|
+
const mergedOptions = {};
|
|
119
|
+
Object.keys(configurationFileInstanceOptions).forEach(key => {
|
|
120
|
+
// preselectedOptions (supplied from command-line) override configurationFileOptions
|
|
121
|
+
if (preselectedOptions[key] !== undefined) {
|
|
122
|
+
mergedOptions[key] = preselectedOptions[key];
|
|
123
|
+
} else if (configurationFileInstanceOptions[key] !== undefined) {
|
|
124
|
+
mergedOptions[key] = configurationFileInstanceOptions[key];
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
return mergedOptions;
|
|
128
|
+
}
|
|
129
|
+
function printConfigurationFile(configurationOptions) {
|
|
130
|
+
const isConfigurationFileEmpty = Object.keys(configurationOptions).length === 0;
|
|
131
|
+
if (isConfigurationFileEmpty) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Customized formatter because Lando's printTable() automatically uppercases keys
|
|
136
|
+
// which may be confusing for YAML configuration
|
|
137
|
+
const settingLines = [];
|
|
138
|
+
for (const [key, value] of Object.entries(configurationOptions)) {
|
|
139
|
+
settingLines.push(`${_chalk.default.cyan(key)}: ${String(value)}`);
|
|
140
|
+
}
|
|
141
|
+
console.log(settingLines.join('\n') + '\n');
|
|
142
|
+
}
|
|
143
|
+
const CONFIGURATION_FILE_VERSIONS = ['0.preview-unstable'];
|
|
144
|
+
function getAllConfigurationFileVersions() {
|
|
145
|
+
return CONFIGURATION_FILE_VERSIONS;
|
|
146
|
+
}
|
|
147
|
+
function getLatestConfigurationFileVersion() {
|
|
148
|
+
return CONFIGURATION_FILE_VERSIONS[CONFIGURATION_FILE_VERSIONS.length - 1];
|
|
149
|
+
}
|
|
150
|
+
function isValidConfigurationFileVersion(version) {
|
|
151
|
+
return CONFIGURATION_FILE_VERSIONS.includes(version);
|
|
152
|
+
}
|
|
153
|
+
function getConfigurationFileExample() {
|
|
154
|
+
return `configuration-version: ${getLatestConfigurationFileVersion()}
|
|
155
|
+
slug: dev-site
|
|
156
|
+
php: 8.0
|
|
157
|
+
wordpress: 6.0
|
|
158
|
+
app-code: ./site-code
|
|
159
|
+
mu-plugins: image
|
|
160
|
+
multisite: false
|
|
161
|
+
phpmyadmin: true
|
|
162
|
+
elasticsearch: true
|
|
163
|
+
xdebug: true
|
|
164
|
+
`;
|
|
165
|
+
}
|
|
@@ -17,6 +17,7 @@ exports.printAllEnvironmentsInfo = printAllEnvironmentsInfo;
|
|
|
17
17
|
exports.printEnvironmentInfo = printEnvironmentInfo;
|
|
18
18
|
exports.readEnvironmentData = readEnvironmentData;
|
|
19
19
|
exports.resolveImportPath = resolveImportPath;
|
|
20
|
+
exports.showLogs = showLogs;
|
|
20
21
|
exports.startEnvironment = startEnvironment;
|
|
21
22
|
exports.stopEnvironment = stopEnvironment;
|
|
22
23
|
exports.updateEnvironment = updateEnvironment;
|
|
@@ -200,6 +201,18 @@ function parseComponentForInfo(component) {
|
|
|
200
201
|
}
|
|
201
202
|
return component.tag || '[demo-image]';
|
|
202
203
|
}
|
|
204
|
+
async function showLogs(lando, slug, options = {}) {
|
|
205
|
+
debug('Will display logs command on env', slug, 'with options', options);
|
|
206
|
+
const instancePath = getEnvironmentPath(slug);
|
|
207
|
+
debug('Instance path for', slug, 'is:', instancePath);
|
|
208
|
+
if (options.service) {
|
|
209
|
+
const appInfo = await (0, _devEnvironmentLando.landoInfo)(lando, instancePath);
|
|
210
|
+
if (!appInfo.services.includes(options.service)) {
|
|
211
|
+
throw new _userError.default(`Service '${options.service}' not found. Please choose from one: ${appInfo.services}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return (0, _devEnvironmentLando.landoLogs)(lando, instancePath, options);
|
|
215
|
+
}
|
|
203
216
|
async function printEnvironmentInfo(lando, slug, options) {
|
|
204
217
|
debug('Will get info for an environment', slug);
|
|
205
218
|
const instancePath = getEnvironmentPath(slug);
|
|
@@ -9,7 +9,9 @@ exports.isEnvUp = isEnvUp;
|
|
|
9
9
|
exports.landoDestroy = landoDestroy;
|
|
10
10
|
exports.landoExec = landoExec;
|
|
11
11
|
exports.landoInfo = landoInfo;
|
|
12
|
+
exports.landoLogs = landoLogs;
|
|
12
13
|
exports.landoRebuild = landoRebuild;
|
|
14
|
+
exports.landoShell = landoShell;
|
|
13
15
|
exports.landoStart = landoStart;
|
|
14
16
|
exports.landoStop = landoStop;
|
|
15
17
|
exports.validateDockerAccess = validateDockerAccess;
|
|
@@ -42,7 +44,6 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
42
44
|
*/
|
|
43
45
|
const DEBUG_KEY = '@automattic/vip:bin:dev-environment';
|
|
44
46
|
const debug = (0, _debug.default)(DEBUG_KEY);
|
|
45
|
-
|
|
46
47
|
/**
|
|
47
48
|
* @return {Promise<object>} Lando configuration
|
|
48
49
|
*/
|
|
@@ -160,6 +161,17 @@ async function landoStart(lando, instancePath) {
|
|
|
160
161
|
const app = await getLandoApplication(lando, instancePath);
|
|
161
162
|
await app.start();
|
|
162
163
|
}
|
|
164
|
+
async function landoLogs(lando, instancePath, options) {
|
|
165
|
+
debug('Will show lando logs on path:', instancePath, ' with options: ', options);
|
|
166
|
+
const app = await getLandoApplication(lando, instancePath);
|
|
167
|
+
const logTask = lando.tasks.find(task => task.command === 'logs');
|
|
168
|
+
await logTask.run({
|
|
169
|
+
follow: options.follow,
|
|
170
|
+
service: options.service,
|
|
171
|
+
timestamps: options.timestamps,
|
|
172
|
+
_app: app
|
|
173
|
+
});
|
|
174
|
+
}
|
|
163
175
|
async function landoRebuild(lando, instancePath) {
|
|
164
176
|
debug('Will rebuild lando app on path:', instancePath);
|
|
165
177
|
const app = await getLandoApplication(lando, instancePath);
|
|
@@ -178,12 +190,14 @@ async function addHooks(app, lando) {
|
|
|
178
190
|
debug('Registry ghcr.io is not resolvable, image pull might be broken.');
|
|
179
191
|
registryResolvable = false;
|
|
180
192
|
}
|
|
181
|
-
|
|
182
|
-
if (Array.isArray(data.opts.pullable) && Array.isArray(data.opts.local) && data.opts.local.length === 0 && !
|
|
193
|
+
const pull = registryResolvable && (instanceData.pullAfter || 0) < Date.now();
|
|
194
|
+
if (Array.isArray(data.opts.pullable) && Array.isArray(data.opts.local) && data.opts.local.length === 0 && !pull) {
|
|
195
|
+
// Settigs `data.opts.pullable` to an empty array prevents Lando from pulling images with `docker pull`.
|
|
196
|
+
// Note that if some of the images are not available, they will still be pulled by `docker-compose`.
|
|
183
197
|
data.opts.local = data.opts.pullable;
|
|
184
198
|
data.opts.pullable = [];
|
|
185
199
|
}
|
|
186
|
-
if (
|
|
200
|
+
if (pull || !instanceData.pullAfter) {
|
|
187
201
|
instanceData.pullAfter = Date.now() + 7 * 24 * 60 * 60 * 1000;
|
|
188
202
|
(0, _devEnvironmentCore.writeEnvironmentData)(app._name, instanceData);
|
|
189
203
|
}
|
|
@@ -404,6 +418,21 @@ async function landoExec(lando, instancePath, toolName, args, options) {
|
|
|
404
418
|
process.argv = savedArgv;
|
|
405
419
|
}
|
|
406
420
|
}
|
|
421
|
+
async function landoShell(lando, instancePath, service, user, command) {
|
|
422
|
+
const app = await getLandoApplication(lando, instancePath);
|
|
423
|
+
const shellTask = lando.tasks.find(task => task.command === 'ssh');
|
|
424
|
+
if (!command.length) {
|
|
425
|
+
const interactive = process.stdin.isTTY ? '-i' : '';
|
|
426
|
+
command = ['/bin/sh', '-c', `if [ -x /bin/bash ]; then /bin/bash ${interactive}; else /bin/sh ${interactive}; fi; exit 0`];
|
|
427
|
+
}
|
|
428
|
+
debug('Running command "%o" in service "%s" as user "%s"', command, service, user);
|
|
429
|
+
await shellTask.run({
|
|
430
|
+
command,
|
|
431
|
+
service,
|
|
432
|
+
user,
|
|
433
|
+
_app: app
|
|
434
|
+
});
|
|
435
|
+
}
|
|
407
436
|
|
|
408
437
|
/**
|
|
409
438
|
* Sometimes the proxy network seems to disapper leaving only orphant stopped proxy container.
|
|
@@ -445,10 +474,10 @@ async function validateDockerInstalled(lando) {
|
|
|
445
474
|
}
|
|
446
475
|
async function validateDockerAccess(lando) {
|
|
447
476
|
const docker = lando.engine.docker;
|
|
448
|
-
lando.log.verbose('Fetching docker info to verify
|
|
477
|
+
lando.log.verbose('Fetching docker info to verify Docker connection');
|
|
449
478
|
try {
|
|
450
479
|
await docker.info();
|
|
451
480
|
} catch (error) {
|
|
452
|
-
throw Error('Failed to connect to
|
|
481
|
+
throw Error('Failed to connect to Docker. Please verify that Docker engine (service) is running and follow the troubleshooting instructions for your platform.');
|
|
453
482
|
}
|
|
454
483
|
}
|