@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.
- package/README.md +6 -1
- package/index.js +168 -20
- 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
|
|
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 {
|
|
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
|
|
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'], {
|
|
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'], {
|
|
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,
|
|
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'], {
|
|
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
|
-
|
|
180
|
-
|
|
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 = {
|
|
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 || '
|
|
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 = {
|
|
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.
|
|
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
|
}
|