@mikeyt23/node-cli-utils 1.2.4 → 1.3.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.
Files changed (3) hide show
  1. package/README.md +6 -1
  2. package/index.js +168 -20
  3. package/package.json +1 -2
package/README.md CHANGED
@@ -6,10 +6,15 @@ Simple node cli utils to:
6
6
  - Copy env template file to env file
7
7
  - Sync new env values from template to "real" env files
8
8
  - Manage all env values in root files that then get copied to other locations
9
+ - Dotnet helper methods:
10
+ - Pack and publish a NuGet package
11
+ - Wrapper commands to install or update dotnet ef tool
12
+ - Wrapper commands to spawn dotnet run and publish
13
+ - Helper methods for DB migration commands (see [mikey-t/db-migrations-dotnet](https://github.com/mikey-t/db-migrations-dotnet))
9
14
 
10
15
  # Env Utility Function Notes
11
16
 
12
- A project can have multiple locations that requires the same env values. For example, you might have a database that needs to be used by a docker project, your main app and also a db migration project. Rather than having to remember where each of the .env files is, we want to use the root directory and overwrite subdirectory env files with the ones in the root. We also want to add new `.env.template` values to the root `.env` files and propagate them to subdirectories - all without having to know about anything about any of the .env files except the ones in the root of the project.
17
+ A project can have multiple locations that require the same env values. For example, you might have a database that needs to be used by a docker project, your main app and also a db migration project. Rather than having to remember where each of the .env files is, we want to use the root directory and overwrite subdirectory env files with the ones in the root. We also want to add new `.env.template` values to the root `.env` files and propagate them to subdirectories - all without having to know about anything about any of the .env files except the ones in the root of the project.
13
18
 
14
19
  # Examples
15
20
 
package/index.js CHANGED
@@ -1,8 +1,7 @@
1
1
  const fs = require('fs')
2
- const fse = require('fs-extra')
3
2
  const fsp = require('fs').promises
4
3
  const which = require('which')
5
- const { spawn, spawnSync } = require('child_process')
4
+ const {spawn, spawnSync} = require('child_process')
6
5
  const path = require('path')
7
6
  const tar = require('tar')
8
7
 
@@ -10,7 +9,7 @@ const defaultSpawnOptions = {
10
9
  shell: true,
11
10
  stdio: ['ignore', 'inherit', 'inherit']
12
11
  }
13
- const spawnOptionsWithInput = { ...defaultSpawnOptions, stdio: 'inherit' }
12
+ const defaultSpawnOptionsWithInput = {...defaultSpawnOptions, stdio: 'inherit'}
14
13
 
15
14
  function waitForProcess(childProcess) {
16
15
  return new Promise((resolve, reject) => {
@@ -35,13 +34,12 @@ async function overwriteEnvFile(fromPath, toPath) {
35
34
  await copyEnv(fromPath, toPath)
36
35
  }
37
36
 
38
-
39
37
  async function throwIfDockerNotRunning() {
40
38
  if (!which.sync('docker')) {
41
39
  throw Error('docker command not found')
42
40
  }
43
41
 
44
- let childProcess = spawnSync('docker', ['info'], { encoding: 'utf8' })
42
+ let childProcess = spawnSync('docker', ['info'], {encoding: 'utf8'})
45
43
  if (childProcess.error) {
46
44
  throw childProcess.error
47
45
  }
@@ -53,7 +51,7 @@ async function throwIfDockerNotRunning() {
53
51
  async function bashIntoRunningDockerContainer(containerNamePartial, entryPoint = 'bash') {
54
52
  await throwIfDockerNotRunning()
55
53
 
56
- let childProcess = spawnSync('docker', ['container', 'ls'], { encoding: 'utf8' })
54
+ let childProcess = spawnSync('docker', ['container', 'ls'], {encoding: 'utf8'})
57
55
  if (childProcess.error) {
58
56
  throw childProcess.error
59
57
  }
@@ -75,13 +73,13 @@ async function bashIntoRunningDockerContainer(containerNamePartial, entryPoint =
75
73
  console.log('full container name: ' + containerName)
76
74
 
77
75
  const args = ['exec', '-it', containerName, entryPoint]
78
- return waitForProcess(spawn('docker', args, spawnOptionsWithInput))
76
+ return waitForProcess(spawn('docker', args, defaultSpawnOptionsWithInput))
79
77
  }
80
78
 
81
79
  async function dockerContainerIsRunning(containerNamePartial) {
82
80
  await throwIfDockerNotRunning()
83
81
 
84
- let childProcess = spawnSync('docker', ['container', 'ls'], { encoding: 'utf8' })
82
+ let childProcess = spawnSync('docker', ['container', 'ls'], {encoding: 'utf8'})
85
83
  if (childProcess.error) {
86
84
  throw childProcess.error
87
85
  }
@@ -176,8 +174,10 @@ async function createTarball(directoryToTarball, outputDirectory, tarballName, c
176
174
  console.log('directory to create tarball from: ' + directoryToTarball)
177
175
  console.log('output will be: ' + tarballPath)
178
176
 
179
- if (!fs.existsSync(directoryToTarball)) {
180
- throw new Error('error: dirToTarball directory does not exist')
177
+ let normalizedDirectoryToTarball = !!cwd ? path.join(cwd, directoryToTarball) : directoryToTarball
178
+
179
+ if (!fs.existsSync(normalizedDirectoryToTarball)) {
180
+ throw new Error('error: dirToTarball directory does not exist: ' + normalizedDirectoryToTarball)
181
181
  }
182
182
 
183
183
  if (!fs.existsSync(outputDirectory)) {
@@ -187,21 +187,21 @@ async function createTarball(directoryToTarball, outputDirectory, tarballName, c
187
187
  fs.unlinkSync(tarballPath)
188
188
  }
189
189
  }
190
-
191
- let options = { gzip: true, file: tarballPath }
190
+
191
+ let options = {gzip: true, file: tarballPath}
192
192
  if (!!cwd) {
193
193
  options.cwd = cwd
194
194
  }
195
-
195
+
196
196
  await tar.c(options, [directoryToTarball])
197
197
  }
198
198
 
199
- async function dockerCompose(command, projectName, dockerRelativeDirectory, detached = false) {
199
+ async function dockerCompose(command, projectName, dockerRelativeDirectory = 'docker', detached = false) {
200
200
  if (!projectName || projectName.length === 0) {
201
201
  throw new Error('projectName is required')
202
202
  }
203
203
 
204
- const dockerRelativeDir = dockerRelativeDirectory || 'docker'
204
+ const dockerRelativeDir = dockerRelativeDirectory || './'
205
205
  const dockerWorkingDir = path.join(process.cwd(), dockerRelativeDir)
206
206
 
207
207
  if (!fs.existsSync(dockerWorkingDir)) {
@@ -210,7 +210,7 @@ async function dockerCompose(command, projectName, dockerRelativeDirectory, deta
210
210
 
211
211
  await throwIfDockerNotRunning()
212
212
 
213
- const dockerSpawnOptions = { ...defaultSpawnOptions, cwd: dockerWorkingDir }
213
+ const dockerSpawnOptions = {...defaultSpawnOptions, cwd: dockerWorkingDir, stdio: 'inherit'}
214
214
 
215
215
  let args = ['--project-name', projectName, command]
216
216
  if (detached) {
@@ -221,22 +221,161 @@ async function dockerCompose(command, projectName, dockerRelativeDirectory, deta
221
221
  }
222
222
 
223
223
  async function dockerDepsUp(projectName, dockerRelativeDirectory) {
224
- return dockerCompose('up', projectName, dockerRelativeDirectory)
224
+ return await dockerCompose('up', projectName, dockerRelativeDirectory)
225
225
  }
226
226
 
227
227
  async function dockerDepsUpDetached(projectName, dockerRelativeDirectory) {
228
- return dockerCompose('up', projectName, dockerRelativeDirectory, true)
228
+ return await dockerCompose('up', projectName, dockerRelativeDirectory, true)
229
229
  }
230
230
 
231
231
  async function dockerDepsDown(projectName, dockerRelativeDirectory) {
232
- return dockerCompose('down', projectName, dockerRelativeDirectory)
232
+ return await dockerCompose('down', projectName, dockerRelativeDirectory)
233
233
  }
234
234
 
235
235
  async function dockerDepsStop(projectName, dockerRelativeDirectory) {
236
- return dockerCompose('stop', projectName, dockerRelativeDirectory)
236
+ return await dockerCompose('stop', projectName, dockerRelativeDirectory)
237
+ }
238
+
239
+ async function dotnetBuild(release = true) {
240
+ let args = ['build']
241
+ if (release) {
242
+ args.push('-c', 'Release')
243
+ }
244
+
245
+ return waitForProcess(spawn('dotnet', args, defaultSpawnOptions))
246
+ }
247
+
248
+ async function dotnetPack(projectDirectoryPath, release = true) {
249
+ if (!projectDirectoryPath) {
250
+ throw Error('projectDirectoryPath param is required')
251
+ }
252
+
253
+ let args = ['pack']
254
+ if (release === true) {
255
+ args.push('-c', 'Release')
256
+ }
257
+
258
+ const spawnOptions = {...defaultSpawnOptions, cwd: projectDirectoryPath}
259
+ logCommand('dotnet', args, spawnOptions)
260
+ await waitForProcess(spawn('dotnet', args, spawnOptions))
261
+ }
262
+
263
+ async function dotnetNugetPublish(projectDirectoryPath, csprojFilename, release = true, nugetSource = 'https://api.nuget.org/v3/index.json') {
264
+ const apiKey = process.env.NUGET_API_KEY
265
+ if (!apiKey) {
266
+ throw Error('env var NUGET_API_KEY is required')
267
+ }
268
+
269
+ const packageDir = path.join(projectDirectoryPath, release ? 'bin/Release' : 'bin/Debug')
270
+
271
+ const packageName = await getPackageName(projectDirectoryPath, csprojFilename)
272
+ console.log('publishing package ' + packageName)
273
+ const spawnOptions = {...defaultSpawnOptions, cwd: packageDir}
274
+ await waitForProcess(spawn('dotnet', [
275
+ 'nuget',
276
+ 'push',
277
+ packageName,
278
+ '--api-key',
279
+ apiKey,
280
+ '--source',
281
+ nugetSource], spawnOptions))
282
+ }
283
+
284
+ async function getPackageName(projectPath, csprojFilename) {
285
+ const namespace = csprojFilename.substring(0, csprojFilename.indexOf('.csproj'))
286
+ const csprojPath = path.join(projectPath, csprojFilename)
287
+ const csproj = fs.readFileSync(csprojPath, 'utf-8')
288
+ const versionTag = '<PackageVersion>'
289
+ const xmlVersionTagIndex = csproj.indexOf(versionTag)
290
+ const versionStartIndex = xmlVersionTagIndex + versionTag.length
291
+ const versionStopIndex = csproj.indexOf('<', versionStartIndex)
292
+ const version = csproj.substring(versionStartIndex, versionStopIndex)
293
+ return `${namespace}.${version}.nupkg`
294
+ }
295
+
296
+ function logCommand(command, args, spawnOptions) {
297
+ console.log('running command: ' + `${command} ${args.join(' ')}`)
298
+ console.log('with spawn options: ' + JSON.stringify(spawnOptions))
299
+ }
300
+
301
+ async function dotnetDllCommand(relativeDllPath, argsArray, cwd = null, useStdin = false) {
302
+ throwIfRequiredIsFalsy(relativeDllPath, 'relativeDllPath')
303
+ throwIfRequiredArrayIsFalsyOrEmpty(argsArray, 'argsArray')
304
+
305
+ let args = [relativeDllPath, ...argsArray]
306
+
307
+ let spawnOptions = {...defaultSpawnOptions}
308
+ if (cwd !== null) {
309
+ spawnOptions = {...spawnOptions, cwd: cwd}
310
+ }
311
+ if (useStdin) {
312
+ spawnOptions = {...spawnOptions, stdio: 'inherit'}
313
+ }
314
+
315
+ return waitForProcess(spawn('dotnet', args, spawnOptions))
316
+ }
317
+
318
+ async function dotnetPublish(cwd = null, outputDir = 'publish') {
319
+ let spawnOptions = {...defaultSpawnOptions}
320
+ if (!!cwd) {
321
+ spawnOptions = {...spawnOptions, cwd: cwd}
322
+ }
323
+ if (!outputDir) {
324
+ outputDir = 'publish'
325
+ }
326
+ let args = ['publish', '-o', outputDir]
327
+ return waitForProcess(spawn('dotnet', args, spawnOptions))
328
+ }
329
+
330
+ async function dotnetDbMigrationsList(dbContextName, relativeDbMigratorDirectoryPath) {
331
+ throwIfRequiredIsFalsy(dbContextName, 'dbContextName')
332
+ throwIfRequiredIsFalsy(relativeDbMigratorDirectoryPath, 'relativeDbMigratorDirectoryPath')
333
+ let spawnOptions = {...defaultSpawnOptions, cwd: relativeDbMigratorDirectoryPath}
334
+ return waitForProcess(spawn('dotnet', ['ef', 'migrations', 'list', '--context', dbContextName], spawnOptions))
335
+ }
336
+
337
+ async function dotnetDbMigrate(dbContextName, relativeDbMigratorDirectoryPath, migrationName = '') {
338
+ throwIfRequiredIsFalsy(dbContextName, 'dbContextName')
339
+ throwIfRequiredIsFalsy(relativeDbMigratorDirectoryPath, 'relativeDbMigratorDirectoryPath')
340
+ let args = ['ef', 'database', 'update']
341
+ if (!!migrationName) {
342
+ args.push(migrationName)
343
+ }
344
+ args = [...args, '--context', dbContextName]
345
+ let spawnOptions = {...defaultSpawnOptions, cwd: relativeDbMigratorDirectoryPath}
346
+ return waitForProcess(spawn('dotnet', args, spawnOptions))
347
+ }
348
+
349
+ async function dotnetDbAddMigration(dbContextName, relativeDbMigratorDirectoryPath, migrationName) {
350
+ throwIfRequiredIsFalsy(dbContextName, 'dbContextName')
351
+ throwIfRequiredIsFalsy(relativeDbMigratorDirectoryPath, 'relativeDbMigratorDirectoryPath')
352
+ throwIfRequiredIsFalsy(migrationName, 'migrationName')
353
+ let args = ['ef', 'migrations', 'add', migrationName, '--context', dbContextName, '-o', `Migrations/${dbContextName}Migrations`]
354
+ let spawnOptions = {...defaultSpawnOptions, cwd: relativeDbMigratorDirectoryPath}
355
+ return waitForProcess(spawn('dotnet', args, spawnOptions))
356
+ }
357
+
358
+ async function dotnetDbRemoveMigration(dbContextName, relativeDbMigratorDirectoryPath) {
359
+ throwIfRequiredIsFalsy(dbContextName, 'dbContextName')
360
+ throwIfRequiredIsFalsy(relativeDbMigratorDirectoryPath, 'relativeDbMigratorDirectoryPath')
361
+ let spawnOptions = {...defaultSpawnOptions, cwd: relativeDbMigratorDirectoryPath}
362
+ return waitForProcess(spawn('dotnet', ['ef', 'migrations', 'remove', '--context', dbContextName], spawnOptions))
363
+ }
364
+
365
+ function throwIfRequiredIsFalsy(requiredArg, argName) {
366
+ if (!requiredArg) {
367
+ throw Error(`${argName} is required`)
368
+ }
369
+ }
370
+
371
+ function throwIfRequiredArrayIsFalsyOrEmpty(requiredArrayArg, argName) {
372
+ if (!requiredArrayArg || requiredArrayArg.length === 0 || !Array.isArray(requiredArrayArg)) {
373
+ throw Error(`${argName} array is required`)
374
+ }
237
375
  }
238
376
 
239
377
  exports.defaultSpawnOptions = defaultSpawnOptions
378
+ exports.defaultSpawnOptionsWithInput = defaultSpawnOptionsWithInput
240
379
  exports.waitForProcess = waitForProcess
241
380
  exports.copyNewEnvValues = copyNewEnvValues
242
381
  exports.overwriteEnvFile = overwriteEnvFile
@@ -248,3 +387,12 @@ exports.dockerDepsUp = dockerDepsUp
248
387
  exports.dockerDepsUpDetached = dockerDepsUpDetached
249
388
  exports.dockerDepsDown = dockerDepsDown
250
389
  exports.dockerDepsStop = dockerDepsStop
390
+ exports.dotnetBuild = dotnetBuild
391
+ exports.dotnetPack = dotnetPack
392
+ exports.dotnetNugetPublish = dotnetNugetPublish
393
+ exports.dotnetDllCommand = dotnetDllCommand
394
+ exports.dotnetPublish = dotnetPublish
395
+ exports.dotnetDbMigrationsList = dotnetDbMigrationsList
396
+ exports.dotnetDbMigrate = dotnetDbMigrate
397
+ exports.dotnetDbAddMigration = dotnetDbAddMigration
398
+ exports.dotnetDbRemoveMigration = dotnetDbRemoveMigration
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikeyt23/node-cli-utils",
3
- "version": "1.2.4",
3
+ "version": "1.3.0",
4
4
  "description": "Some node cli utility functions",
5
5
  "author": "Mike Thompson",
6
6
  "license": "MIT",
@@ -15,7 +15,6 @@
15
15
  },
16
16
  "main": "index.js",
17
17
  "dependencies": {
18
- "fs-extra": "^10.0.0",
19
18
  "tar": "^6.1.11",
20
19
  "which": "^2.0.2"
21
20
  }