@automattic/vip 2.27.0-dev2 → 2.27.0-dev4
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 +20 -0
- package/CONTRIBUTING.md +2 -0
- package/assets/dev-env.lando.template.yml.ejs +22 -5
- package/automattic-vip-2.27.0-dev4.tgz +0 -0
- 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-sync-sql.js +1 -0
- package/dist/bin/vip-dev-env-update.js +17 -6
- package/dist/bin/vip-dev-env.js +2 -2
- package/dist/commands/dev-env-import-sql.js +2 -2
- package/dist/commands/dev-env-sync-sql.js +14 -10
- package/dist/commands/export-sql.js +9 -7
- 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 +49 -79
- package/npm-shrinkwrap.json +1390 -99
- package/package.json +4 -1
- package/automattic-vip-2.27.0-dev2.tgz +0 -0
|
@@ -37,6 +37,7 @@ var _devEnvironment = require("../constants/dev-environment");
|
|
|
37
37
|
var _devEnvironmentCore = require("./dev-environment-core");
|
|
38
38
|
var _devEnvironmentLando = require("./dev-environment-lando");
|
|
39
39
|
var _userError = _interopRequireDefault(require("../user-error"));
|
|
40
|
+
var _devEnvironmentConfigurationFile = require("./dev-environment-configuration-file");
|
|
40
41
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
41
42
|
/**
|
|
42
43
|
*
|
|
@@ -128,13 +129,13 @@ const verifyDNSResolution = async slug => {
|
|
|
128
129
|
};
|
|
129
130
|
const VALIDATION_STEPS = [{
|
|
130
131
|
id: 'docker',
|
|
131
|
-
name: 'Check for
|
|
132
|
+
name: 'Check for Docker installation'
|
|
132
133
|
}, {
|
|
133
134
|
id: 'compose',
|
|
134
135
|
name: 'Check for docker-compose installation'
|
|
135
136
|
}, {
|
|
136
137
|
id: 'access',
|
|
137
|
-
name: 'Check
|
|
138
|
+
name: 'Check Docker connectivity'
|
|
138
139
|
}, {
|
|
139
140
|
id: 'dns',
|
|
140
141
|
name: 'Check DNS resolution'
|
|
@@ -181,7 +182,7 @@ const validateDependencies = async (lando, slug, quiet) => {
|
|
|
181
182
|
debug('Validation checks completed in %d ms', duration);
|
|
182
183
|
};
|
|
183
184
|
exports.validateDependencies = validateDependencies;
|
|
184
|
-
function getEnvironmentName(options) {
|
|
185
|
+
async function getEnvironmentName(options) {
|
|
185
186
|
if (options.slug) {
|
|
186
187
|
return options.slug;
|
|
187
188
|
}
|
|
@@ -194,6 +195,12 @@ function getEnvironmentName(options) {
|
|
|
194
195
|
const message = `This command does not support @app.env notation. Use '--slug=${appName}' to target the local environment.`;
|
|
195
196
|
throw new _userError.default(message);
|
|
196
197
|
}
|
|
198
|
+
const configurationFileOptions = await (0, _devEnvironmentConfigurationFile.getConfigurationFileOptions)();
|
|
199
|
+
if (configurationFileOptions.slug) {
|
|
200
|
+
const slug = configurationFileOptions.slug;
|
|
201
|
+
console.log(`Using environment ${_chalk.default.blue.bold(slug)} from ${_chalk.default.gray(_devEnvironmentConfigurationFile.CONFIGURATION_FILE_NAME)}\n`);
|
|
202
|
+
return slug;
|
|
203
|
+
}
|
|
197
204
|
const envs = (0, _devEnvironmentCore.getAllEnvironmentNames)();
|
|
198
205
|
if (envs.length === 1) {
|
|
199
206
|
return envs[0];
|
|
@@ -205,8 +212,9 @@ function getEnvironmentName(options) {
|
|
|
205
212
|
return DEFAULT_SLUG; // Fall back to the default slug if we don't have any, e.g. during the env creation purpose
|
|
206
213
|
}
|
|
207
214
|
|
|
208
|
-
function getEnvironmentStartCommand(slug) {
|
|
209
|
-
|
|
215
|
+
function getEnvironmentStartCommand(slug, configurationFileOptions) {
|
|
216
|
+
const isUsingConfigurationFileSlug = Object.keys(configurationFileOptions).length > 0 && configurationFileOptions.slug === slug;
|
|
217
|
+
if (!slug || isUsingConfigurationFileSlug) {
|
|
210
218
|
return `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} start`;
|
|
211
219
|
}
|
|
212
220
|
return `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} start --slug ${slug}`;
|
|
@@ -272,7 +280,7 @@ async function promptForArguments(preselectedOptions, defaultOptions, suppressPr
|
|
|
272
280
|
}
|
|
273
281
|
const instanceData = {
|
|
274
282
|
wpTitle: preselectedOptions.title || (await promptForText('WordPress site title', defaultOptions.title || _devEnvironment.DEV_ENVIRONMENT_DEFAULTS.title)),
|
|
275
|
-
multisite:
|
|
283
|
+
multisite: preselectedOptions.multisite !== undefined ? preselectedOptions.multisite : await promptForBoolean(multisiteText, !!multisiteDefault),
|
|
276
284
|
elasticsearch: false,
|
|
277
285
|
php: preselectedOptions.php ? resolvePhpVersion(preselectedOptions.php) : await promptForPhpVersion(resolvePhpVersion(defaultOptions.php || _devEnvironment.DEV_ENVIRONMENT_DEFAULTS.phpVersion)),
|
|
278
286
|
mariadb: preselectedOptions.mariadb || defaultOptions.mariadb,
|
|
@@ -312,7 +320,7 @@ async function promptForArguments(preselectedOptions, defaultOptions, suppressPr
|
|
|
312
320
|
const defaultValue = ((_defaultOptions$compo = defaultOptions[component]) !== null && _defaultOptions$compo !== void 0 ? _defaultOptions$compo : '').toString();
|
|
313
321
|
|
|
314
322
|
// eslint-disable-next-line no-await-in-loop
|
|
315
|
-
const result = await processComponent(component, option, defaultValue);
|
|
323
|
+
const result = await processComponent(component, option, defaultValue, suppressPrompts);
|
|
316
324
|
if (null === result) {
|
|
317
325
|
throw new Error('processComponent() returned null');
|
|
318
326
|
}
|
|
@@ -337,14 +345,14 @@ async function promptForArguments(preselectedOptions, defaultOptions, suppressPr
|
|
|
337
345
|
debug('Instance data after prompts', instanceData);
|
|
338
346
|
return instanceData;
|
|
339
347
|
}
|
|
340
|
-
async function processComponent(component, preselectedValue, defaultValue) {
|
|
348
|
+
async function processComponent(component, preselectedValue, defaultValue, suppressPrompts = false) {
|
|
341
349
|
debug(`processing a component '${component}', with preselected/default - ${preselectedValue}/${defaultValue}`);
|
|
342
350
|
let result = null;
|
|
343
351
|
const allowLocal = component !== 'wordpress';
|
|
344
352
|
const defaultObject = defaultValue ? processComponentOptionInput(defaultValue, allowLocal) : null;
|
|
345
353
|
if (preselectedValue) {
|
|
346
354
|
result = processComponentOptionInput(preselectedValue, allowLocal);
|
|
347
|
-
if (allowLocal) {
|
|
355
|
+
if (allowLocal && suppressPrompts === false) {
|
|
348
356
|
console.log(`${_chalk.default.green('✓')} Path to your local ${componentDisplayNames[component]}: ${preselectedValue}`);
|
|
349
357
|
}
|
|
350
358
|
} else {
|
|
@@ -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
|
*/
|
|
@@ -124,7 +125,6 @@ async function landoRecovery(lando, instancePath, error) {
|
|
|
124
125
|
console.error(_chalk.default.green('Recovery successful, trying to initialize again...'));
|
|
125
126
|
try {
|
|
126
127
|
const app = lando.getApp(instancePath);
|
|
127
|
-
addHooks(app, lando);
|
|
128
128
|
await app.init();
|
|
129
129
|
return app;
|
|
130
130
|
} catch (initError) {
|
|
@@ -142,7 +142,6 @@ async function getLandoApplication(lando, instancePath) {
|
|
|
142
142
|
let app;
|
|
143
143
|
try {
|
|
144
144
|
app = lando.getApp(instancePath);
|
|
145
|
-
await addHooks(app, lando);
|
|
146
145
|
await app.init();
|
|
147
146
|
} catch (error) {
|
|
148
147
|
app = await landoRecovery(lando, instancePath, error);
|
|
@@ -152,24 +151,8 @@ async function getLandoApplication(lando, instancePath) {
|
|
|
152
151
|
}
|
|
153
152
|
async function bootstrapLando() {
|
|
154
153
|
const lando = new _lando.default(await getLandoConfig());
|
|
155
|
-
await lando.bootstrap();
|
|
156
|
-
return lando;
|
|
157
|
-
}
|
|
158
|
-
async function landoStart(lando, instancePath) {
|
|
159
|
-
debug('Will start lando app on path:', instancePath);
|
|
160
|
-
const app = await getLandoApplication(lando, instancePath);
|
|
161
|
-
await app.start();
|
|
162
|
-
}
|
|
163
|
-
async function landoRebuild(lando, instancePath) {
|
|
164
|
-
debug('Will rebuild lando app on path:', instancePath);
|
|
165
|
-
const app = await getLandoApplication(lando, instancePath);
|
|
166
|
-
await ensureNoOrphantProxyContainer(lando);
|
|
167
|
-
await app.rebuild();
|
|
168
|
-
}
|
|
169
|
-
async function addHooks(app, lando) {
|
|
170
|
-
app.events.on('post-start', 1, () => healthcheckHook(app, lando));
|
|
171
154
|
lando.events.once('pre-engine-build', async data => {
|
|
172
|
-
const instanceData = (0, _devEnvironmentCore.readEnvironmentData)(
|
|
155
|
+
const instanceData = (0, _devEnvironmentCore.readEnvironmentData)(data.name);
|
|
173
156
|
let registryResolvable = false;
|
|
174
157
|
try {
|
|
175
158
|
registryResolvable = (await _dns.default.promises.lookup('ghcr.io')).address || false;
|
|
@@ -178,70 +161,42 @@ async function addHooks(app, lando) {
|
|
|
178
161
|
debug('Registry ghcr.io is not resolvable, image pull might be broken.');
|
|
179
162
|
registryResolvable = false;
|
|
180
163
|
}
|
|
181
|
-
|
|
182
|
-
if (Array.isArray(data.opts.pullable) && Array.isArray(data.opts.local) && data.opts.local.length === 0 && !
|
|
164
|
+
const pull = registryResolvable && (instanceData.pullAfter || 0) < Date.now();
|
|
165
|
+
if (Array.isArray(data.opts.pullable) && Array.isArray(data.opts.local) && data.opts.local.length === 0 && !pull) {
|
|
166
|
+
// Setting `data.opts.pullable` to an empty array prevents Lando from pulling images with `docker pull`.
|
|
167
|
+
// Note that if some of the images are not available, they will still be pulled by `docker-compose`.
|
|
183
168
|
data.opts.local = data.opts.pullable;
|
|
184
169
|
data.opts.pullable = [];
|
|
185
170
|
}
|
|
186
|
-
if (
|
|
171
|
+
if (pull || !instanceData.pullAfter) {
|
|
187
172
|
instanceData.pullAfter = Date.now() + 7 * 24 * 60 * 60 * 1000;
|
|
188
|
-
(0, _devEnvironmentCore.writeEnvironmentData)(
|
|
173
|
+
(0, _devEnvironmentCore.writeEnvironmentData)(data.name, instanceData);
|
|
189
174
|
}
|
|
190
175
|
});
|
|
176
|
+
await lando.bootstrap();
|
|
177
|
+
return lando;
|
|
191
178
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
}
|
|
197
|
-
async function
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
cmd: healthChecks[container.service],
|
|
214
|
-
compose: app.compose,
|
|
215
|
-
project: app.project,
|
|
216
|
-
opts: {
|
|
217
|
-
silent: true,
|
|
218
|
-
noTTY: true,
|
|
219
|
-
cstdio: 'pipe',
|
|
220
|
-
services: [container.service]
|
|
221
|
-
}
|
|
222
|
-
}));
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
const results = await Promise.allSettled(checkPromises);
|
|
226
|
-
results.forEach((result, index) => {
|
|
227
|
-
if (result.status === 'rejected') {
|
|
228
|
-
debug(`${containerOrder[index].service} Health check failed`);
|
|
229
|
-
notHealthyContainers.push(containerOrder[index]);
|
|
230
|
-
}
|
|
231
|
-
});
|
|
232
|
-
if (notHealthyContainers.length) {
|
|
233
|
-
notHealthyContainers.forEach(container => console.log(`Waiting for service ${container.service} ...`));
|
|
234
|
-
return Promise.reject(notHealthyContainers);
|
|
235
|
-
}
|
|
236
|
-
}, {
|
|
237
|
-
max: 20,
|
|
238
|
-
backoff: 1000
|
|
239
|
-
});
|
|
240
|
-
} catch (containersWithFailingHealthCheck) {
|
|
241
|
-
containersWithFailingHealthCheck.forEach(container => console.log(_chalk.default.yellow('WARNING:') + ` Service ${container.service} failed healthcheck`));
|
|
242
|
-
}
|
|
243
|
-
const duration = new Date().getTime() - now.getTime();
|
|
244
|
-
debug(`Healthcheck completed in ${duration}ms`);
|
|
179
|
+
async function landoStart(lando, instancePath) {
|
|
180
|
+
debug('Will start lando app on path:', instancePath);
|
|
181
|
+
const app = await getLandoApplication(lando, instancePath);
|
|
182
|
+
await app.start();
|
|
183
|
+
}
|
|
184
|
+
async function landoLogs(lando, instancePath, options) {
|
|
185
|
+
debug('Will show lando logs on path:', instancePath, ' with options: ', options);
|
|
186
|
+
const app = await getLandoApplication(lando, instancePath);
|
|
187
|
+
const logTask = lando.tasks.find(task => task.command === 'logs');
|
|
188
|
+
await logTask.run({
|
|
189
|
+
follow: options.follow,
|
|
190
|
+
service: options.service,
|
|
191
|
+
timestamps: options.timestamps,
|
|
192
|
+
_app: app
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
async function landoRebuild(lando, instancePath) {
|
|
196
|
+
debug('Will rebuild lando app on path:', instancePath);
|
|
197
|
+
const app = await getLandoApplication(lando, instancePath);
|
|
198
|
+
await ensureNoOrphantProxyContainer(lando);
|
|
199
|
+
await app.rebuild();
|
|
245
200
|
}
|
|
246
201
|
async function landoStop(lando, instancePath) {
|
|
247
202
|
debug('Will stop lando app on path:', instancePath);
|
|
@@ -404,6 +359,21 @@ async function landoExec(lando, instancePath, toolName, args, options) {
|
|
|
404
359
|
process.argv = savedArgv;
|
|
405
360
|
}
|
|
406
361
|
}
|
|
362
|
+
async function landoShell(lando, instancePath, service, user, command) {
|
|
363
|
+
const app = await getLandoApplication(lando, instancePath);
|
|
364
|
+
const shellTask = lando.tasks.find(task => task.command === 'ssh');
|
|
365
|
+
if (!command.length) {
|
|
366
|
+
const interactive = process.stdin.isTTY ? '-i' : '';
|
|
367
|
+
command = ['/bin/sh', '-c', `if [ -x /bin/bash ]; then /bin/bash ${interactive}; else /bin/sh ${interactive}; fi; exit 0`];
|
|
368
|
+
}
|
|
369
|
+
debug('Running command "%o" in service "%s" as user "%s"', command, service, user);
|
|
370
|
+
await shellTask.run({
|
|
371
|
+
command,
|
|
372
|
+
service,
|
|
373
|
+
user,
|
|
374
|
+
_app: app
|
|
375
|
+
});
|
|
376
|
+
}
|
|
407
377
|
|
|
408
378
|
/**
|
|
409
379
|
* Sometimes the proxy network seems to disapper leaving only orphant stopped proxy container.
|
|
@@ -445,10 +415,10 @@ async function validateDockerInstalled(lando) {
|
|
|
445
415
|
}
|
|
446
416
|
async function validateDockerAccess(lando) {
|
|
447
417
|
const docker = lando.engine.docker;
|
|
448
|
-
lando.log.verbose('Fetching docker info to verify
|
|
418
|
+
lando.log.verbose('Fetching docker info to verify Docker connection');
|
|
449
419
|
try {
|
|
450
420
|
await docker.info();
|
|
451
421
|
} catch (error) {
|
|
452
|
-
throw Error('Failed to connect to
|
|
422
|
+
throw Error('Failed to connect to Docker. Please verify that Docker engine (service) is running and follow the troubleshooting instructions for your platform.');
|
|
453
423
|
}
|
|
454
424
|
}
|