This repository has been archived by the owner on Jan 25, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbuild.fsx
337 lines (296 loc) · 13.1 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
#r "paket:
// Currently we use 'old' FAKE in the task because
// not all APIs (StrongNameKeyPair) are available in netcore
// And Mono.Cecil has removed support for Strong naming in netcore version
// Sadly we run into a size limitation when trying to bundle all binaries
// -> therefore we download stuff when running the task first time.
//nuget FAKE prerelease
//nuget Microsoft.Build.Utilities.Core
//nuget NuGet.CommandLine
nuget FSharp.Core
nuget Fake.Core.Process prerelease
nuget Fake.IO.FileSystem prerelease
nuget Fake.Core.Targets prerelease
nuget Fake.Core.Environment prerelease
//"
#load ".fake/build.fsx/intellisense.fsx"
open System
open System.Text
open System.Text.RegularExpressions
open System.IO
open System.Collections.Generic
open Fake.Core
open Fake.IO
open System.Net.Http
open System.Collections.Generic
type WebClient = HttpClient
type HttpClient with
member x.DownloadFileTaskAsync (uri : Uri, filePath : string) =
async {
let! response = x.GetAsync(uri) |> Async.AwaitTask
use fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None)
do! response.Content.CopyToAsync(fileStream) |> Async.AwaitTask
fileStream.Flush()
} |> Async.StartAsTask
member x.DownloadFileTaskAsync (uri : string, filePath : string) =
x.DownloadFileTaskAsync(Uri uri, filePath)
member x.DownloadFile (uri : string, filePath : string) =
x.DownloadFileTaskAsync(uri, filePath).GetAwaiter().GetResult()
member x.DownloadFile (uri : Uri, filePath : string) =
x.DownloadFileTaskAsync(uri, filePath).GetAwaiter().GetResult()
member x.DownloadStringTaskAsync (uri : Uri) =
async {
let! response = x.GetAsync(uri) |> Async.AwaitTask
let! result = response.Content.ReadAsStringAsync() |> Async.AwaitTask
return result
} |> Async.StartAsTask
member x.DownloadStringTaskAsync (uri : string) = x.DownloadStringTaskAsync(Uri uri)
member x.DownloadString (uri : string) =
x.DownloadStringTaskAsync(uri).GetAwaiter().GetResult()
member x.DownloadString (uri : Uri) =
x.DownloadStringTaskAsync(uri).GetAwaiter().GetResult()
member x.DownloadDataTaskAsync(uri : Uri) =
async {
let! response = x.GetAsync(uri) |> Async.AwaitTask
let! result = response.Content.ReadAsByteArrayAsync() |> Async.AwaitTask
return result
} |> Async.StartAsTask
member x.DownloadDataTaskAsync (uri : string) = x.DownloadDataTaskAsync(Uri uri)
member x.DownloadData(uri : string) =
x.DownloadDataTaskAsync(uri).GetAwaiter().GetResult()
member x.DownloadData(uri : Uri) =
x.DownloadDataTaskAsync(uri).GetAwaiter().GetResult()
member x.UploadFileAsMultipart (url : Uri) filename =
let fileTemplate =
"--{0}\r\nContent-Disposition: form-data; name=\"{1}\"; filename=\"{2}\"\r\nContent-Type: {3}\r\n\r\n"
let boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x", System.Globalization.CultureInfo.InvariantCulture)
let fileInfo = (new FileInfo(Path.GetFullPath(filename)))
let fileHeaderBytes =
System.String.Format
(System.Globalization.CultureInfo.InvariantCulture, fileTemplate, boundary, "package", "package", "application/octet-stream")
|> Encoding.UTF8.GetBytes
let newlineBytes = Environment.NewLine |> Encoding.UTF8.GetBytes
let trailerbytes = String.Format(System.Globalization.CultureInfo.InvariantCulture, "--{0}--", boundary) |> Encoding.UTF8.GetBytes
x.DefaultRequestHeaders.Add("ContentType", "multipart/form-data; boundary=" + boundary)
use stream = new MemoryStream() // x.OpenWrite(url, "PUT")
stream.Write(fileHeaderBytes, 0, fileHeaderBytes.Length)
use fileStream = File.OpenRead fileInfo.FullName
fileStream.CopyTo(stream, (4 * 1024))
stream.Write(newlineBytes, 0, newlineBytes.Length)
stream.Write(trailerbytes, 0, trailerbytes.Length)
stream.Write(newlineBytes, 0, newlineBytes.Length)
stream.Position <- 0L
x.PutAsync(url, new StreamContent(stream)).GetAwaiter().GetResult()
member x.DownloadStringAndHeaders (url :string) =
async {
let! response = x.GetAsync(url) |> Async.AwaitTask
let! result = response.Content.ReadAsStringAsync() |> Async.AwaitTask
return
result,
response.Headers :> seq<KeyValuePair<string, seq<string>>>
|> Seq.map (fun kv -> kv.Key, kv.Value |> Seq.toList)
|> Map.ofSeq
} |> Async.RunSynchronously
let internal addAcceptHeader (client:HttpClient) (contentType:string) =
for headerVal in contentType.Split([|','|], System.StringSplitOptions.RemoveEmptyEntries) do
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue(headerVal))
let internal addHeader (client:HttpClient) (headerKey:string) (headerVal:string) =
client.DefaultRequestHeaders.Add(headerKey, headerVal)
module Util =
open System.Net
let retryIfFails maxRetries f =
let rec loop retriesRemaining =
try
f ()
with _ when retriesRemaining > 0 ->
loop (retriesRemaining - 1)
loop maxRetries
let (|RegexReplace|_|) =
let cache = new Dictionary<string, Regex>()
fun pattern (replacement: string) input ->
let regex =
match cache.TryGetValue(pattern) with
| true, regex -> regex
| false, _ ->
let regex = Regex pattern
cache.Add(pattern, regex)
regex
let m = regex.Match(input)
if m.Success
then regex.Replace(input, replacement) |> Some
else None
let join pathParts =
Path.Combine(Array.ofSeq pathParts)
let run workingDir fileName args =
printfn "CWD: %s" workingDir
let fileName, args =
if Environment.isUnix
then fileName, args else "cmd", ("/C " + fileName + " " + args)
let ok =
Process.execProcess (fun info ->
{ info with
FileName = fileName
WorkingDirectory = workingDir
Arguments = args }) TimeSpan.MaxValue
if not ok then failwith (sprintf "'%s> %s %s' task failed" workingDir fileName args)
let start workingDir fileName args =
let p = new System.Diagnostics.Process()
p.StartInfo.FileName <- fileName
p.StartInfo.WorkingDirectory <- workingDir
p.StartInfo.Arguments <- args
p.Start() |> ignore
p
let runAndReturn workingDir fileName args =
printfn "CWD: %s" workingDir
let fileName, args =
if Environment.isUnix
then fileName, args else "cmd", ("/C " + args)
Process.ExecProcessAndReturnMessages (fun info ->
{ info with
FileName = fileName
WorkingDirectory = workingDir
Arguments = args}) TimeSpan.MaxValue
|> fun p -> p.Messages |> String.concat "\n"
let downloadArtifact path (url: string) =
async {
let tempFile = Path.ChangeExtension(Path.GetTempFileName(), ".zip")
use client = new WebClient()
do! client.DownloadFileTaskAsync(Uri url, tempFile) |> Async.AwaitTask
Shell.mkdir path
Shell.CleanDir path
run path "unzip" (sprintf "-q %s" tempFile)
File.Delete tempFile
} |> Async.RunSynchronously
let rmdir dir =
if Environment.isUnix
then Shell.rm_rf dir
// Use this in Windows to prevent conflicts with paths too long
else run "." "cmd" ("/C rmdir /s /q " + Path.GetFullPath dir)
let visitFile (visitor: string->string) (fileName : string) =
File.ReadAllLines(fileName)
|> Array.map (visitor)
|> fun lines -> File.WriteAllLines(fileName, lines)
let replaceLines (replacer: string->Match->string option) (reg: Regex) (fileName: string) =
fileName |> visitFile (fun line ->
let m = reg.Match(line)
if not m.Success
then line
else
match replacer line m with
| None -> line
| Some newLine -> newLine)
let normalizeVersion (version: string) =
let i = version.IndexOf("-")
if i > 0 then version.Substring(0, i) else version
type ComparisonResult = Smaller | Same | Bigger
let foldi f init (xs: 'T seq) =
let mutable i = -1
(init, xs) ||> Seq.fold (fun state x ->
i <- i + 1
f i state x)
let compareVersions (expected: string) (actual: string) =
if actual = "*" // Wildcard for custom fable-core builds
then Same
else
let expected = expected.Split('.', '-')
let actual = actual.Split('.', '-')
(Same, expected) ||> foldi (fun i comp expectedPart ->
match comp with
| Bigger -> Bigger
| Same when actual.Length <= i -> Smaller
| Same ->
let actualPart = actual.[i]
match Int32.TryParse(expectedPart), Int32.TryParse(actualPart) with
// TODO: Don't allow bigger for major version?
| (true, expectedPart), (true, actualPart) ->
if actualPart > expectedPart
then Bigger
elif actualPart = expectedPart
then Same
else Smaller
| _ ->
if actualPart = expectedPart
then Same
else Smaller
| Smaller -> Smaller)
module Npm =
let script workingDir script args =
sprintf "run %s -- %s" script (String.concat " " args)
|> Util.run workingDir "npm"
let install workingDir modules =
let npmInstall () =
sprintf "install %s" (String.concat " " modules)
|> Util.run workingDir "npm"
// On windows, retry npm install to avoid bug related to https://github.com/npm/npm/issues/9696
Util.retryIfFails (if Environment.isWindows then 3 else 0) npmInstall
let command workingDir command args =
sprintf "%s %s" command (String.concat " " args)
|> Util.run workingDir "npm"
let commandAndReturn workingDir command args =
sprintf "%s %s" command (String.concat " " args)
|> Util.runAndReturn workingDir "npm"
let getLatestVersion package tag =
let package =
match tag with
| Some tag -> package + "@" + tag
| None -> package
commandAndReturn "." "show" [package; "version"]
let updatePackageKeyValue f pkgDir keys =
let pkgJson = Path.Combine(pkgDir, "package.json")
let reg =
String.concat "|" keys
|> sprintf "\"(%s)\"\\s*:\\s*\"(.*?)\""
|> Regex
let lines =
File.ReadAllLines pkgJson
|> Array.map (fun line ->
let m = reg.Match(line)
if m.Success then
match f(m.Groups.[1].Value, m.Groups.[2].Value) with
| Some(k,v) -> reg.Replace(line, sprintf "\"%s\": \"%s\"" k v)
| None -> line
else line)
File.WriteAllLines(pkgJson, lines)
module Node =
let run workingDir script args =
let args = sprintf "%s %s" script (String.concat " " args)
Util.run workingDir "node" args
open Fake.Core
open Fake.Core.TargetOperators
let dirs = ["CreateSignedPackages.dev"]
Target.Create "Clean" (fun _ -> ())
Target.Create "NpmInstall" (fun _ ->
Npm.install "." []
for dir in dirs do
Npm.install dir []
)
//Target "PrepareBinaries" (fun _ ->
// Directory.ensure "CreateSignedPackages.dev/bin/Fake"
// Shell.cp_r ".fake/build.fsx/packages/FAKE/tools" "CreateSignedPackages.dev/bin/Fake"
// Directory.ensure "CreateSignedPackages.dev/bin/Microsoft.Build.Utilities.Core"
// Shell.cp_r ".fake/build.fsx/packages/Microsoft.Build.Utilities.Core/lib/net46" "CreateSignedPackages.dev/bin/Microsoft.Build.Utilities.Core"
// Directory.ensure "CreateSignedPackages.dev/bin/NuGet"
// Shell.cp_r ".fake/build.fsx/packages/NuGet.CommandLine/tools" "CreateSignedPackages.dev/bin/NuGet"
//)
Target.Create "Compile" (fun _ ->
for dir in dirs do
Npm.script dir "tsc" []
)
Target.Create "Bundle" (fun _ ->
// Workaround for not having an "exclude" feature...
Shell.CleanDir "CreateSignedPackages"
Shell.cp_r "CreateSignedPackages.dev" "CreateSignedPackages"
Shell.rm_rf "CreateSignedPackages/mypackages"
Shell.rm_rf "CreateSignedPackages/packages"
Shell.rm_rf "CreateSignedPackages/signedPackages"
Shell.rm_rf "CreateSignedPackages/.fake"
Npm.script "." "tfx" ["extension"; "create"; "--manifest-globs"; "vss-extension.json"]
)
Target.Create "Default" (fun _ -> ())
"Clean"
==> "NpmInstall"
//==> "PrepareBinaries"
==> "Compile"
==> "Bundle"
==> "Default"
Target.RunOrDefault "Default"