@mikeyt23/node-cli-utils 1.4.1 → 2.0.0-beta.1
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/README.md +45 -87
- package/dist/cjs/NodeCliUtilsConfig.d.ts +21 -0
- package/dist/cjs/NodeCliUtilsConfig.d.ts.map +1 -0
- package/dist/cjs/NodeCliUtilsConfig.js +41 -0
- package/dist/cjs/TarballUtility.d.ts +53 -0
- package/dist/cjs/TarballUtility.d.ts.map +1 -0
- package/dist/cjs/TarballUtility.js +149 -0
- package/dist/cjs/certUtils.d.ts +30 -0
- package/dist/cjs/certUtils.d.ts.map +1 -0
- package/dist/cjs/certUtils.js +219 -0
- package/dist/cjs/dbMigrationUtils.d.ts +39 -0
- package/dist/cjs/dbMigrationUtils.d.ts.map +1 -0
- package/dist/cjs/dbMigrationUtils.js +205 -0
- package/dist/cjs/dotnetUtils.d.ts +25 -0
- package/dist/cjs/dotnetUtils.d.ts.map +1 -0
- package/dist/cjs/dotnetUtils.js +59 -0
- package/dist/cjs/esmSpecific.d.mts +2 -0
- package/dist/cjs/esmSpecific.d.mts.map +1 -0
- package/dist/cjs/esmSpecific.mjs +10 -0
- package/dist/cjs/generalUtils.d.ts +323 -0
- package/dist/cjs/generalUtils.d.ts.map +1 -0
- package/dist/cjs/generalUtils.js +652 -0
- package/dist/cjs/generalUtilsInternal.d.ts +9 -0
- package/dist/cjs/generalUtilsInternal.d.ts.map +1 -0
- package/dist/cjs/generalUtilsInternal.js +217 -0
- package/dist/cjs/index.d.ts +4 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +25 -0
- package/dist/cjs/package.json +5 -0
- package/dist/cjs/runWhileParentAlive.d.ts +2 -0
- package/dist/cjs/runWhileParentAlive.d.ts.map +1 -0
- package/dist/cjs/runWhileParentAlive.js +161 -0
- package/dist/esm/NodeCliUtilsConfig.d.ts +21 -0
- package/dist/esm/NodeCliUtilsConfig.d.ts.map +1 -0
- package/dist/esm/NodeCliUtilsConfig.js +35 -0
- package/dist/esm/TarballUtility.d.ts +53 -0
- package/dist/esm/TarballUtility.d.ts.map +1 -0
- package/dist/esm/TarballUtility.js +143 -0
- package/dist/esm/certUtils.d.ts +30 -0
- package/dist/esm/certUtils.d.ts.map +1 -0
- package/dist/esm/certUtils.js +185 -0
- package/dist/esm/dbMigrationUtils.d.ts +39 -0
- package/dist/esm/dbMigrationUtils.d.ts.map +1 -0
- package/dist/esm/dbMigrationUtils.js +194 -0
- package/dist/esm/dotnetUtils.d.ts +25 -0
- package/dist/esm/dotnetUtils.d.ts.map +1 -0
- package/dist/esm/dotnetUtils.js +52 -0
- package/dist/esm/esmSpecific.d.mts +2 -0
- package/dist/esm/esmSpecific.d.mts.map +1 -0
- package/dist/esm/esmSpecific.mjs +6 -0
- package/dist/esm/generalUtils.d.ts +323 -0
- package/dist/esm/generalUtils.d.ts.map +1 -0
- package/dist/esm/generalUtils.js +591 -0
- package/dist/esm/generalUtilsInternal.d.ts +9 -0
- package/dist/esm/generalUtilsInternal.d.ts.map +1 -0
- package/dist/esm/generalUtilsInternal.js +185 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +4 -0
- package/dist/esm/runWhileParentAlive.d.ts +2 -0
- package/dist/esm/runWhileParentAlive.d.ts.map +1 -0
- package/dist/esm/runWhileParentAlive.js +153 -0
- package/package.json +67 -10
- package/index.js +0 -627
|
@@ -0,0 +1,591 @@
|
|
|
1
|
+
import { exec, spawnSync } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import fsp from 'node:fs/promises';
|
|
4
|
+
import { platform } from 'node:os';
|
|
5
|
+
import path, { resolve } from 'node:path';
|
|
6
|
+
import * as readline from 'readline';
|
|
7
|
+
import { config } from './NodeCliUtilsConfig.js';
|
|
8
|
+
import { copyEnv, dictionaryToEnvFileString, getEnvAsDictionary, spawnAsyncInternal } from './generalUtilsInternal.js';
|
|
9
|
+
const dockerComposeCommandsThatSupportDetached = ['exec', 'logs', 'ps', 'restart', 'run', 'start', 'stop', 'up'];
|
|
10
|
+
/**
|
|
11
|
+
* Just a wrapper for console.log() to type less.
|
|
12
|
+
* @param data The data to log
|
|
13
|
+
* @param moreData More data to log
|
|
14
|
+
*/
|
|
15
|
+
export function log(data, ...moreData) {
|
|
16
|
+
console.log(data, ...moreData);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Wrapper for console.log() that is suppressed if NodeCliUtilsConfig.logEnabled is false.
|
|
20
|
+
* @param data The data to log
|
|
21
|
+
* @param moreData More data to log
|
|
22
|
+
*/
|
|
23
|
+
export function trace(data, ...moreData) {
|
|
24
|
+
if (config.traceEnabled) {
|
|
25
|
+
const prefix = `[TRACE]`;
|
|
26
|
+
console.log(prefix, data, ...moreData);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Error throw by {@link spawnAsync} when the spawned process exits with a non-zero exit code and options.throwOnNonZero is true.
|
|
31
|
+
*
|
|
32
|
+
* Contains a {@link SpawnResult} with the exit code, stdout, stderr, and error (if any).
|
|
33
|
+
*/
|
|
34
|
+
export class SpawnError extends Error {
|
|
35
|
+
result;
|
|
36
|
+
constructor(message, result) {
|
|
37
|
+
super(message);
|
|
38
|
+
this.result = result;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Error throw by {@link simpleSpawnSync} and {@link simpleCmdSync} when the spawned process exits with a non-zero exit code and throwOnNonZero param is true (the default).
|
|
43
|
+
*
|
|
44
|
+
* Contains a {@link SimpleSpawnResult} with the exit code, stdout, stderr, and error (if any) in addition to stdoutLines, which is stdout split into lines from stdout that weren't empty.
|
|
45
|
+
*/
|
|
46
|
+
export class SimpleSpawnError extends Error {
|
|
47
|
+
result;
|
|
48
|
+
constructor(message, result) {
|
|
49
|
+
super(message);
|
|
50
|
+
this.result = result;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Sleeps for the specified number of milliseconds.
|
|
55
|
+
* @param ms The number of milliseconds to sleep
|
|
56
|
+
* @returns A Promise that resolves after the specified number of milliseconds
|
|
57
|
+
*/
|
|
58
|
+
export async function sleep(ms) {
|
|
59
|
+
return new Promise((resolve) => {
|
|
60
|
+
setTimeout(resolve, ms);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* This is a wrapper function for NodeJS. Defaults stdio to inherit so that output is visible in the console,
|
|
65
|
+
* but note that this means stdout and stderr will not be available in the returned SpawnResult.
|
|
66
|
+
*
|
|
67
|
+
* When spawning long-running processes, use {@link spawnAsyncLongRunning} instead so that unexpected
|
|
68
|
+
* termination of the parent process will not orphan the child process tree on windows.
|
|
69
|
+
* @param command The command to spawn
|
|
70
|
+
* @param args The arguments to pass to the command
|
|
71
|
+
* @param options The options to pass to the command
|
|
72
|
+
* @returns A Promise that resolves to a {@link SpawnResult}
|
|
73
|
+
*/
|
|
74
|
+
export async function spawnAsync(command, args, options) {
|
|
75
|
+
return spawnAsyncInternal(command, args, options);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Use this alternate spawn wrapper instead of {@link spawnAsync} when spawning long-running processes to
|
|
79
|
+
* avoid orphaned child process trees on Windows.
|
|
80
|
+
* @param command The command to spawn
|
|
81
|
+
* @param args The arguments to pass to the command
|
|
82
|
+
* @param cwd The current working directory to run the command from - defaults to process.cwd()
|
|
83
|
+
* @returns A Promise that resolves to a {@link SpawnResult}
|
|
84
|
+
*/
|
|
85
|
+
export async function spawnAsyncLongRunning(command, args, cwd) {
|
|
86
|
+
return spawnAsyncInternal(command, args, { cwd: cwd, isLongRunning: true });
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Ensure the directory exists. Similar to `mkdir -p` (creates parent directories if they don't exist).
|
|
90
|
+
* @param dir The directory to ensure exists. If it does not exist, it will be created.
|
|
91
|
+
*/
|
|
92
|
+
export async function ensureDirectory(dir) {
|
|
93
|
+
requireString('dir', dir);
|
|
94
|
+
if (!fs.existsSync(dir)) {
|
|
95
|
+
await mkdirp(dir);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Create a directory. Will create parent directory structure if it don't exist. Similar to `mkdir -p`.
|
|
100
|
+
* @param dir The directory to create.
|
|
101
|
+
*/
|
|
102
|
+
export async function mkdirp(dir) {
|
|
103
|
+
await fsp.mkdir(dir, { recursive: true });
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Empties a directory of all files and subdirectories.
|
|
107
|
+
*
|
|
108
|
+
* Optionally skips files and directories at the top level.
|
|
109
|
+
* @param directoryToEmpty The directory to empty.
|
|
110
|
+
* @param fileAndDirectoryNamesToSkip An optional array of file and directory names to skip, but only at the top level of the directoryToEmpty.
|
|
111
|
+
*/
|
|
112
|
+
export async function emptyDirectory(directoryToEmpty, fileAndDirectoryNamesToSkip) {
|
|
113
|
+
requireString('directoryToEmpty', directoryToEmpty);
|
|
114
|
+
if (!fs.existsSync(directoryToEmpty)) {
|
|
115
|
+
trace(`directoryToEmpty does not exist - creating directory ${directoryToEmpty}`);
|
|
116
|
+
await mkdirp(directoryToEmpty);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
if (!fs.lstatSync(directoryToEmpty).isDirectory()) {
|
|
120
|
+
throw new Error(`directoryToEmpty is not a directory: ${directoryToEmpty}`);
|
|
121
|
+
}
|
|
122
|
+
// Add some guardrails to prevent accidentally emptying the wrong directory
|
|
123
|
+
const absolutePath = path.resolve(directoryToEmpty);
|
|
124
|
+
log(`emptying directory: ${absolutePath}`);
|
|
125
|
+
if (!absolutePath.startsWith(process.cwd())) {
|
|
126
|
+
throw new Error(`directoryToEmpty must be a child of the current working directory: ${directoryToEmpty}`);
|
|
127
|
+
}
|
|
128
|
+
if (absolutePath === process.cwd()) {
|
|
129
|
+
throw new Error(`directoryToEmpty cannot be the current working directory: ${directoryToEmpty}`);
|
|
130
|
+
}
|
|
131
|
+
const dir = await fsp.opendir(directoryToEmpty, { encoding: 'utf-8' });
|
|
132
|
+
if (fileAndDirectoryNamesToSkip && !Array.isArray(fileAndDirectoryNamesToSkip)) {
|
|
133
|
+
throw new Error('fileAndDirectoryNamesToSkip must be an array');
|
|
134
|
+
}
|
|
135
|
+
let dirEntry = await dir.read();
|
|
136
|
+
while (dirEntry) {
|
|
137
|
+
if (fileAndDirectoryNamesToSkip && fileAndDirectoryNamesToSkip.includes(dirEntry.name)) {
|
|
138
|
+
dirEntry = await dir.read();
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
const direntPath = path.join(directoryToEmpty, dirEntry.name);
|
|
142
|
+
if (dirEntry.isDirectory()) {
|
|
143
|
+
await fsp.rm(direntPath, { recursive: true });
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
await fsp.unlink(direntPath);
|
|
147
|
+
}
|
|
148
|
+
dirEntry = await dir.read();
|
|
149
|
+
}
|
|
150
|
+
await dir.close();
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Copies the contents of a directory to another directory (not including the top-level directory itself).
|
|
154
|
+
*
|
|
155
|
+
* If the destination directory does not exist, it will be created.
|
|
156
|
+
* @param sourceDirectory Directory to copy from
|
|
157
|
+
* @param destinationDirectory Directory to copy to
|
|
158
|
+
*/
|
|
159
|
+
export async function copyDirectoryContents(sourceDirectory, destinationDirectory) {
|
|
160
|
+
requireString('sourceDirectory', sourceDirectory);
|
|
161
|
+
requireString('destinationDirectory', destinationDirectory);
|
|
162
|
+
if (!fs.existsSync(sourceDirectory)) {
|
|
163
|
+
throw new Error(`sourceDirectory directory does not exist: ${sourceDirectory}`);
|
|
164
|
+
}
|
|
165
|
+
if (!fs.lstatSync(sourceDirectory).isDirectory()) {
|
|
166
|
+
throw new Error(`sourceDirectory is not a directory: ${sourceDirectory}`);
|
|
167
|
+
}
|
|
168
|
+
if (!fs.existsSync(destinationDirectory)) {
|
|
169
|
+
await mkdirp(destinationDirectory);
|
|
170
|
+
}
|
|
171
|
+
if (!fs.lstatSync(destinationDirectory).isDirectory()) {
|
|
172
|
+
throw new Error(`destinationDirectory is not a directory: ${destinationDirectory}`);
|
|
173
|
+
}
|
|
174
|
+
const dir = await fsp.opendir(sourceDirectory, { encoding: 'utf-8' });
|
|
175
|
+
let dirEntry = await dir.read();
|
|
176
|
+
while (dirEntry) {
|
|
177
|
+
const sourcePath = path.join(sourceDirectory, dirEntry.name);
|
|
178
|
+
const destPath = path.join(destinationDirectory, dirEntry.name);
|
|
179
|
+
if (dirEntry.isDirectory()) {
|
|
180
|
+
await copyDirectoryContents(sourcePath, destPath);
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
await fsp.copyFile(sourcePath, destPath);
|
|
184
|
+
}
|
|
185
|
+
dirEntry = await dir.read();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Helper method to validate that a non-falsy value is provided for a parameter that should be a string.
|
|
190
|
+
*
|
|
191
|
+
* **Warning:** this does not validate the type of the parameter, just whether something non-empty was provided.
|
|
192
|
+
* @param paramName The name of the parameter, for logging purposes
|
|
193
|
+
* @param paramValue The value of the parameter
|
|
194
|
+
*/
|
|
195
|
+
export function requireString(paramName, paramValue) {
|
|
196
|
+
if (paramValue === undefined || paramValue === null || paramValue === '') {
|
|
197
|
+
throw new Error(`Required param '${paramName}' is missing`);
|
|
198
|
+
}
|
|
199
|
+
if (typeof paramValue !== 'string') {
|
|
200
|
+
throw new Error(`Required param '${paramName}' is not a string`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Helper method to validate that the path actually exists for the provided value.
|
|
205
|
+
* @param paramName The name of the parameter, for logging purposes
|
|
206
|
+
* @param paramValue The value of the parameter
|
|
207
|
+
*/
|
|
208
|
+
export function requireValidPath(paramName, paramValue) {
|
|
209
|
+
requireString(paramName, paramValue);
|
|
210
|
+
if (!fs.existsSync(paramValue)) {
|
|
211
|
+
throw new Error(`Invalid or nonexistent path provided for param '${paramName}': ${paramValue}`);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* For docker compose commands, see https://docs.docker.com/compose/reference/.
|
|
216
|
+
* @param dockerComposePath Path to docker-compose.yml
|
|
217
|
+
* @param dockerComposeCommand The docker-compose command to run
|
|
218
|
+
* @param options {@link DockerComposeOptions} to use, including additional arguments to pass to the docker compose command and the project name
|
|
219
|
+
*/
|
|
220
|
+
export async function spawnDockerCompose(dockerComposePath, dockerComposeCommand, options) {
|
|
221
|
+
requireValidPath('dockerComposePath', dockerComposePath);
|
|
222
|
+
requireString('dockerComposeCommand', dockerComposeCommand);
|
|
223
|
+
const useDockerComposeFileDirectoryAsCwd = options && options.useDockerComposeFileDirectoryAsCwd;
|
|
224
|
+
if (await isDockerRunning() === false) {
|
|
225
|
+
throw new Error('Docker is not running');
|
|
226
|
+
}
|
|
227
|
+
const defaultOptions = { attached: false };
|
|
228
|
+
const mergedOptions = { ...defaultOptions, ...options };
|
|
229
|
+
const dockerComposeDir = path.dirname(dockerComposePath);
|
|
230
|
+
const dockerComposeFilename = path.basename(dockerComposePath);
|
|
231
|
+
const spawnCommand = 'docker';
|
|
232
|
+
let spawnArgs = ['compose', '-f', useDockerComposeFileDirectoryAsCwd ? dockerComposeFilename : dockerComposePath];
|
|
233
|
+
if (mergedOptions.projectName) {
|
|
234
|
+
spawnArgs.push('--project-name', mergedOptions.projectName);
|
|
235
|
+
}
|
|
236
|
+
spawnArgs.push(dockerComposeCommand);
|
|
237
|
+
if (!mergedOptions.attached && dockerComposeCommandsThatSupportDetached.includes(dockerComposeCommand)) {
|
|
238
|
+
spawnArgs.push('-d');
|
|
239
|
+
}
|
|
240
|
+
if (mergedOptions.args) {
|
|
241
|
+
spawnArgs = spawnArgs.concat(mergedOptions.args);
|
|
242
|
+
}
|
|
243
|
+
const dockerCommandString = `docker ${spawnArgs.join(' ')}`;
|
|
244
|
+
const traceMessage = useDockerComposeFileDirectoryAsCwd ?
|
|
245
|
+
`running command in ${dockerComposeDir}: ${dockerCommandString}` :
|
|
246
|
+
`running command: ${dockerCommandString}`;
|
|
247
|
+
trace(traceMessage);
|
|
248
|
+
const longRunning = dockerComposeCommandsThatSupportDetached.includes(dockerComposeCommand) && options && options.attached;
|
|
249
|
+
const spawnOptions = {
|
|
250
|
+
cwd: useDockerComposeFileDirectoryAsCwd ? dockerComposeDir : process.cwd(),
|
|
251
|
+
shell: true,
|
|
252
|
+
isLongRunning: longRunning
|
|
253
|
+
};
|
|
254
|
+
const spawnResult = await spawnAsyncInternal(spawnCommand, spawnArgs, spawnOptions);
|
|
255
|
+
if (spawnResult.code !== 0) {
|
|
256
|
+
throw new Error(`docker compose command failed with code ${spawnResult.code}`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Splits a string into lines, removing empty lines and carriage return characters.
|
|
261
|
+
* @param str String to split into lines
|
|
262
|
+
* @returns An array of lines from the string, with empty lines removed
|
|
263
|
+
*/
|
|
264
|
+
export function stringToNonEmptyLines(str) {
|
|
265
|
+
if (!str) {
|
|
266
|
+
return [];
|
|
267
|
+
}
|
|
268
|
+
return str.split('\n').filter(line => line && line.trim()).map(line => line.replace('\r', ''));
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Runs the requested command using NodeJS spawnSync wrapped in an outer Windows CMD.exe command and returns the result with stdout split into lines.
|
|
272
|
+
*
|
|
273
|
+
* Use this for simple quick commands that don't require a lot of control.
|
|
274
|
+
*
|
|
275
|
+
* For commands that aren't Windows and CMD specific, use {@link simpleSpawnSync}.
|
|
276
|
+
* @param command Command to run
|
|
277
|
+
* @param args Arguments to pass to the command
|
|
278
|
+
* @returns An object with the status code, stdout, stderr, and error (if any)
|
|
279
|
+
* @throws {@link SimpleSpawnError} if the command fails and throwOnNonZero is true
|
|
280
|
+
*/
|
|
281
|
+
export function simpleCmdSync(command, args, throwOnNonZero = true) {
|
|
282
|
+
if (!isPlatformWindows()) {
|
|
283
|
+
throw new Error('getCmdResult is only supported on Windows');
|
|
284
|
+
}
|
|
285
|
+
return simpleSpawnSync('cmd', ['/D', '/S', '/C', command, ...(args ?? [])], throwOnNonZero);
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Runs the requested command using NodeJS spawnSync and returns the result with stdout split into lines.
|
|
289
|
+
*
|
|
290
|
+
* Use this for simple quick commands that don't require a lot of control.
|
|
291
|
+
*
|
|
292
|
+
* For commands that are Windows and CMD specific, use {@link simpleCmdSync}.
|
|
293
|
+
* @param command Command to run
|
|
294
|
+
* @param args Arguments to pass to the command
|
|
295
|
+
* @returns An object with the status code, stdout, stderr, and error (if any)
|
|
296
|
+
* @throws {@link SimpleSpawnError} if the command fails and throwOnNonZero is true
|
|
297
|
+
*/
|
|
298
|
+
export function simpleSpawnSync(command, args, throwOnNonZero = true) {
|
|
299
|
+
requireString('command', command);
|
|
300
|
+
const result = spawnSync(command, args ?? [], { encoding: 'utf-8' });
|
|
301
|
+
const spawnResult = {
|
|
302
|
+
code: result.status ?? 1,
|
|
303
|
+
stdout: result.stdout.toString(),
|
|
304
|
+
stderr: result.stdout.toString(),
|
|
305
|
+
stdoutLines: stringToNonEmptyLines(result.stdout.toString()),
|
|
306
|
+
error: result.error,
|
|
307
|
+
cwd: process.cwd()
|
|
308
|
+
};
|
|
309
|
+
if (spawnResult.code !== 0 && throwOnNonZero) {
|
|
310
|
+
throw new SimpleSpawnError(`spawned process failed with code ${spawnResult.code}`, spawnResult);
|
|
311
|
+
}
|
|
312
|
+
return spawnResult;
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* @returns `true` if platform() is 'win32', `false` otherwise
|
|
316
|
+
*/
|
|
317
|
+
export function isPlatformWindows() {
|
|
318
|
+
return platform() === 'win32';
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
*
|
|
322
|
+
* @returns `true` if platform() is 'darwin', `false` otherwise
|
|
323
|
+
*/
|
|
324
|
+
export function isPlatformMac() {
|
|
325
|
+
return platform() === 'darwin';
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
*
|
|
329
|
+
* @returns `true` if {@link isPlatformWindows} and {@link isPlatformMac} are both `false, otherwise returns `false`
|
|
330
|
+
*/
|
|
331
|
+
export function isPlatformLinux() {
|
|
332
|
+
return !isPlatformWindows() && !isPlatformMac();
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* This is a cross-platform method to get the location of a system command. Useful for checking if software
|
|
336
|
+
* is installed, where it's installed and whether there are multiple locations for a command.
|
|
337
|
+
* @param commandName The name of the command to find
|
|
338
|
+
* @returns The location of the command, any additional locations, and an error if one occurred
|
|
339
|
+
*/
|
|
340
|
+
export function whichSync(commandName) {
|
|
341
|
+
if (isPlatformWindows()) {
|
|
342
|
+
const result = simpleCmdSync('where', [commandName]);
|
|
343
|
+
return {
|
|
344
|
+
location: result.stdoutLines[0],
|
|
345
|
+
additionalLocations: result.stdoutLines.slice(1),
|
|
346
|
+
error: result.error
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
const result = simpleSpawnSync('which', ['-a', commandName]);
|
|
351
|
+
return {
|
|
352
|
+
location: result.stdoutLines[0],
|
|
353
|
+
additionalLocations: result.stdoutLines.slice(1),
|
|
354
|
+
error: result.error
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* First checks if docker is installed and if not immediately returns false.
|
|
360
|
+
*
|
|
361
|
+
* Then runs the `docker info` command and looks for "error during connect" in the output to determine if docker is running.
|
|
362
|
+
* @returns `true` if docker is installed and running, `false` otherwise
|
|
363
|
+
*/
|
|
364
|
+
export async function isDockerRunning() {
|
|
365
|
+
if (!whichSync('docker').location) {
|
|
366
|
+
trace('whichSync will not check if docker is running because docker does not appear to be installed - returning false');
|
|
367
|
+
return false;
|
|
368
|
+
}
|
|
369
|
+
return new Promise((resolve) => {
|
|
370
|
+
exec('docker info', (error, stdout) => {
|
|
371
|
+
if (error) {
|
|
372
|
+
resolve(false);
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
if (!stdout || stdout.includes('error during connect')) {
|
|
376
|
+
resolve(false);
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
resolve(true);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Uses built-in NodeJS readline to ask a question and return the user's answer.
|
|
387
|
+
* @param query The question to ask
|
|
388
|
+
* @returns A Promise that resolves to the user's answer
|
|
389
|
+
*/
|
|
390
|
+
export function askQuestion(query) {
|
|
391
|
+
const rl = readline.createInterface({
|
|
392
|
+
input: process.stdin,
|
|
393
|
+
output: process.stdout,
|
|
394
|
+
});
|
|
395
|
+
return new Promise(resolve => rl.question(`\n${query}\n`, ans => {
|
|
396
|
+
rl.close();
|
|
397
|
+
resolve(ans);
|
|
398
|
+
}));
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* A simple CLI prompt using the built-in NodeJS readline functionality to ask for confirmation.
|
|
402
|
+
* @param question The question to ask
|
|
403
|
+
* @returns A Promise that resolves to true if the user answers 'y' or 'yes', false otherwise
|
|
404
|
+
*/
|
|
405
|
+
export function getConfirmation(question) {
|
|
406
|
+
const rl = readline.createInterface({
|
|
407
|
+
input: process.stdin,
|
|
408
|
+
output: process.stdout,
|
|
409
|
+
});
|
|
410
|
+
return new Promise((resolve) => {
|
|
411
|
+
rl.question(`\n ❓ ${question}\n ➡️ Proceed? (yes/no): `, (answer) => {
|
|
412
|
+
rl.close();
|
|
413
|
+
const confirmed = answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes';
|
|
414
|
+
log(confirmed ? ' ✅ Proceeding\n' : ' ❌ Aborting\n');
|
|
415
|
+
resolve(confirmed);
|
|
416
|
+
});
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Example of using {@link getConfirmation}.
|
|
421
|
+
*/
|
|
422
|
+
export async function getConfirmationExample() {
|
|
423
|
+
if (await getConfirmation('Do you even?')) {
|
|
424
|
+
log('you do even');
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
log('you do not even');
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Copy entries from a source .env file to a destination .env file for which the destination .env file does not already have entries.
|
|
432
|
+
* If the destination .env file does not exist, it will be created and populated with the source .env file's values.
|
|
433
|
+
*
|
|
434
|
+
* This is useful for copying values from a .env.template file to a root .env file.
|
|
435
|
+
*
|
|
436
|
+
* For copying root .env files to other locations, use {@link overwriteEnvFile}.
|
|
437
|
+
* @param sourcePath The path to the source .env file such as a `.env.template` file (use {@link overwriteEnvFile} for copying root .env files to other locations)
|
|
438
|
+
* @param destinationPath The path to the destination .env file, such as the root .env file
|
|
439
|
+
*/
|
|
440
|
+
export async function copyNewEnvValues(sourcePath, destinationPath) {
|
|
441
|
+
await copyEnv(sourcePath, destinationPath, false);
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Copy entries from a source .env file to a destination .env file, overwriting any existing entries in the destination .env file.
|
|
445
|
+
* If the destination .env file does not exist, it will be created and populated with the source .env file's values.
|
|
446
|
+
*
|
|
447
|
+
* This is useful for copying values from a root .env file to additional locations (server, client, docker-compose directory, etc.)
|
|
448
|
+
* throughout your solution so you only have to manage one .env file.
|
|
449
|
+
*
|
|
450
|
+
* Note that this does not delete any existing entries in the destination .env file, which is useful if you have additional entries in
|
|
451
|
+
* the destination .env file that you don't want to overwrite.
|
|
452
|
+
*
|
|
453
|
+
* For copying .env.template files to root .env files, use {@link copyNewEnvValues}.
|
|
454
|
+
* @param sourcePath The path to the source .env file such as a root .env file (use {@link copyNewEnvValues} for .env.template files)
|
|
455
|
+
* @param destinationPath The path to the destination .env file
|
|
456
|
+
* @param suppressAddKeysMessages If true, messages about adding missing keys will not be logged (useful if you're always calling {@link copyModifiedEnv} after this call)
|
|
457
|
+
*/
|
|
458
|
+
export async function overwriteEnvFile(sourcePath, destinationPath, suppressAddKeysMessages = false) {
|
|
459
|
+
await copyEnv(sourcePath, destinationPath, true, suppressAddKeysMessages);
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Copy entries from a source .env file to a destination .env file, but only for the keys specified in keepKeys.
|
|
463
|
+
* Will also modify entries in the destination .env file as specified in modifyEntries.
|
|
464
|
+
* @param sourcePath The path to the source .env file
|
|
465
|
+
* @param destinationPath The path to the destination .env file
|
|
466
|
+
* @param keepKeys The keys to keep from the source .env file
|
|
467
|
+
* @param modifyEntries The entries to modify in the destination .env file
|
|
468
|
+
*/
|
|
469
|
+
export async function copyModifiedEnv(sourcePath, destinationPath, keepKeys, modifyEntries) {
|
|
470
|
+
requireValidPath('sourcePath', sourcePath);
|
|
471
|
+
const destPathDir = path.dirname(destinationPath);
|
|
472
|
+
if (!fs.existsSync(destPathDir)) {
|
|
473
|
+
await ensureDirectory(destPathDir);
|
|
474
|
+
}
|
|
475
|
+
const sourceDict = getEnvAsDictionary(sourcePath);
|
|
476
|
+
const newDict = filterDictionary(sourceDict, key => keepKeys.includes(key));
|
|
477
|
+
if (modifyEntries && Object.keys(modifyEntries).length > 0) {
|
|
478
|
+
for (const [key, value] of Object.entries(modifyEntries)) {
|
|
479
|
+
newDict[key] = value;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
const newSortedDict = sortDictionaryByKeyAsc(newDict);
|
|
483
|
+
const newEnvFileContent = dictionaryToEnvFileString(newSortedDict);
|
|
484
|
+
await fsp.writeFile(destinationPath, newEnvFileContent);
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Filters a dictionary by key.
|
|
488
|
+
* @param dict The dictionary to filter
|
|
489
|
+
* @param predicate A function that returns true if the key should be included in the filtered dictionary
|
|
490
|
+
* @returns A new dictionary with only the keys that passed the predicate
|
|
491
|
+
*/
|
|
492
|
+
export function filterDictionary(dict, predicate) {
|
|
493
|
+
// Notes to self:
|
|
494
|
+
// - The second param of reduce is the initial value of the accumulator
|
|
495
|
+
// - Reduce processes each element of the array and returns the accumulator for the next iteration
|
|
496
|
+
// - In our case, the accumulator is a new dictionary that we're building up
|
|
497
|
+
return Object.keys(dict)
|
|
498
|
+
.filter(predicate)
|
|
499
|
+
.reduce((accumulator, key) => {
|
|
500
|
+
accumulator[key] = dict[key];
|
|
501
|
+
return accumulator;
|
|
502
|
+
}, {});
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Sorts a dictionary by key in ascending order.
|
|
506
|
+
* @param dict The dictionary to sort
|
|
507
|
+
* @returns A new dictionary sorted by key in ascending order
|
|
508
|
+
*/
|
|
509
|
+
export function sortDictionaryByKeyAsc(dict) {
|
|
510
|
+
const newSortedDict = Object.entries(dict).sort((a, b) => {
|
|
511
|
+
if (a < b) {
|
|
512
|
+
return -1;
|
|
513
|
+
}
|
|
514
|
+
if (a > b) {
|
|
515
|
+
return 1;
|
|
516
|
+
}
|
|
517
|
+
return 0;
|
|
518
|
+
});
|
|
519
|
+
return Object.fromEntries(newSortedDict);
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Helper method to delete a .env file if it exists.
|
|
523
|
+
* @param envPath The path to the .env file to delete
|
|
524
|
+
*/
|
|
525
|
+
export async function deleteEnvIfExists(envPath) {
|
|
526
|
+
// Just protecting ourselves from accidentally deleting something we didn't mean to
|
|
527
|
+
if (envPath.endsWith('.env') === false) {
|
|
528
|
+
throw new Error(`envPath must end with '.env': ${envPath}`);
|
|
529
|
+
}
|
|
530
|
+
// Using fsp.unlink will throw an error if it's a directory
|
|
531
|
+
if (fs.existsSync(envPath)) {
|
|
532
|
+
await fsp.unlink(envPath);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Searches a directory recursively for files that match the specified pattern.
|
|
537
|
+
* The filenamePattern is a simple text string with asterisks (*) for wildcards.
|
|
538
|
+
* @param dir The directory to find files in
|
|
539
|
+
* @param filenamePattern The pattern to match files against
|
|
540
|
+
* @param options Specify a max depth to search, defaults to 5
|
|
541
|
+
* @returns A Promise that resolves to an array of file paths that match the pattern
|
|
542
|
+
*/
|
|
543
|
+
export async function findFilesRecursively(dir, filenamePattern, options) {
|
|
544
|
+
requireValidPath('dir', dir);
|
|
545
|
+
requireString('pattern', filenamePattern);
|
|
546
|
+
if (filenamePattern.length > 50) {
|
|
547
|
+
throw new Error(`filenamePattern param must have fewer than 50 characters`);
|
|
548
|
+
}
|
|
549
|
+
const numWildcards = filenamePattern.replace(/\*+/g, '*').split('*').length - 1;
|
|
550
|
+
if (numWildcards > 5) {
|
|
551
|
+
throw new Error(`filenamePattern param must contain 5 or fewer wildcards`);
|
|
552
|
+
}
|
|
553
|
+
if (filenamePattern.includes('/') || filenamePattern.includes('\\')) {
|
|
554
|
+
throw new Error('filenamePattern param must not contain slashes');
|
|
555
|
+
}
|
|
556
|
+
const defaultOptions = { maxDepth: 5 };
|
|
557
|
+
const mergedOptions = { ...defaultOptions, ...options };
|
|
558
|
+
// Convert the pattern to a regex
|
|
559
|
+
const regex = new RegExp('^' + filenamePattern.split(/\*+/).map(escapeStringForRegex).join('.*') + '$');
|
|
560
|
+
const matches = [];
|
|
561
|
+
// Recursive function to search within directories
|
|
562
|
+
async function searchDirectory(directory, depth) {
|
|
563
|
+
if (depth > mergedOptions.maxDepth)
|
|
564
|
+
return;
|
|
565
|
+
const entries = await fsp.readdir(directory, { withFileTypes: true });
|
|
566
|
+
for (const entry of entries) {
|
|
567
|
+
const fullPath = resolve(directory, entry.name);
|
|
568
|
+
if (entry.isDirectory()) {
|
|
569
|
+
// Check if directory is in the exclude list
|
|
570
|
+
if (!mergedOptions.excludeDirectoryNames || !mergedOptions.excludeDirectoryNames.includes(entry.name)) {
|
|
571
|
+
await searchDirectory(fullPath, depth + 1);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
else if (entry.isFile() && regex.test(entry.name)) {
|
|
575
|
+
if (mergedOptions.returnForwardSlashRelativePaths) {
|
|
576
|
+
matches.push(path.relative(dir, fullPath).replace(/\\/g, '/'));
|
|
577
|
+
}
|
|
578
|
+
else {
|
|
579
|
+
matches.push(fullPath);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
await searchDirectory(dir, 1); // Start search from the first depth
|
|
585
|
+
return matches;
|
|
586
|
+
}
|
|
587
|
+
/** Utility function to escape a string for use within regex */
|
|
588
|
+
export function escapeStringForRegex(str) {
|
|
589
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
590
|
+
}
|
|
591
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2VuZXJhbFV0aWxzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2dlbmVyYWxVdGlscy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQWdCLElBQUksRUFBRSxTQUFTLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQTtBQUNsRSxPQUFPLEVBQUUsTUFBTSxTQUFTLENBQUE7QUFDeEIsT0FBTyxHQUFHLE1BQU0sa0JBQWtCLENBQUE7QUFDbEMsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLFNBQVMsQ0FBQTtBQUNsQyxPQUFPLElBQUksRUFBRSxFQUFFLE9BQU8sRUFBRSxNQUFNLFdBQVcsQ0FBQTtBQUN6QyxPQUFPLEtBQUssUUFBUSxNQUFNLFVBQVUsQ0FBQTtBQUNwQyxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0seUJBQXlCLENBQUE7QUFDaEQsT0FBTyxFQUF3QixPQUFPLEVBQUUseUJBQXlCLEVBQUUsa0JBQWtCLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQTtBQUU1SSxNQUFNLHdDQUF3QyxHQUFHLENBQUMsTUFBTSxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLEtBQUssRUFBRSxPQUFPLEVBQUUsTUFBTSxFQUFFLElBQUksQ0FBQyxDQUFBO0FBRWhIOzs7O0dBSUc7QUFDSCxNQUFNLFVBQVUsR0FBRyxDQUFDLElBQWEsRUFBRSxHQUFHLFFBQW1CO0lBQ3ZELE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxFQUFFLEdBQUcsUUFBUSxDQUFDLENBQUE7QUFDaEMsQ0FBQztBQUVEOzs7O0dBSUc7QUFDSCxNQUFNLFVBQVUsS0FBSyxDQUFDLElBQWMsRUFBRSxHQUFHLFFBQW1CO0lBQzFELElBQUksTUFBTSxDQUFDLFlBQVksRUFBRTtRQUN2QixNQUFNLE1BQU0sR0FBRyxTQUFTLENBQUE7UUFDeEIsT0FBTyxDQUFDLEdBQUcsQ0FBQyxNQUFNLEVBQUUsSUFBSSxFQUFFLEdBQUcsUUFB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
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { SpawnOptionsWithThrow, SpawnResult, StringKeyedDictionary } from './generalUtils.js';
|
|
2
|
+
export declare function copyEnv(sourcePath: string, destinationPath: string, overrideExistingDestinationValues?: boolean, suppressAddKeysMessages?: boolean): Promise<void>;
|
|
3
|
+
export declare function getEnvAsDictionary(envPath: string): StringKeyedDictionary;
|
|
4
|
+
export declare function dictionaryToEnvFileString(dict: StringKeyedDictionary): string;
|
|
5
|
+
export interface SpawnOptionsInternal extends SpawnOptionsWithThrow {
|
|
6
|
+
isLongRunning?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function spawnAsyncInternal(command: string, args?: string[], options?: SpawnOptionsInternal): Promise<SpawnResult>;
|
|
9
|
+
//# sourceMappingURL=generalUtilsInternal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generalUtilsInternal.d.ts","sourceRoot":"","sources":["../../src/generalUtilsInternal.ts"],"names":[],"mappings":"AAKA,OAAO,EAAc,qBAAqB,EAAE,WAAW,EAAE,qBAAqB,EAA8G,MAAM,mBAAmB,CAAA;AAOrN,wBAAsB,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,iCAAiC,UAAO,EAAE,uBAAuB,UAAQ,iBAuCnJ;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,qBAAqB,CAUzE;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,qBAAqB,GAAG,MAAM,CAE7E;AAED,MAAM,WAAW,oBAAqB,SAAQ,qBAAqB;IACjE,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AAED,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAC,WAAW,CAAC,CAmG/H"}
|