@automattic/vip 2.33.0-dev1 → 2.33.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/dist/bin/vip-backup.js +1 -1
- package/dist/bin/vip-dev-env-start.js +0 -19
- package/dist/bin/vip-dev-env-sync-sql.js +4 -1
- package/dist/commands/backup-db.js +1 -1
- package/dist/commands/dev-env-sync-sql.js +9 -4
- package/dist/commands/export-sql.js +23 -1
- package/dist/lib/backup-storage-availability/backup-storage-availability.js +123 -0
- package/dist/lib/backup-storage-availability/docker-machine-not-found-error.js +12 -0
- package/dist/lib/cli/format.js +19 -5
- package/dist/lib/cli/progress.js +58 -9
- package/dist/lib/dev-environment/dev-environment-core.js +63 -59
- package/dist/lib/dev-environment/dev-environment-lando.js +7 -2
- package/npm-shrinkwrap.json +422 -436
- package/package.json +8 -6
package/dist/bin/vip-backup.js
CHANGED
|
@@ -14,6 +14,6 @@ var _tracker = require("../lib/tracker");
|
|
|
14
14
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
15
15
|
void (0, _command.default)({
|
|
16
16
|
usage: 'vip backup'
|
|
17
|
-
}).command('db', 'Trigger a new backup for your database').example('vip backup
|
|
17
|
+
}).command('db', 'Trigger a new backup for your database').example('vip backup db @mysite.develop', 'Trigger a new backup for your database of the @mysite.develop environment').argv(process.argv, async () => {
|
|
18
18
|
await (0, _tracker.trackEvent)('vip_backup_command_execute');
|
|
19
19
|
});
|
|
@@ -10,9 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
"use strict";
|
|
12
12
|
|
|
13
|
-
var _chalk = _interopRequireDefault(require("chalk"));
|
|
14
13
|
var _debug = _interopRequireDefault(require("debug"));
|
|
15
|
-
var _child_process = require("child_process");
|
|
16
14
|
var _tracker = require("../lib/tracker");
|
|
17
15
|
var _command = _interopRequireDefault(require("../lib/cli/command"));
|
|
18
16
|
var _devEnvironmentCore = require("../lib/dev-environment/dev-environment-core");
|
|
@@ -24,9 +22,6 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
24
22
|
* Internal dependencies
|
|
25
23
|
*/
|
|
26
24
|
const debug = (0, _debug.default)('@automattic/vip:bin:dev-environment');
|
|
27
|
-
|
|
28
|
-
// PowerShell command for Windows Docker patch
|
|
29
|
-
const dockerWindowsPathCmd = 'wsl -d docker-desktop bash -c "sysctl -w vm.max_map_count=262144"';
|
|
30
25
|
const examples = [{
|
|
31
26
|
usage: `${_devEnvironment.DEV_ENVIRONMENT_FULL_COMMAND} start`,
|
|
32
27
|
description: 'Starts a local dev environment'
|
|
@@ -48,20 +43,6 @@ const examples = [{
|
|
|
48
43
|
skipWpVersionsCheck: !!opt.skipWpVersionsCheck
|
|
49
44
|
};
|
|
50
45
|
try {
|
|
51
|
-
if (process.platform === 'win32') {
|
|
52
|
-
debug('Windows platform detected. Applying Docker patch...');
|
|
53
|
-
(0, _child_process.exec)(dockerWindowsPathCmd, {
|
|
54
|
-
shell: 'powershell.exe'
|
|
55
|
-
}, (error, stdout) => {
|
|
56
|
-
if (error) {
|
|
57
|
-
debug(error);
|
|
58
|
-
console.log(`${_chalk.default.red('✕')} There was an error while applying the Windows Docker patch.`);
|
|
59
|
-
} else {
|
|
60
|
-
debug(stdout);
|
|
61
|
-
console.log(`${_chalk.default.green('✓')} Docker patch for Windows applied.`);
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
46
|
await (0, _devEnvironmentCore.startEnvironment)(lando, slug, options);
|
|
66
47
|
const processingTime = Math.ceil((new Date() - startProcessing) / 1000); // in seconds
|
|
67
48
|
const successTrackingInfo = {
|
|
@@ -79,6 +79,9 @@ const appQuery = `
|
|
|
79
79
|
const cmd = new _devEnvSyncSql.DevEnvSyncSQLCommand(app, env, slug, trackerFn);
|
|
80
80
|
// TODO: There's a function called handleCLIException for dev-env that handles exceptions but DevEnvSyncSQLCommand has its own implementation.
|
|
81
81
|
// We should probably use handleCLIException instead?
|
|
82
|
-
await cmd.run();
|
|
82
|
+
const didCommandRun = await cmd.run();
|
|
83
|
+
if (!didCommandRun) {
|
|
84
|
+
console.log('Command canceled by user.');
|
|
85
|
+
}
|
|
83
86
|
await trackerFn('success');
|
|
84
87
|
});
|
|
@@ -172,7 +172,7 @@ class BackupDBCommand {
|
|
|
172
172
|
stack: error.stack
|
|
173
173
|
});
|
|
174
174
|
const errMessage = `A new database backup was not generated because a recently generated backup already exists.
|
|
175
|
-
If you would like to run the same command, you can retry
|
|
175
|
+
If you would like to run the same command, you can retry in ${(0, _format.formatDuration)(new Date(), new Date(retryAfter))}
|
|
176
176
|
Alternatively, you can export the latest existing database backup by running: ${_chalk.default.green('vip @app.env export sql')}, right away.
|
|
177
177
|
Learn more about limitations around generating database backups: https://docs.wpvip.com/technical-references/vip-dashboard/backups/#0-limitations
|
|
178
178
|
`;
|
|
@@ -24,6 +24,7 @@ var _utils = require("../lib/utils");
|
|
|
24
24
|
var _lineByLine = require("../lib/validations/line-by-line");
|
|
25
25
|
var exit = _interopRequireWildcard(require("../lib/cli/exit"));
|
|
26
26
|
var _devEnvImportSql = require("./dev-env-import-sql");
|
|
27
|
+
var _backupStorageAvailability = require("../lib/backup-storage-availability/backup-storage-availability");
|
|
27
28
|
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); }
|
|
28
29
|
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; }
|
|
29
30
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
@@ -92,16 +93,19 @@ class DevEnvSyncSQLCommand {
|
|
|
92
93
|
get gzFile() {
|
|
93
94
|
return `${this.tmpDir}/sql-export.sql.gz`;
|
|
94
95
|
}
|
|
96
|
+
async confirmEnoughStorage(job) {
|
|
97
|
+
const storageAvailability = _backupStorageAvailability.BackupStorageAvailability.createFromDbCopyJob(job);
|
|
98
|
+
return await storageAvailability.validateAndPromptDiskSpaceWarningForDevEnvBackupImport();
|
|
99
|
+
}
|
|
95
100
|
|
|
96
101
|
/**
|
|
97
102
|
* Runs the SQL export command to generate the SQL export from
|
|
98
103
|
* the latest backup
|
|
99
|
-
*
|
|
100
|
-
* @return {Promise<void>} Promise that resolves when the export is complete
|
|
101
104
|
*/
|
|
102
105
|
async generateExport() {
|
|
103
106
|
const exportCommand = new _exportSql.ExportSQLCommand(this.app, this.env, {
|
|
104
|
-
outputFile: this.gzFile
|
|
107
|
+
outputFile: this.gzFile,
|
|
108
|
+
confirmEnoughStorageHook: this.confirmEnoughStorage.bind(this)
|
|
105
109
|
}, this.track);
|
|
106
110
|
await exportCommand.run();
|
|
107
111
|
}
|
|
@@ -161,7 +165,7 @@ class DevEnvSyncSQLCommand {
|
|
|
161
165
|
* Sequentially runs the commands to export, search-replace, and import the SQL file
|
|
162
166
|
* to the local environment
|
|
163
167
|
*
|
|
164
|
-
* @return {Promise<void>} Promise that resolves when the commands are complete
|
|
168
|
+
* @return {Promise<void>} Promise that resolves to true when the commands are complete. It will return false if the user did not continue during validation prompts.
|
|
165
169
|
*/
|
|
166
170
|
async run() {
|
|
167
171
|
try {
|
|
@@ -221,6 +225,7 @@ class DevEnvSyncSQLCommand {
|
|
|
221
225
|
console.log('Importing the SQL file...');
|
|
222
226
|
await this.runImport();
|
|
223
227
|
console.log(`${_chalk.default.green('✓')} SQL file imported`);
|
|
228
|
+
return true;
|
|
224
229
|
} catch (err) {
|
|
225
230
|
await this.track('error', {
|
|
226
231
|
error_type: 'import_sql_file',
|
|
@@ -15,6 +15,7 @@ var _progress = require("../lib/cli/progress");
|
|
|
15
15
|
var exit = _interopRequireWildcard(require("../lib/cli/exit"));
|
|
16
16
|
var _utils = require("../lib/utils");
|
|
17
17
|
var _backupDb = require("./backup-db");
|
|
18
|
+
var _backupStorageAvailability = require("../lib/backup-storage-availability/backup-storage-availability");
|
|
18
19
|
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); }
|
|
19
20
|
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; }
|
|
20
21
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
@@ -190,6 +191,7 @@ class ExportSQLCommand {
|
|
|
190
191
|
PREPARE: 'prepare',
|
|
191
192
|
CREATE: 'create',
|
|
192
193
|
DOWNLOAD_LINK: 'downloadLink',
|
|
194
|
+
CONFIRM_ENOUGH_STORAGE: 'confirmEnoughStorage',
|
|
193
195
|
DOWNLOAD: 'download'
|
|
194
196
|
};
|
|
195
197
|
/**
|
|
@@ -204,6 +206,7 @@ class ExportSQLCommand {
|
|
|
204
206
|
this.app = app;
|
|
205
207
|
this.env = env;
|
|
206
208
|
this.outputFile = typeof options.outputFile === 'string' ? (0, _utils.getAbsolutePath)(options.outputFile) : null;
|
|
209
|
+
this.confirmEnoughStorageHook = options.confirmEnoughStorageHook;
|
|
207
210
|
this.generateBackup = options.generateBackup || false;
|
|
208
211
|
this.progressTracker = new _progress.ProgressTracker([{
|
|
209
212
|
id: this.steps.PREPARE,
|
|
@@ -211,6 +214,9 @@ class ExportSQLCommand {
|
|
|
211
214
|
}, {
|
|
212
215
|
id: this.steps.CREATE,
|
|
213
216
|
name: 'Creating backup copy'
|
|
217
|
+
}, {
|
|
218
|
+
id: this.steps.CONFIRM_ENOUGH_STORAGE,
|
|
219
|
+
name: "Checking if there's enough storage"
|
|
214
220
|
}, {
|
|
215
221
|
id: this.steps.DOWNLOAD_LINK,
|
|
216
222
|
name: 'Requesting download link'
|
|
@@ -320,11 +326,17 @@ class ExportSQLCommand {
|
|
|
320
326
|
this.log(noticeMessage);
|
|
321
327
|
await cmd.run(false);
|
|
322
328
|
}
|
|
329
|
+
async confirmEnoughStorage(job) {
|
|
330
|
+
if (this.confirmEnoughStorageHook) {
|
|
331
|
+
return await this.confirmEnoughStorageHook(job);
|
|
332
|
+
}
|
|
333
|
+
const storageAvailability = _backupStorageAvailability.BackupStorageAvailability.createFromDbCopyJob(job);
|
|
334
|
+
return await storageAvailability.validateAndPromptDiskSpaceWarningForBackupImport();
|
|
335
|
+
}
|
|
323
336
|
|
|
324
337
|
/**
|
|
325
338
|
* Sequentially runs the steps of the export workflow
|
|
326
339
|
*
|
|
327
|
-
* @return {Promise} A promise which resolves to void
|
|
328
340
|
*/
|
|
329
341
|
async run() {
|
|
330
342
|
if (this.outputFile) {
|
|
@@ -389,6 +401,16 @@ class ExportSQLCommand {
|
|
|
389
401
|
this.progressTracker.stepSuccess(this.steps.PREPARE);
|
|
390
402
|
await (0, _utils.pollUntil)(this.getExportJob.bind(this), EXPORT_SQL_PROGRESS_POLL_INTERVAL, this.isCreated.bind(this));
|
|
391
403
|
this.progressTracker.stepSuccess(this.steps.CREATE);
|
|
404
|
+
const storageConfirmed = await this.progressTracker.handleContinuePrompt(async () => {
|
|
405
|
+
return await this.confirmEnoughStorage(await this.getExportJob());
|
|
406
|
+
}, 3);
|
|
407
|
+
if (storageConfirmed) {
|
|
408
|
+
this.progressTracker.stepSuccess(this.steps.CONFIRM_ENOUGH_STORAGE);
|
|
409
|
+
} else {
|
|
410
|
+
this.progressTracker.stepFailed(this.steps.CONFIRM_ENOUGH_STORAGE);
|
|
411
|
+
this.stopProgressTracker();
|
|
412
|
+
exit.withError('Command canceled by user.');
|
|
413
|
+
}
|
|
392
414
|
const url = await generateDownloadLink(this.app.id, this.env.id, latestBackup.id);
|
|
393
415
|
this.progressTracker.stepSuccess(this.steps.DOWNLOAD_LINK);
|
|
394
416
|
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.BackupStorageAvailability = void 0;
|
|
7
|
+
var _shelljs = require("shelljs");
|
|
8
|
+
var _path = _interopRequireDefault(require("path"));
|
|
9
|
+
var _xdgBasedir = _interopRequireDefault(require("xdg-basedir"));
|
|
10
|
+
var _os = _interopRequireDefault(require("os"));
|
|
11
|
+
var _checkDiskSpace = _interopRequireDefault(require("check-disk-space"));
|
|
12
|
+
var _enquirer = require("enquirer");
|
|
13
|
+
var _format = require("../cli/format");
|
|
14
|
+
var _dockerMachineNotFoundError = require("./docker-machine-not-found-error");
|
|
15
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
16
|
+
const oneGiBInBytes = 1024 * 1024 * 1024;
|
|
17
|
+
class BackupStorageAvailability {
|
|
18
|
+
constructor(archiveSize) {
|
|
19
|
+
this.archiveSize = archiveSize;
|
|
20
|
+
}
|
|
21
|
+
static createFromDbCopyJob(job) {
|
|
22
|
+
var _job$metadata;
|
|
23
|
+
const bytesWrittenMeta = (_job$metadata = job.metadata) === null || _job$metadata === void 0 ? void 0 : _job$metadata.find(meta => (meta === null || meta === void 0 ? void 0 : meta.name) === 'bytesWritten');
|
|
24
|
+
if (!(bytesWrittenMeta !== null && bytesWrittenMeta !== void 0 && bytesWrittenMeta.value)) {
|
|
25
|
+
throw new Error('Meta not found');
|
|
26
|
+
}
|
|
27
|
+
return new BackupStorageAvailability(Number(bytesWrittenMeta.value));
|
|
28
|
+
}
|
|
29
|
+
getDockerStorageKiBRaw() {
|
|
30
|
+
return (0, _shelljs.exec)(`docker run --rm alpine df -k`, {
|
|
31
|
+
silent: true
|
|
32
|
+
}).grep(/\/dev\/vda1/).head({
|
|
33
|
+
'-n': 1
|
|
34
|
+
}).replace(/\s+/g, ' ').split(' ')[3];
|
|
35
|
+
}
|
|
36
|
+
getDockerStorageAvailable() {
|
|
37
|
+
const kiBLeft = this.getDockerStorageKiBRaw();
|
|
38
|
+
if (!kiBLeft || Number.isNaN(Number(kiBLeft))) {
|
|
39
|
+
throw new _dockerMachineNotFoundError.DockerMachineNotFoundError();
|
|
40
|
+
}
|
|
41
|
+
return Number(kiBLeft) * 1024;
|
|
42
|
+
}
|
|
43
|
+
bytesToHuman(bytes) {
|
|
44
|
+
return (0, _format.formatMetricBytes)(bytes);
|
|
45
|
+
}
|
|
46
|
+
async getStorageAvailableInVipPath() {
|
|
47
|
+
const vipDir = _path.default.join(_xdgBasedir.default.data ?? _os.default.tmpdir(), 'vip');
|
|
48
|
+
const diskSpace = await (0, _checkDiskSpace.default)(vipDir);
|
|
49
|
+
return diskSpace.free;
|
|
50
|
+
}
|
|
51
|
+
getReserveSpace() {
|
|
52
|
+
return oneGiBInBytes;
|
|
53
|
+
}
|
|
54
|
+
getSqlSize() {
|
|
55
|
+
// We estimated that it'd be about 3.5x the archive size.
|
|
56
|
+
return this.archiveSize * 3.5;
|
|
57
|
+
}
|
|
58
|
+
getArchiveSize() {
|
|
59
|
+
return this.archiveSize;
|
|
60
|
+
}
|
|
61
|
+
getStorageRequiredInMainMachine() {
|
|
62
|
+
return this.getArchiveSize() + this.getSqlSize() + this.getReserveSpace();
|
|
63
|
+
}
|
|
64
|
+
getStorageRequiredInDockerMachine() {
|
|
65
|
+
return this.getSqlSize() + this.getReserveSpace();
|
|
66
|
+
}
|
|
67
|
+
async isStorageAvailableInMainMachine() {
|
|
68
|
+
return (await this.getStorageAvailableInVipPath()) > this.getStorageRequiredInMainMachine();
|
|
69
|
+
}
|
|
70
|
+
isStorageAvailableInDockerMachine() {
|
|
71
|
+
return this.getDockerStorageAvailable() > this.getStorageRequiredInDockerMachine();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// eslint-disable-next-line id-length
|
|
75
|
+
async validateAndPromptDiskSpaceWarningForBackupImport() {
|
|
76
|
+
const isStorageAvailable = (await this.getStorageAvailableInVipPath()) > this.getArchiveSize();
|
|
77
|
+
if (!isStorageAvailable) {
|
|
78
|
+
const storageRequired = this.getArchiveSize();
|
|
79
|
+
const confirmPrompt = new _enquirer.Confirm({
|
|
80
|
+
message: `We recommend that you have at least ${this.bytesToHuman(storageRequired)} of free space in your machine to download this database backup. Do you still want to continue with downloading the database backup?`
|
|
81
|
+
});
|
|
82
|
+
return await confirmPrompt.run();
|
|
83
|
+
}
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// eslint-disable-next-line id-length
|
|
88
|
+
async validateAndPromptDiskSpaceWarningForDevEnvBackupImport() {
|
|
89
|
+
let storageAvailableInMainMachinePrompted = false;
|
|
90
|
+
if (!(await this.isStorageAvailableInMainMachine())) {
|
|
91
|
+
const storageRequired = this.getStorageRequiredInMainMachine();
|
|
92
|
+
const storageAvailableInVipPath = this.bytesToHuman(await this.getStorageAvailableInVipPath());
|
|
93
|
+
const confirmPrompt = new _enquirer.Confirm({
|
|
94
|
+
message: `We recommend that you have at least ${this.bytesToHuman(storageRequired)} of free space in your machine to import this database backup. We estimate that you currently have ${storageAvailableInVipPath} of space in your machine.
|
|
95
|
+
Do you still want to continue with importing the database backup?
|
|
96
|
+
`
|
|
97
|
+
});
|
|
98
|
+
storageAvailableInMainMachinePrompted = await confirmPrompt.run();
|
|
99
|
+
if (!storageAvailableInMainMachinePrompted) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
if (!this.isStorageAvailableInDockerMachine()) {
|
|
105
|
+
const storageRequired = this.getStorageRequiredInDockerMachine();
|
|
106
|
+
const storageAvailableInDockerMachine = this.bytesToHuman(this.getDockerStorageAvailable());
|
|
107
|
+
const confirmPrompt = new _enquirer.Confirm({
|
|
108
|
+
message: `We recommend that you have at least ${this.bytesToHuman(storageRequired)} of free space in your Docker machine to import this database backup. We estimate that you currently have ${storageAvailableInDockerMachine} of space in your machine.
|
|
109
|
+
Do you still want to continue with importing the database backup?`
|
|
110
|
+
});
|
|
111
|
+
return await confirmPrompt.run();
|
|
112
|
+
}
|
|
113
|
+
} catch (error) {
|
|
114
|
+
if (error instanceof _dockerMachineNotFoundError.DockerMachineNotFoundError) {
|
|
115
|
+
// skip storage available check
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
exports.BackupStorageAvailability = BackupStorageAvailability;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.DockerMachineNotFoundError = void 0;
|
|
7
|
+
class DockerMachineNotFoundError extends Error {
|
|
8
|
+
constructor() {
|
|
9
|
+
super('Docker machine not found');
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
exports.DockerMachineNotFoundError = DockerMachineNotFoundError;
|
package/dist/lib/cli/format.js
CHANGED
|
@@ -9,6 +9,7 @@ exports.formatBytes = void 0;
|
|
|
9
9
|
exports.formatData = formatData;
|
|
10
10
|
exports.formatDuration = formatDuration;
|
|
11
11
|
exports.formatEnvironment = formatEnvironment;
|
|
12
|
+
exports.formatMetricBytes = void 0;
|
|
12
13
|
exports.formatSearchReplaceValues = formatSearchReplaceValues;
|
|
13
14
|
exports.getGlyphForStatus = getGlyphForStatus;
|
|
14
15
|
exports.isJson = isJson;
|
|
@@ -186,34 +187,44 @@ function formatSearchReplaceValues(values, message) {
|
|
|
186
187
|
}
|
|
187
188
|
|
|
188
189
|
// Format bytes into kilobytes, megabytes, etc based on the size
|
|
189
|
-
|
|
190
|
+
// for historical reasons, this uses KB instead of KiB, MB instead of MiB and so on.
|
|
191
|
+
const formatBytes = (bytes, decimals = 2, bytesMultiplier = 1024, sizes = ['bytes', 'KB', 'MB', 'GB', 'TB']) => {
|
|
190
192
|
if (0 === bytes) {
|
|
191
193
|
return '0 Bytes';
|
|
192
194
|
}
|
|
193
|
-
const bytesMultiplier = 1024;
|
|
194
195
|
const dm = decimals < 0 ? 0 : decimals;
|
|
195
|
-
const sizes = ['bytes', 'KB', 'MB', 'GB', 'TB'];
|
|
196
196
|
const idx = Math.floor(Math.log(bytes) / Math.log(bytesMultiplier));
|
|
197
197
|
return `${parseFloat((bytes / Math.pow(bytesMultiplier, idx)).toFixed(dm))} ${sizes[idx]}`;
|
|
198
198
|
};
|
|
199
199
|
|
|
200
200
|
/**
|
|
201
|
+
* Format bytes in powers of 1000, based on the size
|
|
202
|
+
* This is how it's displayed on Macs
|
|
203
|
+
*/
|
|
204
|
+
exports.formatBytes = formatBytes;
|
|
205
|
+
const formatMetricBytes = (bytes, decimals = 2) => {
|
|
206
|
+
return formatBytes(bytes, decimals, 1000);
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
/*
|
|
201
210
|
* Get the duration between two dates
|
|
202
211
|
*
|
|
203
212
|
* @param {Date} from The start date
|
|
204
213
|
* @param {Date} to The end date
|
|
205
214
|
* @returns {string} The duration between the two dates
|
|
206
215
|
*/
|
|
207
|
-
exports.
|
|
216
|
+
exports.formatMetricBytes = formatMetricBytes;
|
|
208
217
|
function formatDuration(from, to) {
|
|
209
218
|
const millisecondsPerSecond = 1000;
|
|
210
219
|
const millisecondsPerMinute = 60 * millisecondsPerSecond;
|
|
211
220
|
const millisecondsPerHour = 60 * millisecondsPerMinute;
|
|
212
221
|
const millisecondsPerDay = 24 * millisecondsPerHour;
|
|
213
|
-
const duration =
|
|
222
|
+
const duration = to.getTime() - from.getTime();
|
|
223
|
+
if (duration < 1000) return '0 second';
|
|
214
224
|
const days = Math.floor(duration / millisecondsPerDay);
|
|
215
225
|
const hours = Math.floor(duration % millisecondsPerDay / millisecondsPerHour);
|
|
216
226
|
const minutes = Math.floor(duration % millisecondsPerHour / millisecondsPerMinute);
|
|
227
|
+
const seconds = Math.floor(duration % millisecondsPerMinute / millisecondsPerSecond);
|
|
217
228
|
let durationString = '';
|
|
218
229
|
if (days > 0) {
|
|
219
230
|
durationString += `${days} day${days > 1 ? 's' : ''} `;
|
|
@@ -224,5 +235,8 @@ function formatDuration(from, to) {
|
|
|
224
235
|
if (minutes > 0) {
|
|
225
236
|
durationString += `${minutes} minute${minutes > 1 ? 's' : ''} `;
|
|
226
237
|
}
|
|
238
|
+
if (seconds > 0) {
|
|
239
|
+
durationString += `${seconds} second${seconds > 1 ? 's' : ''}`;
|
|
240
|
+
}
|
|
227
241
|
return durationString.trim();
|
|
228
242
|
}
|
package/dist/lib/cli/progress.js
CHANGED
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.ProgressTracker = void 0;
|
|
6
|
+
exports.StepStatus = exports.ProgressTracker = void 0;
|
|
7
7
|
var _singleLineLog = require("single-line-log");
|
|
8
8
|
var _format = require("../../lib/cli/format");
|
|
9
|
+
var _os = _interopRequireDefault(require("os"));
|
|
10
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
9
11
|
// @format
|
|
10
12
|
|
|
11
13
|
/**
|
|
@@ -17,7 +19,17 @@ var _format = require("../../lib/cli/format");
|
|
|
17
19
|
*/
|
|
18
20
|
|
|
19
21
|
const PRINT_INTERVAL = process.env.DEBUG ? 5000 : 200; // How often the report is printed. Mainly affects the "spinner" animation.
|
|
20
|
-
|
|
22
|
+
let StepStatus = /*#__PURE__*/function (StepStatus) {
|
|
23
|
+
StepStatus["PENDING"] = "pending";
|
|
24
|
+
StepStatus["RUNNING"] = "running";
|
|
25
|
+
StepStatus["SUCCESS"] = "success";
|
|
26
|
+
StepStatus["FAILED"] = "failed";
|
|
27
|
+
StepStatus["UNKNOWN"] = "unknown";
|
|
28
|
+
StepStatus["SKIPPED"] = "skipped";
|
|
29
|
+
return StepStatus;
|
|
30
|
+
}({});
|
|
31
|
+
exports.StepStatus = StepStatus;
|
|
32
|
+
const COMPLETED_STEP_SLUGS = [StepStatus.SUCCESS, StepStatus.SKIPPED];
|
|
21
33
|
class ProgressTracker {
|
|
22
34
|
// Track the state of each step
|
|
23
35
|
|
|
@@ -27,6 +39,14 @@ class ProgressTracker {
|
|
|
27
39
|
|
|
28
40
|
// This gets printed after the step status
|
|
29
41
|
|
|
42
|
+
/**
|
|
43
|
+
* This determines from which step should we display the steps
|
|
44
|
+
*
|
|
45
|
+
* Useful when you want to display a prompt.
|
|
46
|
+
*
|
|
47
|
+
* And we don't want to repeatedly display the steps that has finished.
|
|
48
|
+
*/
|
|
49
|
+
displayFromStep = 0;
|
|
30
50
|
constructor(steps) {
|
|
31
51
|
this.runningSprite = new _format.RunningSprite();
|
|
32
52
|
this.hasFailure = false;
|
|
@@ -48,7 +68,7 @@ class ProgressTracker {
|
|
|
48
68
|
map.set(id, {
|
|
49
69
|
id,
|
|
50
70
|
name,
|
|
51
|
-
status: status ||
|
|
71
|
+
status: status || StepStatus.PENDING
|
|
52
72
|
});
|
|
53
73
|
return map;
|
|
54
74
|
}, new Map());
|
|
@@ -90,7 +110,7 @@ class ProgressTracker {
|
|
|
90
110
|
}) => status === 'pending');
|
|
91
111
|
if (firstPendingStepIndex !== -1) {
|
|
92
112
|
// "Promote" the first "pending" to "running"
|
|
93
|
-
formattedSteps[firstPendingStepIndex].status =
|
|
113
|
+
formattedSteps[firstPendingStepIndex].status = StepStatus.RUNNING;
|
|
94
114
|
}
|
|
95
115
|
}
|
|
96
116
|
this.stepsFromServer = this.mapSteps(formattedSteps);
|
|
@@ -114,16 +134,16 @@ class ProgressTracker {
|
|
|
114
134
|
}) => status === 'running');
|
|
115
135
|
}
|
|
116
136
|
stepRunning(stepId) {
|
|
117
|
-
this.setStatusForStepId(stepId,
|
|
137
|
+
this.setStatusForStepId(stepId, StepStatus.RUNNING);
|
|
118
138
|
}
|
|
119
139
|
stepFailed(stepId) {
|
|
120
|
-
this.setStatusForStepId(stepId,
|
|
140
|
+
this.setStatusForStepId(stepId, StepStatus.FAILED);
|
|
121
141
|
}
|
|
122
142
|
stepSkipped(stepId) {
|
|
123
|
-
this.setStatusForStepId(stepId,
|
|
143
|
+
this.setStatusForStepId(stepId, StepStatus.SKIPPED);
|
|
124
144
|
}
|
|
125
145
|
stepSuccess(stepId) {
|
|
126
|
-
this.setStatusForStepId(stepId,
|
|
146
|
+
this.setStatusForStepId(stepId, StepStatus.SUCCESS);
|
|
127
147
|
// The stepSuccess helper automatically sets the next step to "running"
|
|
128
148
|
const nextStep = this.getNextStep();
|
|
129
149
|
if (nextStep) {
|
|
@@ -163,6 +183,32 @@ class ProgressTracker {
|
|
|
163
183
|
clearInterval(this.printInterval);
|
|
164
184
|
}
|
|
165
185
|
}
|
|
186
|
+
async handleContinuePrompt(prompt) {
|
|
187
|
+
this.print();
|
|
188
|
+
this.stopPrinting();
|
|
189
|
+
const returnValue = await prompt();
|
|
190
|
+
this.displayFromStep = [...this.getSteps().values()].findIndex(step => step.status === StepStatus.RUNNING);
|
|
191
|
+
let hasPrintedOnce = false;
|
|
192
|
+
const printingStartedPromise = new Promise(resolve => {
|
|
193
|
+
this.startPrinting(() => {
|
|
194
|
+
if (hasPrintedOnce) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// this is so that we leave some room for the progress tracker to refresh
|
|
199
|
+
// without this, any prompt, or any text in between will get overwritten by the progress tracker
|
|
200
|
+
let linesToSkip = '';
|
|
201
|
+
for (let iteration = 0; iteration < this.stepsFromCaller.size; iteration++) {
|
|
202
|
+
linesToSkip += _os.default.EOL;
|
|
203
|
+
}
|
|
204
|
+
process.stdout.write(linesToSkip);
|
|
205
|
+
hasPrintedOnce = true;
|
|
206
|
+
resolve();
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
await printingStartedPromise;
|
|
210
|
+
return returnValue;
|
|
211
|
+
}
|
|
166
212
|
print({
|
|
167
213
|
clearAfter = false
|
|
168
214
|
} = {}) {
|
|
@@ -177,7 +223,10 @@ class ProgressTracker {
|
|
|
177
223
|
percentage,
|
|
178
224
|
status,
|
|
179
225
|
progress
|
|
180
|
-
}) => {
|
|
226
|
+
}, stepNumber) => {
|
|
227
|
+
if (stepNumber < this.displayFromStep) {
|
|
228
|
+
return accumulator;
|
|
229
|
+
}
|
|
181
230
|
const statusIcon = (0, _format.getGlyphForStatus)(status, this.runningSprite);
|
|
182
231
|
let suffix = '';
|
|
183
232
|
if (id === 'upload') {
|