@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
package/index.js
DELETED
|
@@ -1,627 +0,0 @@
|
|
|
1
|
-
const fs = require('fs')
|
|
2
|
-
const fsp = require('fs').promises
|
|
3
|
-
const fse = require('fs-extra')
|
|
4
|
-
const which = require('which')
|
|
5
|
-
const { spawn, spawnSync } = require('child_process')
|
|
6
|
-
const path = require('path')
|
|
7
|
-
const tar = require('tar')
|
|
8
|
-
|
|
9
|
-
const defaultSpawnOptions = {
|
|
10
|
-
shell: true,
|
|
11
|
-
stdio: ['ignore', 'inherit', 'inherit']
|
|
12
|
-
}
|
|
13
|
-
const defaultSpawnOptionsWithInput = { ...defaultSpawnOptions, stdio: 'inherit' }
|
|
14
|
-
|
|
15
|
-
function waitForProcess(childProcess) {
|
|
16
|
-
return new Promise((resolve, reject) => {
|
|
17
|
-
childProcess.once('exit', (returnCode) => {
|
|
18
|
-
if (returnCode === 0) {
|
|
19
|
-
resolve(returnCode)
|
|
20
|
-
} else {
|
|
21
|
-
reject(returnCode)
|
|
22
|
-
}
|
|
23
|
-
})
|
|
24
|
-
childProcess.once('error', (err) => {
|
|
25
|
-
reject(err)
|
|
26
|
-
})
|
|
27
|
-
})
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
async function copyNewEnvValues(fromPath, toPath) {
|
|
31
|
-
await copyEnv(fromPath, toPath, false)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
async function overwriteEnvFile(fromPath, toPath) {
|
|
35
|
-
await copyEnv(fromPath, toPath)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
async function throwIfDockerNotRunning() {
|
|
39
|
-
if (!which.sync('docker')) {
|
|
40
|
-
throw Error('docker command not found')
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
let childProcess = spawnSync('docker', ['info'], { encoding: 'utf8' })
|
|
44
|
-
if (childProcess.error) {
|
|
45
|
-
throw childProcess.error
|
|
46
|
-
}
|
|
47
|
-
if (!childProcess.stdout || childProcess.stdout.includes('ERROR: error during connect')) {
|
|
48
|
-
throw Error('docker is not running')
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
async function bashIntoRunningDockerContainer(containerNamePartial, entryPoint = 'bash') {
|
|
53
|
-
await throwIfDockerNotRunning()
|
|
54
|
-
|
|
55
|
-
let childProcess = spawnSync('docker', ['container', 'ls'], { encoding: 'utf8' })
|
|
56
|
-
if (childProcess.error) {
|
|
57
|
-
throw childProcess.error
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
let matchingLines = childProcess.stdout.split('\n').filter(line => line.includes(containerNamePartial))
|
|
61
|
-
|
|
62
|
-
if (!matchingLines || matchingLines.length === 0) {
|
|
63
|
-
throw Error('container is not running')
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (matchingLines.length > 1) {
|
|
67
|
-
throw Error('more than one container matches the provided containerNamePartial ' + containerNamePartial)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
let stringArray = matchingLines[0].split(/(\s+)/)
|
|
71
|
-
|
|
72
|
-
let containerName = stringArray[stringArray.length - 1]
|
|
73
|
-
|
|
74
|
-
console.log('full container name: ' + containerName)
|
|
75
|
-
|
|
76
|
-
const args = ['exec', '-it', containerName, entryPoint]
|
|
77
|
-
return waitForProcess(spawn('docker', args, defaultSpawnOptionsWithInput))
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
async function dockerContainerIsRunning(containerNamePartial) {
|
|
81
|
-
await throwIfDockerNotRunning()
|
|
82
|
-
|
|
83
|
-
let childProcess = spawnSync('docker', ['container', 'ls'], { encoding: 'utf8' })
|
|
84
|
-
if (childProcess.error) {
|
|
85
|
-
throw childProcess.error
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
let matchingLines = childProcess.stdout.split('\n').filter(l => l.includes(containerNamePartial))
|
|
89
|
-
|
|
90
|
-
return !!matchingLines && matchingLines.length > 0
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
async function copyEnv(fromPath, toPath, overrideAll = true) {
|
|
94
|
-
await ensureFile(fromPath, toPath)
|
|
95
|
-
|
|
96
|
-
let templateDict = getEnvDictionary(fromPath)
|
|
97
|
-
let envDict = getEnvDictionary(toPath)
|
|
98
|
-
|
|
99
|
-
// Determine what keys are missing from .env that are in template
|
|
100
|
-
let templateKeys = Object.keys(templateDict)
|
|
101
|
-
let envKeys = Object.keys(envDict)
|
|
102
|
-
let missingKeys = templateKeys.filter(k => !envKeys.includes(k))
|
|
103
|
-
|
|
104
|
-
if (missingKeys.length > 0) {
|
|
105
|
-
console.log(`Adding missing keys in ${toPath}: `, missingKeys)
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Merge missing values with existing
|
|
109
|
-
let newEnvDict = {}
|
|
110
|
-
for (const [key, value] of Object.entries(overrideAll ? templateDict : envDict)) {
|
|
111
|
-
newEnvDict[key] = value
|
|
112
|
-
}
|
|
113
|
-
for (const key of missingKeys) {
|
|
114
|
-
newEnvDict[key] = templateDict[key]
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// Sort
|
|
118
|
-
let newDictEntries = Object.entries(newEnvDict)
|
|
119
|
-
let newSortedEntries = newDictEntries.sort((a, b) => {
|
|
120
|
-
if (a < b) {
|
|
121
|
-
return -1
|
|
122
|
-
}
|
|
123
|
-
if (a > b) {
|
|
124
|
-
return 1
|
|
125
|
-
}
|
|
126
|
-
return 0
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
// Write to .env file
|
|
130
|
-
let newEnvFileContent = ''
|
|
131
|
-
for (let kvp of newSortedEntries) {
|
|
132
|
-
newEnvFileContent += `${kvp[0]}=${kvp[1]}\n`
|
|
133
|
-
}
|
|
134
|
-
await fsp.writeFile(toPath, newEnvFileContent)
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function getEnvDictionary(filePath) {
|
|
138
|
-
let dict = {}
|
|
139
|
-
fs.readFileSync(filePath).toString().split('\n').forEach(function (line) {
|
|
140
|
-
if (line && line.indexOf('=') !== -1) {
|
|
141
|
-
line = line.replace('\r', '').trim()
|
|
142
|
-
let parts = line.split('=')
|
|
143
|
-
dict[parts[0].trim()] = parts[1].trim()
|
|
144
|
-
}
|
|
145
|
-
})
|
|
146
|
-
return dict
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
async function ensureFile(fromPath, toPath) {
|
|
150
|
-
if (!fs.existsSync(toPath)) {
|
|
151
|
-
console.log('Creating new file ' + toPath)
|
|
152
|
-
await fsp.copyFile(fromPath, toPath)
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
exports.defaultSpawnOptions = {
|
|
157
|
-
shell: true,
|
|
158
|
-
cwd: __dirname,
|
|
159
|
-
stdio: ['ignore', 'inherit', 'inherit']
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
async function createTarball(directoryToTarball, outputDirectory, tarballName, cwd = '') {
|
|
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
|
-
}
|
|
174
|
-
|
|
175
|
-
const tarballPath = path.join(outputDirectory, tarballName)
|
|
176
|
-
|
|
177
|
-
console.log('directory to create tarball from: ' + directoryToTarball)
|
|
178
|
-
console.log('output will be: ' + tarballPath)
|
|
179
|
-
|
|
180
|
-
let normalizedDirectoryToTarball = !!cwd ? path.join(cwd, directoryToTarball) : directoryToTarball
|
|
181
|
-
|
|
182
|
-
if (!fs.existsSync(normalizedDirectoryToTarball)) {
|
|
183
|
-
throw new Error('error: dirToTarball directory does not exist: ' + normalizedDirectoryToTarball)
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
if (!fs.existsSync(outputDirectory)) {
|
|
187
|
-
fs.mkdirSync(outputDirectory)
|
|
188
|
-
} else {
|
|
189
|
-
if (fs.existsSync(tarballPath)) {
|
|
190
|
-
fs.unlinkSync(tarballPath)
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
let options = { gzip: true, file: tarballPath }
|
|
195
|
-
if (!!cwd) {
|
|
196
|
-
options.cwd = cwd
|
|
197
|
-
}
|
|
198
|
-
|
|
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
|
-
})
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
async function dockerCompose(command, projectName, dockerRelativeDirectory = 'docker', detached = false) {
|
|
213
|
-
if (!projectName || projectName.length === 0) {
|
|
214
|
-
throw new Error('projectName is required')
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
const dockerRelativeDir = dockerRelativeDirectory || './'
|
|
218
|
-
const dockerWorkingDir = path.join(process.cwd(), dockerRelativeDir)
|
|
219
|
-
|
|
220
|
-
if (!fs.existsSync(dockerWorkingDir)) {
|
|
221
|
-
throw new Error('Docker directory does not exist: ' + dockerWorkingDir)
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
await throwIfDockerNotRunning()
|
|
225
|
-
|
|
226
|
-
const dockerSpawnOptions = { ...defaultSpawnOptions, cwd: dockerWorkingDir, stdio: 'inherit' }
|
|
227
|
-
|
|
228
|
-
let args = ['--project-name', projectName, command]
|
|
229
|
-
if (detached) {
|
|
230
|
-
args.push('-d')
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
return waitForProcess(spawn('docker-compose', args, dockerSpawnOptions))
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
async function dockerDepsUp(projectName, dockerRelativeDirectory) {
|
|
237
|
-
return await dockerCompose('up', projectName, dockerRelativeDirectory)
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
async function dockerDepsUpDetached(projectName, dockerRelativeDirectory) {
|
|
241
|
-
return await dockerCompose('up', projectName, dockerRelativeDirectory, true)
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
async function dockerDepsDown(projectName, dockerRelativeDirectory) {
|
|
245
|
-
return await dockerCompose('down', projectName, dockerRelativeDirectory)
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
async function dockerDepsStop(projectName, dockerRelativeDirectory) {
|
|
249
|
-
return await dockerCompose('stop', projectName, dockerRelativeDirectory)
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
async function dotnetBuild(release = true) {
|
|
253
|
-
let args = ['build']
|
|
254
|
-
if (release) {
|
|
255
|
-
args.push('-c', 'Release')
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
return waitForProcess(spawn('dotnet', args, defaultSpawnOptions))
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
async function dotnetPack(projectDirectoryPath, release = true) {
|
|
262
|
-
if (!projectDirectoryPath) {
|
|
263
|
-
throw Error('projectDirectoryPath param is required')
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
let args = ['pack']
|
|
267
|
-
if (release === true) {
|
|
268
|
-
args.push('-c', 'Release')
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
const spawnOptions = { ...defaultSpawnOptions, cwd: projectDirectoryPath }
|
|
272
|
-
logCommand('dotnet', args, spawnOptions)
|
|
273
|
-
await waitForProcess(spawn('dotnet', args, spawnOptions))
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
async function dotnetNugetPublish(projectDirectoryPath, csprojFilename, release = true, nugetSource = 'https://api.nuget.org/v3/index.json') {
|
|
277
|
-
const apiKey = process.env.NUGET_API_KEY
|
|
278
|
-
if (!apiKey) {
|
|
279
|
-
throw Error('env var NUGET_API_KEY is required')
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
const packageDir = path.join(projectDirectoryPath, release ? 'bin/Release' : 'bin/Debug')
|
|
283
|
-
|
|
284
|
-
const packageName = await getPackageName(projectDirectoryPath, csprojFilename)
|
|
285
|
-
console.log('publishing package ' + packageName)
|
|
286
|
-
const spawnOptions = { ...defaultSpawnOptions, cwd: packageDir }
|
|
287
|
-
await waitForProcess(spawn('dotnet', [
|
|
288
|
-
'nuget',
|
|
289
|
-
'push',
|
|
290
|
-
packageName,
|
|
291
|
-
'--api-key',
|
|
292
|
-
apiKey,
|
|
293
|
-
'--source',
|
|
294
|
-
nugetSource], spawnOptions))
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
async function getPackageName(projectPath, csprojFilename) {
|
|
298
|
-
const namespace = csprojFilename.substring(0, csprojFilename.indexOf('.csproj'))
|
|
299
|
-
const csprojPath = path.join(projectPath, csprojFilename)
|
|
300
|
-
const csproj = fs.readFileSync(csprojPath, 'utf-8')
|
|
301
|
-
const versionTag = '<PackageVersion>'
|
|
302
|
-
const xmlVersionTagIndex = csproj.indexOf(versionTag)
|
|
303
|
-
const versionStartIndex = xmlVersionTagIndex + versionTag.length
|
|
304
|
-
const versionStopIndex = csproj.indexOf('<', versionStartIndex)
|
|
305
|
-
const version = csproj.substring(versionStartIndex, versionStopIndex)
|
|
306
|
-
return `${namespace}.${version}.nupkg`
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
function logCommand(command, args, spawnOptions) {
|
|
310
|
-
console.log('running command: ' + `${command} ${args.join(' ')}`)
|
|
311
|
-
console.log('with spawn options: ' + JSON.stringify(spawnOptions))
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
async function dotnetDllCommand(relativeDllPath, argsArray, cwd = null, useStdin = false) {
|
|
315
|
-
throwIfRequiredIsFalsy(relativeDllPath, 'relativeDllPath')
|
|
316
|
-
throwIfRequiredArrayIsFalsyOrEmpty(argsArray, 'argsArray')
|
|
317
|
-
|
|
318
|
-
let args = [relativeDllPath, ...argsArray]
|
|
319
|
-
|
|
320
|
-
let spawnOptions = { ...defaultSpawnOptions }
|
|
321
|
-
if (cwd !== null) {
|
|
322
|
-
spawnOptions = { ...spawnOptions, cwd: cwd }
|
|
323
|
-
}
|
|
324
|
-
if (useStdin) {
|
|
325
|
-
spawnOptions = { ...spawnOptions, stdio: 'inherit' }
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
return waitForProcess(spawn('dotnet', args, spawnOptions))
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
async function dotnetPublish(cwd = null, outputDir = 'publish') {
|
|
332
|
-
let spawnOptions = { ...defaultSpawnOptions }
|
|
333
|
-
if (!!cwd) {
|
|
334
|
-
spawnOptions = { ...spawnOptions, cwd: cwd }
|
|
335
|
-
}
|
|
336
|
-
if (!outputDir) {
|
|
337
|
-
outputDir = 'publish'
|
|
338
|
-
}
|
|
339
|
-
let args = ['publish', '-o', outputDir]
|
|
340
|
-
return waitForProcess(spawn('dotnet', args, spawnOptions))
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
async function dotnetDbMigrationsList(dbContextName, relativeDbMigratorDirectoryPath) {
|
|
344
|
-
throwIfRequiredIsFalsy(dbContextName, 'dbContextName')
|
|
345
|
-
throwIfRequiredIsFalsy(relativeDbMigratorDirectoryPath, 'relativeDbMigratorDirectoryPath')
|
|
346
|
-
let spawnOptions = { ...defaultSpawnOptions, cwd: relativeDbMigratorDirectoryPath }
|
|
347
|
-
return waitForProcess(spawn('dotnet', ['ef', 'migrations', 'list', '--context', dbContextName], spawnOptions))
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
async function dotnetDbMigrate(dbContextName, relativeDbMigratorDirectoryPath, migrationName = '') {
|
|
351
|
-
throwIfRequiredIsFalsy(dbContextName, 'dbContextName')
|
|
352
|
-
throwIfRequiredIsFalsy(relativeDbMigratorDirectoryPath, 'relativeDbMigratorDirectoryPath')
|
|
353
|
-
let args = ['ef', 'database', 'update']
|
|
354
|
-
if (!!migrationName) {
|
|
355
|
-
args.push(migrationName)
|
|
356
|
-
}
|
|
357
|
-
args = [...args, '--context', dbContextName]
|
|
358
|
-
let spawnOptions = { ...defaultSpawnOptions, cwd: relativeDbMigratorDirectoryPath }
|
|
359
|
-
return waitForProcess(spawn('dotnet', args, spawnOptions))
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
async function dotnetDbAddMigration(dbContextName, relativeDbMigratorDirectoryPath, migrationName, withBoilerplate = false) {
|
|
363
|
-
throwIfRequiredIsFalsy(dbContextName, 'dbContextName')
|
|
364
|
-
throwIfRequiredIsFalsy(relativeDbMigratorDirectoryPath, 'relativeDbMigratorDirectoryPath')
|
|
365
|
-
throwIfRequiredIsFalsy(migrationName, 'migrationName')
|
|
366
|
-
|
|
367
|
-
const migrationsOutputDir = `Migrations/${dbContextName}Migrations`
|
|
368
|
-
|
|
369
|
-
let args = ['ef', 'migrations', 'add', migrationName, '--context', dbContextName, '-o', migrationsOutputDir]
|
|
370
|
-
let spawnOptions = { ...defaultSpawnOptions, cwd: relativeDbMigratorDirectoryPath }
|
|
371
|
-
await waitForProcess(spawn('dotnet', args, spawnOptions))
|
|
372
|
-
|
|
373
|
-
if (withBoilerplate) {
|
|
374
|
-
await dotnetDbAddMigrationBoilerplate(dbContextName, relativeDbMigratorDirectoryPath, migrationName)
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
async function dotnetDbAddMigrationBoilerplate(dbContextName, relativeDbMigratorDirectoryPath, migrationName) {
|
|
379
|
-
console.log(`Attempting to write boilerplate to generated migration C# file`)
|
|
380
|
-
|
|
381
|
-
const migrationsOutputDir = `Migrations/${dbContextName}Migrations`
|
|
382
|
-
const dirPath = path.join(relativeDbMigratorDirectoryPath, migrationsOutputDir)
|
|
383
|
-
|
|
384
|
-
console.log(`Checking for generated C# file in directory: ${dirPath}`)
|
|
385
|
-
|
|
386
|
-
const filenames = fs.readdirSync(dirPath).filter(fn => fn.endsWith(`${migrationName}.cs`))
|
|
387
|
-
if (!filenames || filenames.length === 0) {
|
|
388
|
-
console.log(`Unable to add boilerplate - could not find auto generated file in directory: ${dirPath}`)
|
|
389
|
-
}
|
|
390
|
-
const filename = filenames[0]
|
|
391
|
-
const filePath = path.join(dirPath, filename)
|
|
392
|
-
|
|
393
|
-
if (!fs.existsSync(filePath)) {
|
|
394
|
-
console.log(`Could not find the file to add boilerplate to at: ${filePath}`)
|
|
395
|
-
return
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
console.log(`Auto generated C# file to modify: ${filePath}`)
|
|
399
|
-
|
|
400
|
-
const usingLine = 'using MikeyT.DbMigrations;'
|
|
401
|
-
const upLine = `MigrationScriptRunner.RunScript(migrationBuilder, "${migrationName}.sql");`
|
|
402
|
-
const downLine = `MigrationScriptRunner.RunScript(migrationBuilder, "${migrationName}_Down.sql");`
|
|
403
|
-
|
|
404
|
-
const fileContents = await fsp.readFile(filePath, { encoding: 'utf8' })
|
|
405
|
-
let lines = fileContents.replaceAll('\r', '').split('\n')
|
|
406
|
-
|
|
407
|
-
let newLines = []
|
|
408
|
-
|
|
409
|
-
newLines.push(lines[0].trim())
|
|
410
|
-
newLines.push(usingLine)
|
|
411
|
-
|
|
412
|
-
let addUpLine = false
|
|
413
|
-
let addDownLine = false
|
|
414
|
-
let skipNextLineIfBlank = false
|
|
415
|
-
for (let i = 1; i < lines.length; i++) {
|
|
416
|
-
if (skipNextLineIfBlank && lines[i].trim().length === 0) {
|
|
417
|
-
skipNextLineIfBlank = false
|
|
418
|
-
continue
|
|
419
|
-
}
|
|
420
|
-
if (addUpLine) {
|
|
421
|
-
let newLine = lines[i].replace('{', `{\n\t\t\t${upLine}`)
|
|
422
|
-
newLines.push(newLine)
|
|
423
|
-
addUpLine = false
|
|
424
|
-
skipNextLineIfBlank = true
|
|
425
|
-
continue
|
|
426
|
-
}
|
|
427
|
-
if (addDownLine) {
|
|
428
|
-
let newLine = lines[i].replace('{', `{\n\t\t\t${downLine}`)
|
|
429
|
-
newLines.push(newLine)
|
|
430
|
-
addDownLine = false
|
|
431
|
-
skipNextLineIfBlank = true
|
|
432
|
-
continue
|
|
433
|
-
}
|
|
434
|
-
newLines.push(lines[i])
|
|
435
|
-
if (lines[i].includes('void Up')) {
|
|
436
|
-
addUpLine = true
|
|
437
|
-
}
|
|
438
|
-
if (lines[i].includes('void Down')) {
|
|
439
|
-
addDownLine = true
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
const newFileContents = newLines.join('\n')
|
|
444
|
-
|
|
445
|
-
await fsp.writeFile(filePath, newFileContents, { encoding: 'utf8' })
|
|
446
|
-
|
|
447
|
-
console.log(`Updated file with boilerplate - please ensure it is correct: ${filePath}`)
|
|
448
|
-
|
|
449
|
-
const upScriptPath = path.join(relativeDbMigratorDirectoryPath, `Scripts/${migrationName}.sql`)
|
|
450
|
-
const downScriptPath = path.join(relativeDbMigratorDirectoryPath, `Scripts/${migrationName}_Down.sql`)
|
|
451
|
-
|
|
452
|
-
console.log('Creating corresponding empty sql files (no action will be taken if they already exist):')
|
|
453
|
-
console.log(` - ${upScriptPath}`)
|
|
454
|
-
console.log(` - ${downScriptPath}`)
|
|
455
|
-
|
|
456
|
-
if (!fs.existsSync(upScriptPath)) {
|
|
457
|
-
await fsp.writeFile(upScriptPath, '', { encoding: 'utf8' })
|
|
458
|
-
} else {
|
|
459
|
-
console.log('Skipping Up sql script (already exists)')
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
if (!fs.existsSync(downScriptPath)) {
|
|
463
|
-
await fsp.writeFile(downScriptPath, '', { encoding: 'utf8' })
|
|
464
|
-
} else {
|
|
465
|
-
console.log('Skipping Down sql script (already exists)')
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
async function dotnetDbRemoveMigration(dbContextName, relativeDbMigratorDirectoryPath) {
|
|
470
|
-
throwIfRequiredIsFalsy(dbContextName, 'dbContextName')
|
|
471
|
-
throwIfRequiredIsFalsy(relativeDbMigratorDirectoryPath, 'relativeDbMigratorDirectoryPath')
|
|
472
|
-
let spawnOptions = { ...defaultSpawnOptions, cwd: relativeDbMigratorDirectoryPath }
|
|
473
|
-
return waitForProcess(spawn('dotnet', ['ef', 'migrations', 'remove', '--context', dbContextName], spawnOptions))
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
function throwIfRequiredIsFalsy(requiredArg, argName) {
|
|
477
|
-
if (!requiredArg) {
|
|
478
|
-
throw Error(`${argName} is required`)
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
function throwIfRequiredArrayIsFalsyOrEmpty(requiredArrayArg, argName) {
|
|
483
|
-
if (!requiredArrayArg || requiredArrayArg.length === 0 || !Array.isArray(requiredArrayArg)) {
|
|
484
|
-
throw Error(`${argName} array is required`)
|
|
485
|
-
}
|
|
486
|
-
}
|
|
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
|
-
|
|
602
|
-
exports.defaultSpawnOptions = defaultSpawnOptions
|
|
603
|
-
exports.defaultSpawnOptionsWithInput = defaultSpawnOptionsWithInput
|
|
604
|
-
exports.waitForProcess = waitForProcess
|
|
605
|
-
exports.copyNewEnvValues = copyNewEnvValues
|
|
606
|
-
exports.overwriteEnvFile = overwriteEnvFile
|
|
607
|
-
exports.throwIfDockerNotRunning = throwIfDockerNotRunning
|
|
608
|
-
exports.bashIntoRunningDockerContainer = bashIntoRunningDockerContainer
|
|
609
|
-
exports.dockerContainerIsRunning = dockerContainerIsRunning
|
|
610
|
-
exports.createTarball = createTarball
|
|
611
|
-
exports.dockerDepsUp = dockerDepsUp
|
|
612
|
-
exports.dockerDepsUpDetached = dockerDepsUpDetached
|
|
613
|
-
exports.dockerDepsDown = dockerDepsDown
|
|
614
|
-
exports.dockerDepsStop = dockerDepsStop
|
|
615
|
-
exports.dotnetBuild = dotnetBuild
|
|
616
|
-
exports.dotnetPack = dotnetPack
|
|
617
|
-
exports.dotnetNugetPublish = dotnetNugetPublish
|
|
618
|
-
exports.dotnetDllCommand = dotnetDllCommand
|
|
619
|
-
exports.dotnetPublish = dotnetPublish
|
|
620
|
-
exports.dotnetDbMigrationsList = dotnetDbMigrationsList
|
|
621
|
-
exports.dotnetDbMigrate = dotnetDbMigrate
|
|
622
|
-
exports.dotnetDbAddMigration = dotnetDbAddMigration
|
|
623
|
-
exports.dotnetDbRemoveMigration = dotnetDbRemoveMigration
|
|
624
|
-
exports.generateCertWithOpenSsl = generateCertWithOpenSsl
|
|
625
|
-
exports.winInstallCert = winInstallCert
|
|
626
|
-
exports.winUninstallCert = winUninstallCert
|
|
627
|
-
exports.linuxInstallCert = linuxInstallCert
|