forked from SAFE-Stack/SAFE-template
-
Notifications
You must be signed in to change notification settings - Fork 0
/
build.fsx
441 lines (371 loc) · 14.6 KB
/
build.fsx
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
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
#r "paket: groupref build //"
#load "./.fake/build.fsx/intellisense.fsx"
#if !FAKE
#r "netstandard"
#r "Facades/netstandard" // https://github.com/ionide/ionide-vscode-fsharp/issues/839#issuecomment-396296095
#endif
open System
open Fake.Core
open Fake.DotNet
open Fake.IO
open Fake.IO.FileSystemOperators
open Fake.IO.Globbing.Operators
open Fake.Tools
let templatePath = "./Content/.template.config/template.json"
let versionFilePath = "./Content/src/Client/Version.fs"
let templateProj = "SAFE.Template.proj"
let templateName = "SAFE-Stack Web App"
let nupkgDir = Path.getFullName "./nupkg"
let release = ReleaseNotes.load "RELEASE_NOTES.md"
let formattedRN =
release.Notes
|> List.map (sprintf "* %s")
|> String.concat "\n"
Target.create "Clean" (fun _ ->
Shell.cleanDirs [ nupkgDir ]
Git.CommandHelper.directRunGitCommandAndFail "./Content" "clean -fxd"
)
Target.create "BuildWebPackConfig" (fun _ ->
let srcDir = "paket-files/fable-compiler/webpack-config-template/webpack.config.js"
let destDir = "Content/webpack.config.js"
Shell.copyFile destDir srcDir
let devServerProxy =
"""{
// redirect requests that start with /api/* to the server on port 8085
'/api/*': {
target: 'http://localhost:' + (process.env.SERVER_PROXY_PORT || "8085"),
changeOrigin: true
},
// redirect websocket requests that start with /socket/* to the server on the port 8085
'/socket/*': {
target: 'http://localhost:' + (process.env.SERVER_PROXY_PORT || "8085"),
ws: true
}
}"""
let quote = sprintf "'%s'"
let replacements =
[
"indexHtmlTemplate", quote "./src/Client/index.html"
"fsharpEntry", quote "./src/Client/Client.fsproj"
"cssEntry", quote "./src/Client/style.scss"
"outputDir", quote "./src/Client/deploy"
"assetsDir", quote "./src/Client/public"
"devServerProxy", devServerProxy
]
for (key, value) in replacements do
Shell.regexReplaceInFileWithEncoding
(sprintf " %s: .+," key)
(sprintf " %s: %s," key value)
System.Text.Encoding.UTF8
destDir
)
Target.create "Pack" (fun _ ->
Shell.regexReplaceInFileWithEncoding
" \"name\": .+,"
(" \"name\": \"" + templateName + " v" + release.NugetVersion + "\",")
System.Text.Encoding.UTF8
templatePath
Shell.regexReplaceInFileWithEncoding
"let template = \".+\""
("let template = \"" + release.NugetVersion + "\"")
System.Text.Encoding.UTF8
versionFilePath
DotNet.pack
(fun args ->
{ args with
OutputPath = Some nupkgDir
Common =
{ args.Common with
CustomParams =
Some (sprintf "/p:PackageVersion=%s /p:PackageReleaseNotes=\"%s\""
release.NugetVersion
formattedRN) }
})
templateProj
)
Target.create "Install" (fun _ ->
let args=
let nupkgFileName = sprintf "SAFE.Template.%s.nupkg" release.NugetVersion
let fullPathToNupkg = System.IO.Path.Combine(nupkgDir, nupkgFileName)
sprintf "-i \"%s\"" fullPathToNupkg
let result = DotNet.exec (fun x -> { x with DotNetCliPath = "dotnet" }) "new" args
if not result.OK then failwithf "`dotnet %s` failed with %O" args result
)
let psi exe arg dir (x: ProcStartInfo) : ProcStartInfo =
{ x with
FileName = exe
Arguments = arg
WorkingDirectory = dir }
let run exe arg dir =
let result = Process.execWithResult (psi exe arg dir) TimeSpan.MaxValue
if not result.OK then (failwithf "`%s %s` failed: %A" exe arg result.Errors)
type Communication =
| Remoting
| Bridge
type BuildPaketDependencies =
{ Azure : bool }
with override x.ToString () =
if x.Azure then "azure" else "noazure"
type ClientPaketDependencies =
{ Communication : Communication option
Fulma : bool
Reaction : bool }
with override x.ToString () =
let communication =
x.Communication
|> Option.map (function
| Remoting -> "remoting"
| Bridge -> "bridge")
|> Option.defaultValue "nocommunication"
let fulma = if x.Fulma then "fulma" else "nofulma"
let reaction = if x.Reaction then "reaction" else "noreaction"
sprintf "%s-%s-%s" communication fulma reaction
type ServerPaketDependency = Saturn | Giraffe | Suave
with override x.ToString () =
match x with
| Saturn -> "saturn"
| Giraffe -> "giraffe"
| Suave -> "suave"
type ServerPaketDependencies =
{ Server : ServerPaketDependency
Communication : Communication option
Azure : bool }
with override x.ToString () =
let server = string x.Server
let communication =
x.Communication
|> Option.map (function
| Remoting -> "remoting"
| Bridge -> "bridge")
|> Option.defaultValue "nocommunication"
let azure = if x.Azure then "azure" else "noazure"
sprintf "%s-%s-%s" server communication azure
type CombinedPaketDependencies =
{ Azure : bool
Communication : Communication option
Fulma : bool
Server : ServerPaketDependency
Reaction : bool }
member x.ToBuild : BuildPaketDependencies =
{ Azure = x.Azure }
member x.ToClient : ClientPaketDependencies =
{ Communication = x.Communication
Fulma = x.Fulma
Reaction = x.Reaction }
member x.ToServer : ServerPaketDependencies =
{ Server = x.Server
Communication = x.Communication
Azure = x.Azure }
override x.ToString () =
let communication =
x.Communication
|> Option.map (fun comm ->
sprintf "--communication %s"
(match comm with
| Bridge -> "bridge"
| Remoting -> "remoting"))
let azure = if x.Azure then Some "--deploy azure" else None
let fulma = if not x.Fulma then Some "--layout none" else None
let server = if x.Server <> Saturn then Some (sprintf "--server %O" x.Server) else None
let reaction = if x.Reaction then Some "--pattern reaction" else None
[ communication
azure
fulma
server
reaction ]
|> List.choose id
|> String.concat " "
let configs =
[ for azure in [ false; true ] do
for fulma in [ false; true ] do
for communication in [ Some Bridge; Some Remoting; None ] do
for server in [ Saturn; Giraffe; Suave ] do
for reaction in [ false; true ] do
yield
{ Azure = azure
Fulma = fulma
Server = server
Communication = communication
Reaction = reaction }
]
let specific f =
List.map (fun x -> f x, string x)
>> List.groupBy fst
>> List.map (snd >> List.head)
let specificConfigs =
[ "Build", configs |> specific (fun x -> string x.ToBuild)
"Client", configs |> specific (fun x -> string x.ToClient)
"Server", configs |> specific (fun x -> string x.ToServer) ]
|> Map.ofList
let fullLockFileName build client server =
sprintf "paket_%O_%O_%O.lock" build client server
let runPaket args wd =
run "paket" args wd
Target.create "BuildPaketLockFiles" (fun _ ->
for config in configs do
let contents =
[
"Content" </> "src" </> "Build" </> sprintf "paket_%O.lock" config.ToBuild
"Content" </> "src" </> "Client" </> sprintf "paket_%O.lock" config.ToClient
"Content" </> "src" </> "Server" </> sprintf "paket_%O.lock" config.ToServer
]
|> List.map File.read
|> Seq.concat
let lockFileName = fullLockFileName config.ToBuild config.ToClient config.ToServer
File.writeWithEncoding (Text.UTF8Encoding(false)) false ("Content" </> lockFileName) contents
)
Target.create "RemovePaketLockFiles" (fun _ ->
for config in configs do
let lockFileName = fullLockFileName config.ToBuild config.ToClient config.ToServer
File.delete ("Content" </> lockFileName)
)
Target.create "GenJsonConditions" (fun _ ->
for config in configs do
let lockFileName = fullLockFileName config.ToBuild config.ToClient config.ToServer
let server = string config.Server
let azureOperator = if config.Azure then "==" else "!="
let communication = config.Communication
let layoutOperator = if config.Fulma then "!=" else "=="
let reaction = config.Reaction
let template =
sprintf """ {
"include": "%s",
"condition": "(server == \"%s\" && remoting == %b && bridge == %b && deploy %s \"azure\" && layout %s \"none\" && reaction == %b)",
"rename": { "%s": "paket.lock" }
},"""
lockFileName
server
(communication = Some Remoting)
(communication = Some Bridge)
azureOperator
layoutOperator
reaction
lockFileName
printfn "%s" template
)
Target.create "GenPaketLockFiles" (fun _ ->
let baseDir = "gen-paket-lock-files"
Directory.delete baseDir
Directory.create baseDir
for config in configs do
let dirName = baseDir </> "tmp"
Directory.delete dirName
Directory.create dirName
let arg = string config
run "dotnet" (sprintf "new SAFE %s" arg) dirName
let lockFile = dirName </> "paket.lock"
if not (File.exists lockFile) then
printfn "'paket.lock' doesn't exist for args '%s', installing..." arg
runPaket "install" dirName
let lines = File.readAsString lockFile
Directory.delete dirName
Directory.create dirName
let delimeter = "GROUP "
let groups =
lines
|> String.splitStr delimeter
|> List.filter (String.isNullOrWhiteSpace >> not)
|> List.map (fun group -> group.Substring(0, group.IndexOf Environment.NewLine), delimeter + group)
for (groupName, group) in groups do
let dirName = baseDir </> groupName
Directory.create dirName
let lockFileSuffix =
match groupName with
| "Build" -> string config.ToBuild
| "Client" -> string config.ToClient
| "Server" -> string config.ToServer
| _ -> failwithf "Unhandled name '%s'" groupName
let fileName = sprintf "paket_%s.lock" lockFileSuffix
let filePath = dirName </> fileName
if not (File.exists filePath) then
File.writeString false filePath group
Shell.copyFile ("Content" </> "src" </> groupName </> fileName) (dirName </> fileName)
else
printfn "'%s' already exists" filePath
)
Target.create "UpdatePaketLockFiles" (fun x ->
let baseDir = "gen-paket-lock-files"
Directory.delete baseDir
Directory.create baseDir
let groupNames =
match x.Context.Arguments with
| [ ] -> specificConfigs |> Seq.map (fun kv -> kv.Key) |> Seq.toList
| xs -> xs
for groupName in groupNames do
let configs =
match Map.tryFind groupName specificConfigs with
| Some x -> x |> List.indexed
| None -> failwithf "unknown group: '%s'" groupName
printfn "Group name: %s, all configs: %A" groupName configs
for (index, (configAbbr, safeArgs)) in configs do
let dirName = baseDir </> "tmp"
Directory.delete dirName
Directory.create dirName
printfn "Updating lock file %d of %d" (index + 1) configs.Length
run "dotnet" (sprintf "new SAFE %s" safeArgs) dirName
let lockFile = dirName </> "paket.lock"
if not (File.exists lockFile) then
failwithf "'paket.lock' doesn't exist for args '%s'" safeArgs
runPaket (sprintf "update -g %s" groupName) dirName
let lines = File.readAsString lockFile
Directory.delete dirName
Directory.create dirName
let delimeter = "GROUP "
let (groupName, group) =
lines
|> String.splitStr delimeter
|> List.filter (String.isNullOrWhiteSpace >> not)
|> List.map (fun group -> group.Substring(0, group.IndexOf Environment.NewLine), delimeter + group)
|> List.filter (fst >> ((=) groupName))
|> List.head
let dirName = baseDir </> groupName
Directory.create dirName
let fileName = sprintf "paket_%s.lock" configAbbr
let filePath = dirName </> fileName
if not (File.exists filePath) then
File.writeString false filePath group
Shell.copyFile ("Content" </> "src" </> groupName </> fileName) (dirName </> fileName)
else
printfn "'%s' already exists" filePath
)
Target.create "Tests" (fun _ ->
let cmd = "run"
let args = "--project tests/tests.fsproj"
let result = DotNet.exec (fun x -> { x with DotNetCliPath = "dotnet" }) cmd args
if not result.OK then failwithf "`dotnet %s %s` failed" cmd args
)
Target.create "Push" (fun _ ->
Paket.push ( fun args ->
{ args with
PublishUrl = "https://www.nuget.org"
WorkingDir = nupkgDir
}
)
let remoteGit = "upstream"
let commitMsg = sprintf "Bumping version to %O" release.NugetVersion
let tagName = string release.NugetVersion
Git.Branches.checkout "" false "master"
Git.CommandHelper.directRunGitCommand "" "fetch origin" |> ignore
Git.CommandHelper.directRunGitCommand "" "fetch origin --tags" |> ignore
Git.Staging.stageAll ""
Git.Commit.exec "" commitMsg
Git.Branches.pushBranch "" remoteGit "master"
Git.Branches.tag "" tagName
Git.Branches.pushTag "" remoteGit tagName
)
Target.create "Release" ignore
open Fake.Core.TargetOperators
"Clean"
==> "BuildWebPackConfig"
==> "BuildPaketLockFiles"
==> "Pack"
==> "RemovePaketLockFiles"
==> "Install"
==> "Tests"
==> "Push"
==> "Release"
"Install"
==> "GenPaketLockFiles"
"Install"
==> "UpdatePaketLockFiles"
Target.runOrDefaultWithArguments "Install"