@automattic/vip 3.19.3-dev.0 → 3.20.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-import-sql.js +58 -5
- package/dist/lib/backup-storage-availability/backup-storage-availability.js +2 -3
- package/dist/lib/cli/exit.js +4 -4
- package/dist/lib/dev-environment/dev-environment-core.js +5 -14
- package/dist/lib/dev-environment/dev-environment-lando.js +2 -4
- package/dist/lib/search-and-replace.js +2 -2
- package/dist/lib/xdg-data.js +15 -0
- package/npm-shrinkwrap.json +3648 -1094
- package/package.json +10 -21
- package/tsconfig.json +2 -2
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
exports.__esModule = true;
|
|
5
5
|
exports.gates = gates;
|
|
6
|
+
exports.parseHeaders = parseHeaders;
|
|
6
7
|
exports.promptToContinue = void 0;
|
|
7
8
|
exports.validateAndGetTableNames = validateAndGetTableNames;
|
|
8
9
|
var _chalk = _interopRequireDefault(require("chalk"));
|
|
@@ -86,8 +87,8 @@ const SQL_IMPORT_PREFLIGHT_PROGRESS_STEPS = [{
|
|
|
86
87
|
*/
|
|
87
88
|
function isValidUrl(str) {
|
|
88
89
|
try {
|
|
89
|
-
new URL(str);
|
|
90
|
-
return
|
|
90
|
+
const url = new URL(str);
|
|
91
|
+
return !/^[a-z]:$/iu.test(url.protocol);
|
|
91
92
|
} catch {
|
|
92
93
|
return false;
|
|
93
94
|
}
|
|
@@ -102,6 +103,35 @@ function isValidMd5(md5) {
|
|
|
102
103
|
return /^[a-f0-9]{32}$/i.test(md5);
|
|
103
104
|
}
|
|
104
105
|
|
|
106
|
+
/**
|
|
107
|
+
* Parse and validate headers from CLI input
|
|
108
|
+
* @param {string|string[]} headers - Header(s) in "Name: Value" format
|
|
109
|
+
* @returns {Array<{name: string, value: string}>} Parsed headers
|
|
110
|
+
*/
|
|
111
|
+
function parseHeaders(headers) {
|
|
112
|
+
if (!headers) {
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
const headerArray = Array.isArray(headers) ? headers : [headers];
|
|
116
|
+
const parsedHeaders = [];
|
|
117
|
+
for (const header of headerArray) {
|
|
118
|
+
const colonIndex = header.indexOf(':');
|
|
119
|
+
if (colonIndex === -1) {
|
|
120
|
+
exit.withError(`Invalid header format: "${header}". Expected format: "Name: Value"`);
|
|
121
|
+
}
|
|
122
|
+
const name = header.substring(0, colonIndex).trim();
|
|
123
|
+
const value = header.substring(colonIndex + 1).trim();
|
|
124
|
+
if (!name) {
|
|
125
|
+
exit.withError(`Invalid header format: "${header}". Header name cannot be empty.`);
|
|
126
|
+
}
|
|
127
|
+
parsedHeaders.push({
|
|
128
|
+
name,
|
|
129
|
+
value
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
return parsedHeaders;
|
|
133
|
+
}
|
|
134
|
+
|
|
105
135
|
/**
|
|
106
136
|
* @param {import('../lib/site-import/db-file-import').AppForImport} app
|
|
107
137
|
* @param {import('../lib/site-import/db-file-import').EnvForImport} env
|
|
@@ -226,6 +256,11 @@ const examples = [
|
|
|
226
256
|
usage: 'vip @example-app.develop import sql https://example.org/file.sql',
|
|
227
257
|
description: 'Import a remote SQL database backup file from the URL with MD5 hash verification to the develop environment of the "example-app" application.'
|
|
228
258
|
},
|
|
259
|
+
// URL import with HTTP Basic Auth
|
|
260
|
+
{
|
|
261
|
+
usage: 'vip @example-app.develop import sql https://username:password@example.org/file.sql',
|
|
262
|
+
description: 'Import a remote SQL database backup file from a URL that requires HTTP Basic Authentication.'
|
|
263
|
+
},
|
|
229
264
|
// `search-replace` flag
|
|
230
265
|
{
|
|
231
266
|
usage: 'vip @example-app.develop import sql file.sql --search-replace="from.example.com,to.example.com" --search-replace="example.com/from,example.com/to"',
|
|
@@ -241,6 +276,16 @@ const examples = [
|
|
|
241
276
|
usage: 'vip @example-app.develop import sql file.sql --search-replace="https://from.example.com,https://to.example.com" --output="updated-file.sql"',
|
|
242
277
|
description: 'Create a copy of the imported file with the completed search and replace operations and save it locally to a file named "updated-file.sql".'
|
|
243
278
|
},
|
|
279
|
+
// URL import with headers
|
|
280
|
+
{
|
|
281
|
+
usage: 'vip @example-app.develop import sql https://example.org/file.sql --header "Authorization: Bearer token"',
|
|
282
|
+
description: 'Import a remote SQL database backup file from a URL with custom authorization header.'
|
|
283
|
+
},
|
|
284
|
+
// URL import with multiple headers
|
|
285
|
+
{
|
|
286
|
+
usage: 'vip @example-app.develop import sql https://example.org/file.sql --header "Authorization: Bearer token" --header "User-Agent: VIP CLI"',
|
|
287
|
+
description: 'Import a remote SQL database backup file from a URL with multiple custom headers.'
|
|
288
|
+
},
|
|
244
289
|
// `sql status` subcommand
|
|
245
290
|
{
|
|
246
291
|
usage: 'vip @example-app.develop import sql status',
|
|
@@ -374,7 +419,7 @@ const displayPlaybook = ({
|
|
|
374
419
|
requiredArgs: 1,
|
|
375
420
|
module: 'import-sql',
|
|
376
421
|
usage
|
|
377
|
-
}).command('status', 'Check the status of a SQL database import currently in progress.').option('skip-validate', 'Do not perform file validation prior to import. If the file contains unsupported entries, the import is likely to fail.').option('search-replace', 'Search for a string in the SQL file and replace it with a new string. Separate the values by a comma only; no spaces (e.g. --search-replace="from,to").').option('in-place', 'Overwrite the local input file with the results of the search and replace operation prior to import.').option('output', 'The local file path to save a copy of the results from the search and replace operation when the --search-replace option is passed. Ignored when used with the --in-place option.').option('skip-maintenance-mode', 'Imports data without putting the environment in maintenance mode. Available only for unlaunched environments. Caution: This may cause site instability during import.').option('md5', 'MD5 hash of the remote SQL file for verification. If not provided, the verification will not be performed.').examples(examples)
|
|
422
|
+
}).command('status', 'Check the status of a SQL database import currently in progress.').option('skip-validate', 'Do not perform file validation prior to import. If the file contains unsupported entries, the import is likely to fail.').option('search-replace', 'Search for a string in the SQL file and replace it with a new string. Separate the values by a comma only; no spaces (e.g. --search-replace="from,to").').option('in-place', 'Overwrite the local input file with the results of the search and replace operation prior to import.').option('output', 'The local file path to save a copy of the results from the search and replace operation when the --search-replace option is passed. Ignored when used with the --in-place option.').option('skip-maintenance-mode', 'Imports data without putting the environment in maintenance mode. Available only for unlaunched environments. Caution: This may cause site instability during import.').option('md5', 'MD5 hash of the remote SQL file for verification. If not provided, the verification will not be performed.').option('header', 'Add a header to the request when downloading from a URL. Format: "Name: Value". Can be used multiple times.').examples(examples)
|
|
378
423
|
// eslint-disable-next-line complexity
|
|
379
424
|
.argv(process.argv, async (arg, opts) => {
|
|
380
425
|
const {
|
|
@@ -385,7 +430,8 @@ const displayPlaybook = ({
|
|
|
385
430
|
skipValidate,
|
|
386
431
|
searchReplace,
|
|
387
432
|
skipMaintenanceMode,
|
|
388
|
-
md5
|
|
433
|
+
md5,
|
|
434
|
+
header
|
|
389
435
|
} = opts;
|
|
390
436
|
const {
|
|
391
437
|
id: envId,
|
|
@@ -394,6 +440,12 @@ const displayPlaybook = ({
|
|
|
394
440
|
const [fileNameOrURL] = arg;
|
|
395
441
|
const isMultiSite = await (0, _isMultiSite.isMultiSiteInSiteMeta)(appId, envId);
|
|
396
442
|
const isUrl = isValidUrl(fileNameOrURL);
|
|
443
|
+
|
|
444
|
+
// Parse and validate headers
|
|
445
|
+
const headers = parseHeaders(header);
|
|
446
|
+
if (!isUrl && headers.length > 0) {
|
|
447
|
+
console.log(_chalk.default.yellowBright('The --header parameter is only used for URL imports. It will be ignored for local file imports.'));
|
|
448
|
+
}
|
|
397
449
|
if (isUrl && opts.inPlace) {
|
|
398
450
|
console.log(_chalk.default.yellowBright('The --in-place option is not supported when importing from a URL. This option will be ignored.'));
|
|
399
451
|
opts.inPlace = false;
|
|
@@ -527,7 +579,8 @@ Processing the SQL import for your environment...
|
|
|
527
579
|
...startImportVariables.input,
|
|
528
580
|
url: fileNameOrURL,
|
|
529
581
|
searchReplace: [],
|
|
530
|
-
md5
|
|
582
|
+
md5,
|
|
583
|
+
urlHeaders: headers
|
|
531
584
|
};
|
|
532
585
|
} else {
|
|
533
586
|
progressTracker.stepRunning('upload');
|
|
@@ -4,12 +4,11 @@ exports.__esModule = true;
|
|
|
4
4
|
exports.BackupStorageAvailability = void 0;
|
|
5
5
|
var _checkDiskSpace = _interopRequireDefault(require("check-disk-space"));
|
|
6
6
|
var _enquirer = require("enquirer");
|
|
7
|
-
var _os = _interopRequireDefault(require("os"));
|
|
8
7
|
var _path = _interopRequireDefault(require("path"));
|
|
9
8
|
var _shelljs = require("shelljs");
|
|
10
|
-
var _xdgBasedir = _interopRequireDefault(require("xdg-basedir"));
|
|
11
9
|
var _dockerMachineNotFoundError = require("./docker-machine-not-found-error");
|
|
12
10
|
var _format = require("../cli/format");
|
|
11
|
+
var _xdgData = require("../xdg-data");
|
|
13
12
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
14
13
|
const oneGiBInBytes = 1024 * 1024 * 1024;
|
|
15
14
|
class BackupStorageAvailability {
|
|
@@ -31,7 +30,7 @@ class BackupStorageAvailability {
|
|
|
31
30
|
return (0, _format.formatMetricBytes)(bytes);
|
|
32
31
|
}
|
|
33
32
|
async getStorageAvailableInVipPath() {
|
|
34
|
-
const vipDir = _path.default.join(
|
|
33
|
+
const vipDir = _path.default.join((0, _xdgData.xdgData)(), 'vip');
|
|
35
34
|
const diskSpace = await (0, _checkDiskSpace.default)(vipDir);
|
|
36
35
|
return diskSpace.free;
|
|
37
36
|
}
|
package/dist/lib/cli/exit.js
CHANGED
|
@@ -2,20 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
exports.__esModule = true;
|
|
4
4
|
exports.withError = withError;
|
|
5
|
-
var _chalk = require("chalk");
|
|
5
|
+
var _chalk = _interopRequireDefault(require("chalk"));
|
|
6
6
|
var _debug = _interopRequireDefault(require("debug"));
|
|
7
7
|
var _env = _interopRequireDefault(require("../../lib/env"));
|
|
8
8
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
9
|
function withError(message) {
|
|
10
10
|
const msg = message instanceof Error ? message.message : message;
|
|
11
|
-
console.error(`${
|
|
11
|
+
console.error(`${_chalk.default.red('Error: ')} ${msg.replace(/^Error:\s*/, '')}`);
|
|
12
12
|
|
|
13
13
|
// Debug ouput is printed below error output both for information
|
|
14
14
|
// hierarchy and to make it more likely that the user copies it to their
|
|
15
15
|
// clipboard when dragging across output.
|
|
16
|
-
console.log(`${
|
|
16
|
+
console.log(`${_chalk.default.yellow('Debug: ')} VIP-CLI v${_env.default.app.version}, Node ${_env.default.node.version}, ${_env.default.os.name} ${_env.default.os.version} ${_env.default.os.arch}`);
|
|
17
17
|
if (_debug.default.names.length > 0 && message instanceof Error) {
|
|
18
|
-
console.error(
|
|
18
|
+
console.error(_chalk.default.yellow('Debug: '), message);
|
|
19
19
|
}
|
|
20
20
|
process.exit(1);
|
|
21
21
|
}
|
|
@@ -35,7 +35,6 @@ var _promises = require("node:fs/promises");
|
|
|
35
35
|
var _nodePath = _interopRequireDefault(require("node:path"));
|
|
36
36
|
var _semver = _interopRequireDefault(require("semver"));
|
|
37
37
|
var _uuid = require("uuid");
|
|
38
|
-
var _xdgBasedir = _interopRequireDefault(require("xdg-basedir"));
|
|
39
38
|
var _devEnvironmentCli = require("./dev-environment-cli");
|
|
40
39
|
var _devEnvironmentLando = require("./dev-environment-lando");
|
|
41
40
|
var _app = _interopRequireDefault(require("../api/app"));
|
|
@@ -44,6 +43,7 @@ var _devEnvironment = require("../constants/dev-environment");
|
|
|
44
43
|
var _proxyAgent = require("../http/proxy-agent");
|
|
45
44
|
var _searchAndReplace = require("../search-and-replace");
|
|
46
45
|
var _userError = _interopRequireDefault(require("../user-error"));
|
|
46
|
+
var _xdgData = require("../xdg-data");
|
|
47
47
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
48
48
|
const debug = (0, _debug.default)('@automattic/vip:bin:dev-environment');
|
|
49
49
|
const landoFileTemplatePath = _nodePath.default.join(__dirname, '..', '..', '..', 'assets', 'dev-env.lando.template.yml.ejs');
|
|
@@ -58,15 +58,6 @@ const integrationsConfigBackupFileName = 'integrations.json.bak';
|
|
|
58
58
|
const uploadPathString = 'uploads';
|
|
59
59
|
const nginxPathString = 'nginx';
|
|
60
60
|
const integrationsConfigPathString = 'integrations-config';
|
|
61
|
-
function xdgDataDirectory() {
|
|
62
|
-
if (_xdgBasedir.default.data) {
|
|
63
|
-
return _xdgBasedir.default.data;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// This should not happen. If it does, this means that the system was unable to find user's home directory.
|
|
67
|
-
// If so, this does not leave us many options as to where to store the data.
|
|
68
|
-
throw new Error('Unable to determine data directory.');
|
|
69
|
-
}
|
|
70
61
|
async function startEnvironment(lando, slug, options) {
|
|
71
62
|
debug('Will start an environment', slug);
|
|
72
63
|
const instancePath = getEnvironmentPath(slug);
|
|
@@ -175,7 +166,7 @@ async function destroyEnvironment(lando, slug, removeFiles) {
|
|
|
175
166
|
} else {
|
|
176
167
|
debug("Lando file doesn't exist, skipping lando destroy.");
|
|
177
168
|
}
|
|
178
|
-
await _nodeFs.default.promises.rm(_nodePath.default.join(
|
|
169
|
+
await _nodeFs.default.promises.rm(_nodePath.default.join((0, _xdgData.xdgData)(), 'vip', 'lando', 'compose', (0, _utils.dockerComposify)(slug)), {
|
|
179
170
|
force: true,
|
|
180
171
|
recursive: true
|
|
181
172
|
});
|
|
@@ -406,7 +397,7 @@ async function prepareLandoEnv(lando, instanceData, instancePath, integrationsCo
|
|
|
406
397
|
}
|
|
407
398
|
}
|
|
408
399
|
function getAllEnvironmentNames() {
|
|
409
|
-
const mainEnvironmentPath =
|
|
400
|
+
const mainEnvironmentPath = (0, _xdgData.xdgData)();
|
|
410
401
|
const baseDir = _nodePath.default.join(mainEnvironmentPath, 'vip', 'dev-environment');
|
|
411
402
|
const doWeHaveAnyEnvironment = _nodeFs.default.existsSync(baseDir);
|
|
412
403
|
let envNames = [];
|
|
@@ -423,7 +414,7 @@ function getEnvironmentPath(name) {
|
|
|
423
414
|
if (!name) {
|
|
424
415
|
throw new Error('Name was not provided');
|
|
425
416
|
}
|
|
426
|
-
const mainEnvironmentPath =
|
|
417
|
+
const mainEnvironmentPath = (0, _xdgData.xdgData)();
|
|
427
418
|
return _nodePath.default.join(mainEnvironmentPath, 'vip', 'dev-environment', String(name));
|
|
428
419
|
}
|
|
429
420
|
async function getApplicationInformation(appId, envType) {
|
|
@@ -721,7 +712,7 @@ async function isVersionListExpired(cacheFile, ttl) {
|
|
|
721
712
|
*/
|
|
722
713
|
async function getVersionList() {
|
|
723
714
|
let res;
|
|
724
|
-
const mainEnvironmentPath =
|
|
715
|
+
const mainEnvironmentPath = (0, _xdgData.xdgData)();
|
|
725
716
|
const cacheFilePath = _nodePath.default.join(mainEnvironmentPath, 'vip');
|
|
726
717
|
const cacheFile = _nodePath.default.join(cacheFilePath, _devEnvironment.DEV_ENVIRONMENT_WORDPRESS_CACHE_KEY);
|
|
727
718
|
// Handle from cache
|
|
@@ -27,11 +27,11 @@ var _promises2 = require("node:fs/promises");
|
|
|
27
27
|
var _nodeOs = require("node:os");
|
|
28
28
|
var _nodePath = _interopRequireWildcard(require("node:path"));
|
|
29
29
|
var _semver = require("semver");
|
|
30
|
-
var _xdgBasedir = _interopRequireDefault(require("xdg-basedir"));
|
|
31
30
|
var _devEnvironmentCore = require("./dev-environment-core");
|
|
32
31
|
var _dockerUtils = require("./docker-utils");
|
|
33
32
|
var _devEnvironment = require("../constants/dev-environment");
|
|
34
33
|
var _userError = _interopRequireDefault(require("../user-error"));
|
|
34
|
+
var _xdgData = require("../xdg-data");
|
|
35
35
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
36
36
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
37
37
|
/**
|
|
@@ -57,9 +57,7 @@ async function getLandoConfig() {
|
|
|
57
57
|
} else {
|
|
58
58
|
logLevelConsole = 'warn';
|
|
59
59
|
}
|
|
60
|
-
|
|
61
|
-
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
|
62
|
-
const vipDir = _nodePath.default.join(_xdgBasedir.default.data || (0, _nodeOs.tmpdir)(), 'vip'); // NOSONAR
|
|
60
|
+
const vipDir = _nodePath.default.join((0, _xdgData.xdgData)(), 'vip');
|
|
63
61
|
const landoDir = _nodePath.default.join(vipDir, 'lando');
|
|
64
62
|
const fakeHomeDir = _nodePath.default.join(landoDir, 'home');
|
|
65
63
|
try {
|
|
@@ -4,7 +4,7 @@ exports.__esModule = true;
|
|
|
4
4
|
exports.getReadAndWriteStreams = getReadAndWriteStreams;
|
|
5
5
|
exports.searchAndReplace = void 0;
|
|
6
6
|
var _vipSearchReplace = require("@automattic/vip-search-replace");
|
|
7
|
-
var _chalk = require("chalk");
|
|
7
|
+
var _chalk = _interopRequireDefault(require("chalk"));
|
|
8
8
|
var _debug = _interopRequireDefault(require("debug"));
|
|
9
9
|
var _fs = _interopRequireDefault(require("fs"));
|
|
10
10
|
var _promises = require("node:stream/promises");
|
|
@@ -138,7 +138,7 @@ const searchAndReplace = async (fileName, pairs, {
|
|
|
138
138
|
try {
|
|
139
139
|
await (0, _promises.pipeline)(streams);
|
|
140
140
|
} catch (error) {
|
|
141
|
-
console.log(
|
|
141
|
+
console.log(_chalk.default.red("Oh no! We couldn't write to the output file. Please check your available disk space and file/folder permissions."));
|
|
142
142
|
throw error;
|
|
143
143
|
}
|
|
144
144
|
const endTime = process.hrtime(startTime);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
exports.__esModule = true;
|
|
4
|
+
exports.xdgData = xdgData;
|
|
5
|
+
var _nodeOs = require("node:os");
|
|
6
|
+
var _nodePath = require("node:path");
|
|
7
|
+
function xdgData() {
|
|
8
|
+
// Use the XDG_DATA_HOME environment variable if set, otherwise default to ~/.local/share
|
|
9
|
+
const xdgDataHome = process.env.XDG_DATA_HOME;
|
|
10
|
+
if (xdgDataHome) {
|
|
11
|
+
return xdgDataHome;
|
|
12
|
+
}
|
|
13
|
+
const homeDirectory = (0, _nodeOs.homedir)();
|
|
14
|
+
return (0, _nodePath.join)(homeDirectory, '.local', 'share');
|
|
15
|
+
}
|