-
Notifications
You must be signed in to change notification settings - Fork 1
/
gulpfile.js
238 lines (189 loc) · 9.85 KB
/
gulpfile.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
// noinspection JSCheckFunctionSignatures
const {src, dest, parallel, series} = require('gulp')
const {spawn} = require('child_process')
const {argv} = require('yargs')
const path = require('path')
const rimraf = require('rimraf')
const fsp = require('fs').promises
const {
defaultSpawnOptions,
waitForProcess,
copyNewEnvValues,
overwriteEnvFile,
throwIfDockerNotRunning,
dockerContainerIsRunning,
bashIntoRunningDockerContainer
} = require('@mikeyt23/gulp-utils')
const clientAppPath = 'src/services/OmegaService.Web/client-app/'
const serverAppPath = 'src/Omega/'
const dbMigratorPath = 'src/libs/Omega.DbMigrator/'
const dockerDirPath = 'deploy/docker/'
const dockerProjectName = 'omega' // The docker project name option prevents warnings about unrelated orphaned containers
const dockerDepsProjectName = 'omega_deps'
const dockerDepsComposeName = 'docker-compose.deps.yml'
const spawnOptions = {...defaultSpawnOptions, cwd: __dirname}
const dockerSpawnOptions = {...spawnOptions, cwd: path.resolve(__dirname, dockerDirPath)}
const clientSpawnOptions = {...spawnOptions, cwd: path.resolve(__dirname, clientAppPath)}
const serverAppSpawnOptions = {...spawnOptions, cwd: path.resolve(__dirname, serverAppPath)}
const migratorSpawnOptions = {...spawnOptions, cwd: path.resolve(__dirname, dbMigratorPath)}
const migratorSpawnOptionsWithInput = {...migratorSpawnOptions, stdio: 'inherit'}
async function syncEnvFiles() {
const rootServerEnv = './.env.server'
const rootClientEnv = './.env.client'
// Copy root .env.[category].template to .env
await copyNewEnvValues(`${rootServerEnv}.template`, rootServerEnv)
await copyNewEnvValues(`${rootClientEnv}.template`, rootClientEnv)
// Copy root .env.[category] to subdirectory .env files
await overwriteEnvFile(rootServerEnv, path.join(serverAppPath, '.env'))
await overwriteEnvFile(rootServerEnv, path.join(dockerDirPath, '.env'))
await overwriteEnvFile(rootServerEnv, path.join(dbMigratorPath, '.env'))
await overwriteEnvFile(rootClientEnv, path.join(clientAppPath, '.env'))
}
async function deleteEnvFiles() {
await Promise.all([
fsp.unlink(path.join('./.env.server')),
fsp.unlink(path.join('./.env.client')),
fsp.unlink(path.join(serverAppPath, '.env')),
fsp.unlink(path.join(dockerDirPath, '.env')),
fsp.unlink(path.join(dbMigratorPath, '.env')),
fsp.unlink(path.join(clientAppPath, '.env'))
])
}
async function yarnInstallClientApp() {
return waitForProcess(spawn('yarn', ['install'], clientSpawnOptions))
}
async function yarnStartClient() {
return waitForProcess(spawn('yarn', ['start'], clientSpawnOptions))
}
async function dotnetWatchRun() {
return waitForProcess(spawn('dotnet', ['watch', 'run'], serverAppSpawnOptions))
}
async function dotnetPublish() {
const args = ['publish', '-c', 'Release']
return waitForProcess(spawn('dotnet', args, spawnOptions))
}
async function yarnBuildClientApp() {
return waitForProcess(spawn('yarn', ['build'], clientSpawnOptions))
}
async function dockerBashRunningContainer() {
await throwIfDockerNotRunning()
await bashIntoRunningDockerContainer(`${dockerDepsProjectName}_${argv['imageName']}`)
}
// We're assuming that dependencies aren't up if the mssql container isn't running
async function throwIfDockerDepsNotUp() {
const mssqlIsRunning = await dockerContainerIsRunning('omega_mssql')
if (!mssqlIsRunning) {
throw 'Docker dependencies are not running'
}
}
async function dockerBuild() {
await throwIfDockerNotRunning()
return waitForProcess(spawn('docker-compose', ['--project-name', dockerProjectName, 'build', '--no-cache'], dockerSpawnOptions))
}
async function dockerUp() {
await throwIfDockerNotRunning()
return waitForProcess(spawn('docker-compose', ['--project-name', dockerProjectName, 'up'], dockerSpawnOptions))
}
async function dockerDown() {
await throwIfDockerNotRunning()
return waitForProcess(spawn('docker-compose', ['--project-name', dockerProjectName, 'down'], dockerSpawnOptions))
}
async function dockerStop() {
await throwIfDockerNotRunning()
return waitForProcess(spawn('docker-compose', ['--project-name', dockerProjectName, 'stop'], dockerSpawnOptions))
}
async function copyPublishedToDockerDir() {
const dockerBuiltServerPath = path.join(__dirname, 'deploy/docker/built_server_app/')
const dockerBuiltClientPath = path.join(__dirname, 'deploy/docker/built_client_app/')
await Promise.all([
new Promise(resolve => rimraf(dockerBuiltServerPath, resolve)),
new Promise(resolve => rimraf(dockerBuiltClientPath, resolve))
])
await Promise.all([fsp.mkdir(dockerBuiltServerPath), fsp.mkdir(dockerBuiltClientPath)])
// For excluding a dir, see https://github.com/gulpjs/gulp/issues/165#issuecomment-32611271
src(['src/Omega/bin/Release/net5.0/publish/**/*', '!**/client-app', '!**/client-app/**']).pipe(dest(dockerBuiltServerPath))
src('src/services/OmegaService.Web/client-app/build/**/*').pipe(dest(dockerBuiltClientPath))
}
async function dockerStandaloneBuild() {
await throwIfDockerNotRunning()
const args = ['build', '-f', 'Dockerfile.standalone', '-t', 'omega_standalone:1.0', '.']
return waitForProcess(spawn('docker', args, dockerSpawnOptions))
}
async function dockerStandaloneRun() {
await throwIfDockerNotRunning()
// Note that running docker as a gulp command disallows passing the -t command since that would try to configure the terminal
const args = ['run', '-i', '--rm', '-p', '5000:80', 'omega_standalone:1.0']
return waitForProcess(spawn('docker', args, dockerSpawnOptions))
}
async function dockerDepsUp() {
await throwIfDockerNotRunning()
return waitForProcess(spawn('docker-compose', ['--project-name', dockerDepsProjectName, '-f', dockerDepsComposeName, 'up'], dockerSpawnOptions))
}
async function dockerDepsUpDetached() {
await throwIfDockerNotRunning()
return waitForProcess(spawn('docker-compose', ['--project-name', dockerDepsProjectName, '-f', dockerDepsComposeName, 'up', '-d'], dockerSpawnOptions))
}
async function dockerDepsDown() {
await throwIfDockerNotRunning()
return waitForProcess(spawn('docker-compose', ['--project-name', dockerDepsProjectName, '-f', dockerDepsComposeName, 'down'], dockerSpawnOptions))
}
async function dockerDepsStop() {
await throwIfDockerNotRunning()
return waitForProcess(spawn('docker-compose', ['--project-name', dockerDepsProjectName, '-f', dockerDepsComposeName, 'stop'], dockerSpawnOptions))
}
async function deleteMigratorPublishDir() {
const rootMigratorPublishPath = path.join(__dirname, 'src/libs/Omega.DbMigrator/publish/')
await new Promise(resolve => rimraf(`${rootMigratorPublishPath}*`, resolve))
}
async function publishMigrator() {
return waitForProcess(spawn('dotnet', ['publish', '-o', 'publish'], migratorSpawnOptions))
}
async function runDbMigrator() {
return waitForProcess(spawn('dotnet', ['publish/Omega.DbMigrator.dll'], migratorSpawnOptions))
}
async function dbDropAll() {
return waitForProcess(spawn('dotnet', ['publish/Omega.DbMigrator.dll', 'dropAll'], migratorSpawnOptionsWithInput))
}
async function testDbMigrate() {
return waitForProcess(spawn('dotnet', ['publish/Omega.DbMigrator.dll', 'testDbMigrate'], migratorSpawnOptionsWithInput))
}
const build = parallel(dotnetPublish, yarnBuildClientApp)
// Runs yarn install on client app while also copying client-app .env.template to .env.
// Add other initial setup tasks here when they're needed.
exports.initialInstall = parallel(yarnInstallClientApp, syncEnvFiles)
// Run client app
exports.startClient = series(syncEnvFiles, yarnStartClient)
// Run dotnet server app
exports.startServer = series(syncEnvFiles, dotnetWatchRun)
// Run dotnet publish and yarn build in parallel.
exports.build = build
// Run docker-compose build. Can also just run dockerUp since it will build if the image doesn't exist.
exports.dockerBuild = series(dockerBuild)
// Build the docker images/network if they don't exist and start containers.
exports.dockerUp = series(dockerUp)
// Tear down the docker containers/network.
exports.dockerDown = dockerDown
// Sometimes ctrl-C doesn't stop the containers and they're left running - stop with this task
exports.dockerStop = dockerStop
// Run images with bash entrypoint to troubleshoot what files are ending up in the built images. Requires an arg --image-name to be passed. See package.json for examples.
exports.dockerBash = dockerBashRunningContainer
// Good for rapidly making docker-compose changes and testing them
exports.dockerRecreate = series(throwIfDockerDepsNotUp, dockerDown, dockerUp)
// Good for testing app in docker after making source changes. Completely rebuilds the images before bringing containers up.
exports.dockerRecreateFull = series(throwIfDockerDepsNotUp, parallel(dockerDown, build), copyPublishedToDockerDir, dockerBuild, dockerUp)
exports.dockerRecreateServer = series(throwIfDockerDepsNotUp, parallel(dockerDown, dotnetPublish), copyPublishedToDockerDir, dockerBuild, dockerUp)
// Docker standalone build
exports.dockerStandaloneBuild = dockerStandaloneBuild
exports.dockerStandaloneRun = series(throwIfDockerDepsNotUp, dockerStandaloneRun)
// Docker dependency operations
exports.dockerDepsUp = series(syncEnvFiles, dockerDepsUp)
exports.dockerDepsUpDetached = series(syncEnvFiles, dockerDepsUpDetached)
exports.dockerDepsDown = dockerDepsDown
exports.dockerDepsStop = dockerDepsStop
// DB operations
exports.dbMigrate = series(throwIfDockerDepsNotUp, parallel(syncEnvFiles, deleteMigratorPublishDir), publishMigrator, runDbMigrator)
exports.dbDropAll = series(throwIfDockerDepsNotUp, parallel(syncEnvFiles, deleteMigratorPublishDir), publishMigrator, dbDropAll)
exports.testDbMigrate = series(throwIfDockerDepsNotUp, parallel(syncEnvFiles, deleteMigratorPublishDir), publishMigrator, testDbMigrate)
// Create .env files if they don't exist and adds any new key value pairs. Also changes existing values in subdirectory .env files if non-template values have changed.
exports.syncEnvFiles = syncEnvFiles
exports.deleteEnvFiles = deleteEnvFiles