@mikeyt23/node-cli-utils 1.3.1 → 1.4.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.
Files changed (2) hide show
  1. package/index.js +181 -50
  2. package/package.json +4 -3
package/index.js CHANGED
@@ -1,7 +1,8 @@
1
1
  const fs = require('fs')
2
2
  const fsp = require('fs').promises
3
+ const fse = require('fs-extra')
3
4
  const which = require('which')
4
- const {spawn, spawnSync} = require('child_process')
5
+ const { spawn, spawnSync } = require('child_process')
5
6
  const path = require('path')
6
7
  const tar = require('tar')
7
8
 
@@ -9,7 +10,7 @@ const defaultSpawnOptions = {
9
10
  shell: true,
10
11
  stdio: ['ignore', 'inherit', 'inherit']
11
12
  }
12
- const defaultSpawnOptionsWithInput = {...defaultSpawnOptions, stdio: 'inherit'}
13
+ const defaultSpawnOptionsWithInput = { ...defaultSpawnOptions, stdio: 'inherit' }
13
14
 
14
15
  function waitForProcess(childProcess) {
15
16
  return new Promise((resolve, reject) => {
@@ -39,7 +40,7 @@ async function throwIfDockerNotRunning() {
39
40
  throw Error('docker command not found')
40
41
  }
41
42
 
42
- let childProcess = spawnSync('docker', ['info'], {encoding: 'utf8'})
43
+ let childProcess = spawnSync('docker', ['info'], { encoding: 'utf8' })
43
44
  if (childProcess.error) {
44
45
  throw childProcess.error
45
46
  }
@@ -51,7 +52,7 @@ async function throwIfDockerNotRunning() {
51
52
  async function bashIntoRunningDockerContainer(containerNamePartial, entryPoint = 'bash') {
52
53
  await throwIfDockerNotRunning()
53
54
 
54
- let childProcess = spawnSync('docker', ['container', 'ls'], {encoding: 'utf8'})
55
+ let childProcess = spawnSync('docker', ['container', 'ls'], { encoding: 'utf8' })
55
56
  if (childProcess.error) {
56
57
  throw childProcess.error
57
58
  }
@@ -79,7 +80,7 @@ async function bashIntoRunningDockerContainer(containerNamePartial, entryPoint =
79
80
  async function dockerContainerIsRunning(containerNamePartial) {
80
81
  await throwIfDockerNotRunning()
81
82
 
82
- let childProcess = spawnSync('docker', ['container', 'ls'], {encoding: 'utf8'})
83
+ let childProcess = spawnSync('docker', ['container', 'ls'], { encoding: 'utf8' })
83
84
  if (childProcess.error) {
84
85
  throw childProcess.error
85
86
  }
@@ -159,41 +160,53 @@ exports.defaultSpawnOptions = {
159
160
  }
160
161
 
161
162
  async function createTarball(directoryToTarball, outputDirectory, tarballName, cwd = '') {
162
- if (!directoryToTarball || directoryToTarball.length === 0) {
163
- throw new Error('directoryToTarball is required')
164
- }
165
- if (!outputDirectory || outputDirectory.length === 0) {
166
- throw new Error('outputDirectory is required')
167
- }
168
- if (!tarballName || tarballName.length === 0) {
169
- throw new Error('tarballName is required')
170
- }
163
+ return new Promise((resolve, reject) => {
164
+ try {
165
+ if (!directoryToTarball || directoryToTarball.length === 0) {
166
+ throw new Error('directoryToTarball is required')
167
+ }
168
+ if (!outputDirectory || outputDirectory.length === 0) {
169
+ throw new Error('outputDirectory is required')
170
+ }
171
+ if (!tarballName || tarballName.length === 0) {
172
+ throw new Error('tarballName is required')
173
+ }
171
174
 
172
- const tarballPath = path.join(outputDirectory, tarballName)
175
+ const tarballPath = path.join(outputDirectory, tarballName)
173
176
 
174
- console.log('directory to create tarball from: ' + directoryToTarball)
175
- console.log('output will be: ' + tarballPath)
177
+ console.log('directory to create tarball from: ' + directoryToTarball)
178
+ console.log('output will be: ' + tarballPath)
176
179
 
177
- let normalizedDirectoryToTarball = !!cwd ? path.join(cwd, directoryToTarball) : directoryToTarball
180
+ let normalizedDirectoryToTarball = !!cwd ? path.join(cwd, directoryToTarball) : directoryToTarball
178
181
 
179
- if (!fs.existsSync(normalizedDirectoryToTarball)) {
180
- throw new Error('error: dirToTarball directory does not exist: ' + normalizedDirectoryToTarball)
181
- }
182
+ if (!fs.existsSync(normalizedDirectoryToTarball)) {
183
+ throw new Error('error: dirToTarball directory does not exist: ' + normalizedDirectoryToTarball)
184
+ }
182
185
 
183
- if (!fs.existsSync(outputDirectory)) {
184
- fs.mkdirSync(outputDirectory)
185
- } else {
186
- if (fs.existsSync(tarballPath)) {
187
- fs.unlinkSync(tarballPath)
188
- }
189
- }
186
+ if (!fs.existsSync(outputDirectory)) {
187
+ fs.mkdirSync(outputDirectory)
188
+ } else {
189
+ if (fs.existsSync(tarballPath)) {
190
+ fs.unlinkSync(tarballPath)
191
+ }
192
+ }
190
193
 
191
- let options = {gzip: true, file: tarballPath}
192
- if (!!cwd) {
193
- options.cwd = cwd
194
- }
194
+ let options = { gzip: true, file: tarballPath }
195
+ if (!!cwd) {
196
+ options.cwd = cwd
197
+ }
195
198
 
196
- await tar.c(options, [directoryToTarball])
199
+ tar.c(options, [directoryToTarball])
200
+ .then(() => {
201
+ resolve()
202
+ })
203
+ .catch(err => {
204
+ reject(err)
205
+ })
206
+ } catch (err) {
207
+ reject(err)
208
+ }
209
+ })
197
210
  }
198
211
 
199
212
  async function dockerCompose(command, projectName, dockerRelativeDirectory = 'docker', detached = false) {
@@ -210,7 +223,7 @@ async function dockerCompose(command, projectName, dockerRelativeDirectory = 'do
210
223
 
211
224
  await throwIfDockerNotRunning()
212
225
 
213
- const dockerSpawnOptions = {...defaultSpawnOptions, cwd: dockerWorkingDir, stdio: 'inherit'}
226
+ const dockerSpawnOptions = { ...defaultSpawnOptions, cwd: dockerWorkingDir, stdio: 'inherit' }
214
227
 
215
228
  let args = ['--project-name', projectName, command]
216
229
  if (detached) {
@@ -255,7 +268,7 @@ async function dotnetPack(projectDirectoryPath, release = true) {
255
268
  args.push('-c', 'Release')
256
269
  }
257
270
 
258
- const spawnOptions = {...defaultSpawnOptions, cwd: projectDirectoryPath}
271
+ const spawnOptions = { ...defaultSpawnOptions, cwd: projectDirectoryPath }
259
272
  logCommand('dotnet', args, spawnOptions)
260
273
  await waitForProcess(spawn('dotnet', args, spawnOptions))
261
274
  }
@@ -270,7 +283,7 @@ async function dotnetNugetPublish(projectDirectoryPath, csprojFilename, release
270
283
 
271
284
  const packageName = await getPackageName(projectDirectoryPath, csprojFilename)
272
285
  console.log('publishing package ' + packageName)
273
- const spawnOptions = {...defaultSpawnOptions, cwd: packageDir}
286
+ const spawnOptions = { ...defaultSpawnOptions, cwd: packageDir }
274
287
  await waitForProcess(spawn('dotnet', [
275
288
  'nuget',
276
289
  'push',
@@ -304,21 +317,21 @@ async function dotnetDllCommand(relativeDllPath, argsArray, cwd = null, useStdin
304
317
 
305
318
  let args = [relativeDllPath, ...argsArray]
306
319
 
307
- let spawnOptions = {...defaultSpawnOptions}
320
+ let spawnOptions = { ...defaultSpawnOptions }
308
321
  if (cwd !== null) {
309
- spawnOptions = {...spawnOptions, cwd: cwd}
322
+ spawnOptions = { ...spawnOptions, cwd: cwd }
310
323
  }
311
324
  if (useStdin) {
312
- spawnOptions = {...spawnOptions, stdio: 'inherit'}
325
+ spawnOptions = { ...spawnOptions, stdio: 'inherit' }
313
326
  }
314
327
 
315
328
  return waitForProcess(spawn('dotnet', args, spawnOptions))
316
329
  }
317
330
 
318
331
  async function dotnetPublish(cwd = null, outputDir = 'publish') {
319
- let spawnOptions = {...defaultSpawnOptions}
332
+ let spawnOptions = { ...defaultSpawnOptions }
320
333
  if (!!cwd) {
321
- spawnOptions = {...spawnOptions, cwd: cwd}
334
+ spawnOptions = { ...spawnOptions, cwd: cwd }
322
335
  }
323
336
  if (!outputDir) {
324
337
  outputDir = 'publish'
@@ -330,7 +343,7 @@ async function dotnetPublish(cwd = null, outputDir = 'publish') {
330
343
  async function dotnetDbMigrationsList(dbContextName, relativeDbMigratorDirectoryPath) {
331
344
  throwIfRequiredIsFalsy(dbContextName, 'dbContextName')
332
345
  throwIfRequiredIsFalsy(relativeDbMigratorDirectoryPath, 'relativeDbMigratorDirectoryPath')
333
- let spawnOptions = {...defaultSpawnOptions, cwd: relativeDbMigratorDirectoryPath}
346
+ let spawnOptions = { ...defaultSpawnOptions, cwd: relativeDbMigratorDirectoryPath }
334
347
  return waitForProcess(spawn('dotnet', ['ef', 'migrations', 'list', '--context', dbContextName], spawnOptions))
335
348
  }
336
349
 
@@ -342,7 +355,7 @@ async function dotnetDbMigrate(dbContextName, relativeDbMigratorDirectoryPath, m
342
355
  args.push(migrationName)
343
356
  }
344
357
  args = [...args, '--context', dbContextName]
345
- let spawnOptions = {...defaultSpawnOptions, cwd: relativeDbMigratorDirectoryPath}
358
+ let spawnOptions = { ...defaultSpawnOptions, cwd: relativeDbMigratorDirectoryPath }
346
359
  return waitForProcess(spawn('dotnet', args, spawnOptions))
347
360
  }
348
361
 
@@ -354,7 +367,7 @@ async function dotnetDbAddMigration(dbContextName, relativeDbMigratorDirectoryPa
354
367
  const migrationsOutputDir = `Migrations/${dbContextName}Migrations`
355
368
 
356
369
  let args = ['ef', 'migrations', 'add', migrationName, '--context', dbContextName, '-o', migrationsOutputDir]
357
- let spawnOptions = {...defaultSpawnOptions, cwd: relativeDbMigratorDirectoryPath}
370
+ let spawnOptions = { ...defaultSpawnOptions, cwd: relativeDbMigratorDirectoryPath }
358
371
  await waitForProcess(spawn('dotnet', args, spawnOptions))
359
372
 
360
373
  if (withBoilerplate) {
@@ -370,7 +383,7 @@ async function dotnetDbAddMigrationBoilerplate(dbContextName, relativeDbMigrator
370
383
 
371
384
  console.log(`Checking for generated C# file in directory: ${dirPath}`)
372
385
 
373
- const filenames = fs.readdirSync(dirPath).filter(fn => fn.endsWith(`${migrationName}.cs`));
386
+ const filenames = fs.readdirSync(dirPath).filter(fn => fn.endsWith(`${migrationName}.cs`))
374
387
  if (!filenames || filenames.length === 0) {
375
388
  console.log(`Unable to add boilerplate - could not find auto generated file in directory: ${dirPath}`)
376
389
  }
@@ -388,7 +401,7 @@ async function dotnetDbAddMigrationBoilerplate(dbContextName, relativeDbMigrator
388
401
  const upLine = `MigrationScriptRunner.RunScript(migrationBuilder, "${migrationName}.sql");`
389
402
  const downLine = `MigrationScriptRunner.RunScript(migrationBuilder, "${migrationName}_Down.sql");`
390
403
 
391
- const fileContents = await fsp.readFile(filePath, {encoding: 'utf8'})
404
+ const fileContents = await fsp.readFile(filePath, { encoding: 'utf8' })
392
405
  let lines = fileContents.replaceAll('\r', '').split('\n')
393
406
 
394
407
  let newLines = []
@@ -429,7 +442,7 @@ async function dotnetDbAddMigrationBoilerplate(dbContextName, relativeDbMigrator
429
442
 
430
443
  const newFileContents = newLines.join('\n')
431
444
 
432
- await fsp.writeFile(filePath, newFileContents, {encoding: 'utf8'})
445
+ await fsp.writeFile(filePath, newFileContents, { encoding: 'utf8' })
433
446
 
434
447
  console.log(`Updated file with boilerplate - please ensure it is correct: ${filePath}`)
435
448
 
@@ -441,13 +454,13 @@ async function dotnetDbAddMigrationBoilerplate(dbContextName, relativeDbMigrator
441
454
  console.log(` - ${downScriptPath}`)
442
455
 
443
456
  if (!fs.existsSync(upScriptPath)) {
444
- await fsp.writeFile(upScriptPath, '', {encoding: 'utf8'})
457
+ await fsp.writeFile(upScriptPath, '', { encoding: 'utf8' })
445
458
  } else {
446
459
  console.log('Skipping Up sql script (already exists)')
447
460
  }
448
461
 
449
462
  if (!fs.existsSync(downScriptPath)) {
450
- await fsp.writeFile(downScriptPath, '', {encoding: 'utf8'})
463
+ await fsp.writeFile(downScriptPath, '', { encoding: 'utf8' })
451
464
  } else {
452
465
  console.log('Skipping Down sql script (already exists)')
453
466
  }
@@ -456,7 +469,7 @@ async function dotnetDbAddMigrationBoilerplate(dbContextName, relativeDbMigrator
456
469
  async function dotnetDbRemoveMigration(dbContextName, relativeDbMigratorDirectoryPath) {
457
470
  throwIfRequiredIsFalsy(dbContextName, 'dbContextName')
458
471
  throwIfRequiredIsFalsy(relativeDbMigratorDirectoryPath, 'relativeDbMigratorDirectoryPath')
459
- let spawnOptions = {...defaultSpawnOptions, cwd: relativeDbMigratorDirectoryPath}
472
+ let spawnOptions = { ...defaultSpawnOptions, cwd: relativeDbMigratorDirectoryPath }
460
473
  return waitForProcess(spawn('dotnet', ['ef', 'migrations', 'remove', '--context', dbContextName], spawnOptions))
461
474
  }
462
475
 
@@ -472,6 +485,120 @@ function throwIfRequiredArrayIsFalsyOrEmpty(requiredArrayArg, argName) {
472
485
  }
473
486
  }
474
487
 
488
+ async function generateCertWithOpenSsl(url, outputDirectory = './cert') {
489
+ if (!url) {
490
+ throw Error('Param \'url\' is required.')
491
+ }
492
+
493
+ // Check if openssl is installed
494
+ let macOpenSslPath
495
+ if (process.platform !== 'darwin') {
496
+ if (!which.sync('openssl', { nothrow: true })) {
497
+ throw Error('openssl is required but was not found in the path')
498
+ }
499
+ } else {
500
+ console.log('*****************************************************************')
501
+ console.log('* Important: mac support requires openssl be installed via brew *')
502
+ console.log('*****************************************************************')
503
+
504
+ macOpenSslPath = `${getBrewOpensslPath()}/bin/openssl`
505
+ console.log(`openssl path: ${macOpenSslPath}`)
506
+ }
507
+
508
+ console.log('openssl is installed, continuing...')
509
+
510
+ fse.mkdirpSync(outputDirectory)
511
+
512
+ const keyName = url + '.key'
513
+ const crtName = url + '.crt'
514
+ const pfxName = url + '.pfx'
515
+
516
+ pfxPath = path.join(outputDirectory, pfxName)
517
+
518
+ if (fse.pathExistsSync(pfxPath)) {
519
+ throw Error(`File ${pfxPath} already exists. Delete this first if you want to generate a new version.`)
520
+ }
521
+
522
+ console.log(`attempting to generate cert ${pfxName}`)
523
+
524
+ const genCertSpawnArgs = { ...defaultSpawnOptions, cwd: outputDirectory }
525
+
526
+ const genKeyAndCrtArgs = `req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes -keyout ${keyName} -out ${crtName} -subj "/CN=${url}" -addext "subjectAltName=DNS:${url},IP:127.0.0.1"`.split(' ')
527
+
528
+ const cmd = process.platform !== 'darwin' ? 'openssl' : macOpenSslPath
529
+
530
+ console.log('cmd: ' + cmd)
531
+
532
+ await waitForProcess(spawn(cmd, genKeyAndCrtArgs, genCertSpawnArgs))
533
+
534
+ console.log('converting key and crt to pfx...')
535
+
536
+ const convertToPfxArgs = `pkcs12 -certpbe AES-256-CBC -export -out ${pfxName} -aes256 -inkey ${keyName} -in ${crtName} -password pass:`.split(' ')
537
+
538
+ await waitForProcess(spawn(cmd, convertToPfxArgs, genCertSpawnArgs))
539
+ }
540
+
541
+ function getBrewOpensslPath() {
542
+ let childProc = spawnSync('brew', ['--prefix', 'openssl'], { encoding: 'utf-8' })
543
+ if (childProc.error) {
544
+ throw Error('error attempting to find openssl installed by brew')
545
+ }
546
+
547
+ const output = childProc.stdout
548
+
549
+ if (!output || output.length === 0 || output.toLowerCase().startsWith('error')) {
550
+ throw Error('unexpected output while attempting to find openssl')
551
+ }
552
+
553
+ return output.replace('\n', '')
554
+ }
555
+
556
+ async function winInstallCert(urlOrCertFilename, relativeCertDirectoryPath = './cert') {
557
+ if (!urlOrCertFilename) {
558
+ throw Error('Param \'urlOrCertFilename\' is required.')
559
+ }
560
+
561
+ console.log('******************************\n* Requires admin permissions *\n******************************')
562
+
563
+ let certName = urlOrCertFilename.endsWith('.pfx') ? urlOrCertFilename : urlOrCertFilename + '.pfx'
564
+
565
+ const certPath = path.join(process.cwd(), relativeCertDirectoryPath, certName)
566
+
567
+ if (!fse.pathExistsSync(certPath)) {
568
+ throw Error(`File ${certPath} does not exist. Generate this first if you want to install it.`)
569
+ }
570
+
571
+ const psCommand = `$env:PSModulePath = [Environment]::GetEnvironmentVariable('PSModulePath', 'Machine'); Import-PfxCertificate -FilePath '${certPath}' -CertStoreLocation Cert:\\LocalMachine\\Root`
572
+
573
+ await waitForProcess(spawn('powershell', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', psCommand]))
574
+ }
575
+
576
+ async function winUninstallCert(urlOrSubject) {
577
+ if (!urlOrSubject) {
578
+ throw Error('Param \'urlOrSubject\' is required.')
579
+ }
580
+
581
+ console.log('******************************\n* Requires admin permissions *\n******************************')
582
+
583
+ const psCommand = `$env:PSModulePath = [Environment]::GetEnvironmentVariable('PSModulePath', 'Machine'); Get-ChildItem Cert:\\LocalMachine\\Root | Where-Object { $_.Subject -match '${urlOrSubject}' } | Remove-Item`
584
+
585
+ await waitForProcess(spawn('powershell', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', psCommand]))
586
+ }
587
+
588
+ function linuxInstallCert() {
589
+ const instructions = `
590
+ Automated linux cert install not supported (chrome does not use system certs without significant extra configuration).
591
+
592
+ Manual Instructions:
593
+ - In Chrome, go to chrome://settings/certificates
594
+ - Select Authorities -> import
595
+ - Select your generated .crt file (in the ./cert/ directory by default - if you haven't generated it, see the generateCertWithOpenSsl method)
596
+ - Check box for "Trust certificate for identifying websites"
597
+ - Click OK
598
+ - Reload site`
599
+ console.log(instructions)
600
+ }
601
+
475
602
  exports.defaultSpawnOptions = defaultSpawnOptions
476
603
  exports.defaultSpawnOptionsWithInput = defaultSpawnOptionsWithInput
477
604
  exports.waitForProcess = waitForProcess
@@ -494,3 +621,7 @@ exports.dotnetDbMigrationsList = dotnetDbMigrationsList
494
621
  exports.dotnetDbMigrate = dotnetDbMigrate
495
622
  exports.dotnetDbAddMigration = dotnetDbAddMigration
496
623
  exports.dotnetDbRemoveMigration = dotnetDbRemoveMigration
624
+ exports.generateCertWithOpenSsl = generateCertWithOpenSsl
625
+ exports.winInstallCert = winInstallCert
626
+ exports.winUninstallCert = winUninstallCert
627
+ exports.linuxInstallCert = linuxInstallCert
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikeyt23/node-cli-utils",
3
- "version": "1.3.1",
3
+ "version": "1.4.1",
4
4
  "description": "Some node cli utility functions",
5
5
  "author": "Mike Thompson",
6
6
  "license": "MIT",
@@ -15,7 +15,8 @@
15
15
  },
16
16
  "main": "index.js",
17
17
  "dependencies": {
18
- "tar": "^6.1.11",
19
- "which": "^2.0.2"
18
+ "fs-extra": "^11.1.1",
19
+ "tar": "^6.1.15",
20
+ "which": "^3.0.1"
20
21
  }
21
22
  }