1 <#
2 .NOTES
3 Author: @jhowardmsft
4
5 Summary: Windows native build script. This is similar to functionality provided
6 by hack\make.sh, but uses native Windows PowerShell semantics. It does
7 not support the full set of options provided by the Linux counterpart.
8 For example:
9
10 - You can't cross-build Linux docker binaries on Windows
11 - Hashes aren't generated on binaries
12 - 'Releasing' isn't supported.
13 - Integration tests. This is because they currently cannot run inside a container,
14 and require significant external setup.
15
16 It does however provided the minimum necessary to support parts of local Windows
17 development and Windows to Windows CI.
18
19 Usage Examples (run from repo root):
20 "hack\make.ps1 -Client" to build docker.exe client 64-bit binary (remote repo)
21 "hack\make.ps1 -TestUnit" to run unit tests
22 "hack\make.ps1 -Daemon -TestUnit" to build the daemon and run unit tests
23 "hack\make.ps1 -All" to run everything this script knows about that can run in a container
24 "hack\make.ps1" to build the daemon binary (same as -Daemon)
25 "hack\make.ps1 -Binary" shortcut to -Client and -Daemon
26
27 .PARAMETER Client
28 Builds the client binaries.
29
30 .PARAMETER Daemon
31 Builds the daemon binary.
32
33 .PARAMETER Binary
34 Builds the client and daemon binaries. A convenient shortcut to `make.ps1 -Client -Daemon`.
35
36 .PARAMETER Race
37 Use -race in go build and go test.
38
39 .PARAMETER Noisy
40 Use -v in go build.
41
42 .PARAMETER ForceBuildAll
43 Use -a in go build.
44
45 .PARAMETER NoOpt
46 Use -gcflags -N -l in go build to disable optimisation (can aide debugging).
47
48 .PARAMETER CommitSuffix
49 Adds a custom string to be appended to the commit ID (spaces are stripped).
50
51 .PARAMETER DCO
52 Runs the DCO (Developer Certificate Of Origin) test (must be run outside a container).
53
54 .PARAMETER PkgImports
55 Runs the pkg\ directory imports test (must be run outside a container).
56
57 .PARAMETER GoFormat
58 Runs the Go formatting test (must be run outside a container).
59
60 .PARAMETER TestUnit
61 Runs unit tests.
62
63 .PARAMETER TestIntegration
64 Runs integration tests.
65
66 .PARAMETER All
67 Runs everything this script knows about that can run in a container.
68
69
70 TODO
71 - Unify the head commit
72 - Add golint and other checks (swagger maybe?)
73
74 #>
75
76
77 param(
78 [Parameter(Mandatory=$False)][switch]$Client,
79 [Parameter(Mandatory=$False)][switch]$Daemon,
80 [Parameter(Mandatory=$False)][switch]$Binary,
81 [Parameter(Mandatory=$False)][switch]$Race,
82 [Parameter(Mandatory=$False)][switch]$Noisy,
83 [Parameter(Mandatory=$False)][switch]$ForceBuildAll,
84 [Parameter(Mandatory=$False)][switch]$NoOpt,
85 [Parameter(Mandatory=$False)][string]$CommitSuffix="",
86 [Parameter(Mandatory=$False)][switch]$DCO,
87 [Parameter(Mandatory=$False)][switch]$PkgImports,
88 [Parameter(Mandatory=$False)][switch]$GoFormat,
89 [Parameter(Mandatory=$False)][switch]$TestUnit,
90 [Parameter(Mandatory=$False)][switch]$TestIntegration,
91 [Parameter(Mandatory=$False)][switch]$All
92 )
93
94 $ErrorActionPreference = "Stop"
95 $ProgressPreference = "SilentlyContinue"
96 $pushed=$False # To restore the directory if we have temporarily pushed to one.
97 Set-Variable GOTESTSUM_LOCATION -option Constant -value "$env:GOPATH/bin/"
98
99 # Utility function to get the commit ID of the repository
Get-GitCommit()100 Function Get-GitCommit() {
101 if (-not (Test-Path ".\.git")) {
102 # If we don't have a .git directory, but we do have the environment
103 # variable DOCKER_GITCOMMIT set, that can override it.
104 if ($env:DOCKER_GITCOMMIT.Length -eq 0) {
105 Throw ".git directory missing and DOCKER_GITCOMMIT environment variable not specified."
106 }
107 Write-Host "INFO: Git commit ($env:DOCKER_GITCOMMIT) assumed from DOCKER_GITCOMMIT environment variable"
108 return $env:DOCKER_GITCOMMIT
109 }
110 $gitCommit=$(git rev-parse --short HEAD)
111 if ($(git status --porcelain --untracked-files=no).Length -ne 0) {
112 $gitCommit="$gitCommit-unsupported"
113 Write-Host ""
114 Write-Warning "This version is unsupported because there are uncommitted file(s)."
115 Write-Warning "Either commit these changes, or add them to .gitignore."
116 git status --porcelain --untracked-files=no | Write-Warning
117 Write-Host ""
118 }
119 return $gitCommit
120 }
121
122 # Utility function to determine if we are running in a container or not.
123 # In Windows, we get this through an environment variable set in `Dockerfile.Windows`
Check-InContainer()124 Function Check-InContainer() {
125 if ($env:FROM_DOCKERFILE.Length -eq 0) {
126 Write-Host ""
127 Write-Warning "Not running in a container. The result might be an incorrect build."
128 Write-Host ""
129 return $False
130 }
131 return $True
132 }
133
134 # Utility function to warn if the version of go is correct. Used for local builds
135 # outside of a container where it may be out of date with master.
Verify-GoVersion()136 Function Verify-GoVersion() {
137 Try {
138 $goVersionDockerfile=(Select-String -Path ".\Dockerfile" -Pattern "^ARG[\s]+GO_VERSION=(.*)$").Matches.groups[1].Value -replace '\.0$',''
139 $goVersionInstalled=(go version).ToString().Split(" ")[2].SubString(2)
140 }
141 Catch [Exception] {
142 Throw "Failed to validate go version correctness: $_"
143 }
144 if (-not($goVersionInstalled -eq $goVersionDockerfile)) {
145 Write-Host ""
146 Write-Warning "Building with golang version $goVersionInstalled. You should update to $goVersionDockerfile"
147 Write-Host ""
148 }
149 }
150
151 # Utility function to get the commit for HEAD
Get-HeadCommit()152 Function Get-HeadCommit() {
153 $head = Invoke-Expression "git rev-parse --verify HEAD"
154 if ($LASTEXITCODE -ne 0) { Throw "Failed getting HEAD commit" }
155
156 return $head
157 }
158
159 # Utility function to get the commit for upstream
Get-UpstreamCommit()160 Function Get-UpstreamCommit() {
161 Invoke-Expression "git fetch -q https://github.com/docker/docker.git refs/heads/master"
162 if ($LASTEXITCODE -ne 0) { Throw "Failed fetching" }
163
164 $upstream = Invoke-Expression "git rev-parse --verify FETCH_HEAD"
165 if ($LASTEXITCODE -ne 0) { Throw "Failed getting upstream commit" }
166
167 return $upstream
168 }
169
170 # Build a binary (client or daemon)
Execute-Build($type, $additionalBuildTags, $directory)171 Function Execute-Build($type, $additionalBuildTags, $directory) {
172 # Generate the build flags
173 $buildTags = "autogen"
174 if ($Noisy) { $verboseParm=" -v" }
175 if ($Race) { Write-Warning "Using race detector"; $raceParm=" -race"}
176 if ($ForceBuildAll) { $allParm=" -a" }
177 if ($NoOpt) { $optParm=" -gcflags "+""""+"-N -l"+"""" }
178 if ($additionalBuildTags -ne "") { $buildTags += $(" " + $additionalBuildTags) }
179
180 # Do the go build in the appropriate directory
181 # Note -linkmode=internal is required to be able to debug on Windows.
182 # https://github.com/golang/go/issues/14319#issuecomment-189576638
183 Write-Host "INFO: Building $type..."
184 Push-Location $root\cmd\$directory; $global:pushed=$True
185 $buildCommand = "go build" + `
186 $raceParm + `
187 $verboseParm + `
188 $allParm + `
189 $optParm + `
190 " -tags """ + $buildTags + """" + `
191 " -ldflags """ + "-linkmode=internal" + """" + `
192 " -o $root\bundles\"+$directory+".exe"
193 Invoke-Expression $buildCommand
194 if ($LASTEXITCODE -ne 0) { Throw "Failed to compile $type" }
195 Pop-Location; $global:pushed=$False
196 }
197
198
199 # Validates the DCO marker is present on each commit
Validate-DCO($headCommit, $upstreamCommit)200 Function Validate-DCO($headCommit, $upstreamCommit) {
201 Write-Host "INFO: Validating Developer Certificate of Origin..."
202 # Username may only contain alphanumeric characters or dashes and cannot begin with a dash
203 $usernameRegex='[a-zA-Z0-9][a-zA-Z0-9-]+'
204
205 $dcoPrefix="Signed-off-by:"
206 $dcoRegex="^(Docker-DCO-1.1-)?$dcoPrefix ([^<]+) <([^<>@]+@[^<>]+)>( \(github: ($usernameRegex)\))?$"
207
208 $counts = Invoke-Expression "git diff --numstat $upstreamCommit...$headCommit"
209 if ($LASTEXITCODE -ne 0) { Throw "Failed git diff --numstat" }
210
211 # Counts of adds and deletes after removing multiple white spaces. AWK anyone? :(
212 $adds=0; $dels=0; $($counts -replace '\s+', ' ') | %{
213 $a=$_.Split(" ");
214 if ($a[0] -ne "-") { $adds+=[int]$a[0] }
215 if ($a[1] -ne "-") { $dels+=[int]$a[1] }
216 }
217 if (($adds -eq 0) -and ($dels -eq 0)) {
218 Write-Warning "DCO validation - nothing to validate!"
219 return
220 }
221
222 $commits = Invoke-Expression "git log $upstreamCommit..$headCommit --format=format:%H%n"
223 if ($LASTEXITCODE -ne 0) { Throw "Failed git log --format" }
224 $commits = $($commits -split '\s+' -match '\S')
225 $badCommits=@()
226 $commits | ForEach-Object{
227 # Skip commits with no content such as merge commits etc
228 if ($(git log -1 --format=format: --name-status $_).Length -gt 0) {
229 # Ignore exit code on next call - always process regardless
230 $commitMessage = Invoke-Expression "git log -1 --format=format:%B --name-status $_"
231 if (($commitMessage -match $dcoRegex).Length -eq 0) { $badCommits+=$_ }
232 }
233 }
234 if ($badCommits.Length -eq 0) {
235 Write-Host "Congratulations! All commits are properly signed with the DCO!"
236 } else {
237 $e = "`nThese commits do not have a proper '$dcoPrefix' marker:`n"
238 $badCommits | %{ $e+=" - $_`n"}
239 $e += "`nPlease amend each commit to include a properly formatted DCO marker.`n`n"
240 $e += "Visit the following URL for information about the Docker DCO:`n"
241 $e += "https://github.com/docker/docker/blob/master/CONTRIBUTING.md#sign-your-work`n"
242 Throw $e
243 }
244 }
245
246 # Validates that .\pkg\... is safely isolated from internal code
Validate-PkgImports($headCommit, $upstreamCommit)247 Function Validate-PkgImports($headCommit, $upstreamCommit) {
248 Write-Host "INFO: Validating pkg import isolation..."
249
250 # Get a list of go source-code files which have changed under pkg\. Ignore exit code on next call - always process regardless
251 $files=@(); $files = Invoke-Expression "git diff $upstreamCommit...$headCommit --diff-filter=ACMR --name-only -- `'pkg\*.go`'"
252 $badFiles=@(); $files | ForEach-Object{
253 $file=$_
254 # For the current changed file, get its list of dependencies, sorted and uniqued.
255 $imports = Invoke-Expression "go list -e -f `'{{ .Deps }}`' $file"
256 if ($LASTEXITCODE -ne 0) { Throw "Failed go list for dependencies on $file" }
257 $imports = $imports -Replace "\[" -Replace "\]", "" -Split(" ") | Sort-Object | Get-Unique
258 # Filter out what we are looking for
259 $imports = @() + $imports -NotMatch "^github.com/docker/docker/pkg/" `
260 -NotMatch "^github.com/docker/docker/vendor" `
261 -Match "^github.com/docker/docker" `
262 -Replace "`n", ""
263 $imports | ForEach-Object{ $badFiles+="$file imports $_`n" }
264 }
265 if ($badFiles.Length -eq 0) {
266 Write-Host 'Congratulations! ".\pkg\*.go" is safely isolated from internal code.'
267 } else {
268 $e = "`nThese files import internal code: (either directly or indirectly)`n"
269 $badFiles | ForEach-Object{ $e+=" - $_"}
270 Throw $e
271 }
272 }
273
274 # Validates that changed files are correctly go-formatted
Validate-GoFormat($headCommit, $upstreamCommit)275 Function Validate-GoFormat($headCommit, $upstreamCommit) {
276 Write-Host "INFO: Validating go formatting on changed files..."
277
278 # Verify gofmt is installed
279 if ($(Get-Command gofmt -ErrorAction SilentlyContinue) -eq $nil) { Throw "gofmt does not appear to be installed" }
280
281 # Get a list of all go source-code files which have changed. Ignore exit code on next call - always process regardless
282 $files=@(); $files = Invoke-Expression "git diff $upstreamCommit...$headCommit --diff-filter=ACMR --name-only -- `'*.go`'"
283 $files = $files | Select-String -NotMatch "^vendor/"
284 $badFiles=@(); $files | %{
285 # Deliberately ignore error on next line - treat as failed
286 $content=Invoke-Expression "git show $headCommit`:$_"
287
288 # Next set of hoops are to ensure we have LF not CRLF semantics as otherwise gofmt on Windows will not succeed.
289 # Also note that gofmt on Windows does not appear to support stdin piping correctly. Hence go through a temporary file.
290 $content=$content -join "`n"
291 $content+="`n"
292 $outputFile=[System.IO.Path]::GetTempFileName()
293 if (Test-Path $outputFile) { Remove-Item $outputFile }
294 [System.IO.File]::WriteAllText($outputFile, $content, (New-Object System.Text.UTF8Encoding($False)))
295 $currentFile = $_ -Replace("/","\")
296 Write-Host Checking $currentFile
297 Invoke-Expression "gofmt -s -l $outputFile"
298 if ($LASTEXITCODE -ne 0) { $badFiles+=$currentFile }
299 if (Test-Path $outputFile) { Remove-Item $outputFile }
300 }
301 if ($badFiles.Length -eq 0) {
302 Write-Host 'Congratulations! All Go source files are properly formatted.'
303 } else {
304 $e = "`nThese files are not properly gofmt`'d:`n"
305 $badFiles | ForEach-Object{ $e+=" - $_`n"}
306 $e+= "`nPlease reformat the above files using `"gofmt -s -w`" and commit the result."
307 Throw $e
308 }
309 }
310
311 # Run the unit tests
Run-UnitTests()312 Function Run-UnitTests() {
313 Write-Host "INFO: Running unit tests..."
314 $testPath="./..."
315 $goListCommand = "go list -e -f '{{if ne .Name """ + '\"github.com/docker/docker\"' + """}}{{.ImportPath}}{{end}}' $testPath"
316 $pkgList = $(Invoke-Expression $goListCommand)
317 if ($LASTEXITCODE -ne 0) { Throw "go list for unit tests failed" }
318 $pkgList = $pkgList | Select-String -Pattern "github.com/docker/docker"
319 $pkgList = $pkgList | Select-String -NotMatch "github.com/docker/docker/vendor"
320 $pkgList = $pkgList | Select-String -NotMatch "github.com/docker/docker/man"
321 $pkgList = $pkgList | Select-String -NotMatch "github.com/docker/docker/integration"
322 $pkgList = $pkgList -replace "`r`n", " "
323
324 $goTestArg = "--format=standard-verbose --jsonfile=bundles\go-test-report-unit-tests.json --junitfile=bundles\junit-report-unit-tests.xml -- " + $raceParm + " -cover -ldflags -w -a """ + "-test.timeout=10m" + """ $pkgList"
325 Write-Host "INFO: Invoking unit tests run with $GOTESTSUM_LOCATION\gotestsum.exe $goTestArg"
326 $pinfo = New-Object System.Diagnostics.ProcessStartInfo
327 $pinfo.FileName = "$GOTESTSUM_LOCATION\gotestsum.exe"
328 $pinfo.WorkingDirectory = "$($PWD.Path)"
329 $pinfo.UseShellExecute = $false
330 $pinfo.Arguments = $goTestArg
331 $p = New-Object System.Diagnostics.Process
332 $p.StartInfo = $pinfo
333 $p.Start() | Out-Null
334 $p.WaitForExit()
335 if ($p.ExitCode -ne 0) { Throw "Unit tests failed" }
336 }
337
338 # Run the integration tests
Run-IntegrationTests()339 Function Run-IntegrationTests() {
340 $escRoot = [Regex]::Escape($root)
341 $env:DOCKER_INTEGRATION_DAEMON_DEST = $bundlesDir + "\tmp"
342 $dirs = go list -test -f '{{- if ne .ForTest `"`" -}}{{- .Dir -}}{{- end -}}' .\integration\...
343 ForEach($dir in $dirs) {
344 # Normalize directory name for using in the test results files.
345 $normDir = $dir.Trim()
346 $normDir = $normDir -replace $escRoot, ""
347 $normDir = $normDir -replace "\\", "-"
348 $normDir = $normDir -replace "\/", "-"
349 $normDir = $normDir -replace "\.", "-"
350 if ($normDir.StartsWith("-"))
351 {
352 $normDir = $normDir.TrimStart("-")
353 }
354 if ($normDir.EndsWith("-"))
355 {
356 $normDir = $normDir.TrimEnd("-")
357 }
358 $jsonFilePath = $bundlesDir + "\go-test-report-int-tests-$normDir" + ".json"
359 $xmlFilePath = $bundlesDir + "\junit-report-int-tests-$normDir" + ".xml"
360 Set-Location $dir
361 Write-Host "Running $($PWD.Path)"
362 $pinfo = New-Object System.Diagnostics.ProcessStartInfo
363 $pinfo.FileName = "gotestsum.exe"
364 $pinfo.WorkingDirectory = "$($PWD.Path)"
365 $pinfo.UseShellExecute = $false
366 $pinfo.Arguments = "--format=standard-verbose --jsonfile=$jsonFilePath --junitfile=$xmlFilePath -- $env:INTEGRATION_TESTFLAGS"
367 $p = New-Object System.Diagnostics.Process
368 $p.StartInfo = $pinfo
369 $p.Start() | Out-Null
370 $p.WaitForExit()
371 if ($p.ExitCode -ne 0) { Throw "Integration tests failed" }
372 }
373 }
374
375 # Start of main code.
376 Try {
377 Write-Host -ForegroundColor Cyan "INFO: make.ps1 starting at $(Get-Date)"
378
379 # Get to the root of the repo
380 $root = $(Split-Path $MyInvocation.MyCommand.Definition -Parent | Split-Path -Parent)
381 Push-Location $root
382
383 # Ensure the bundles directory exists
384 $bundlesDir = $root + "\bundles"
385 Set-Variable bundlesDir -option ReadOnly
386 New-Item -Force $bundlesDir -ItemType Directory | Out-Null
387
388 # Handle the "-All" shortcut to turn on all things we can handle.
389 # Note we expressly only include the items which can run in a container - the validations tests cannot
390 # as they require the .git directory which is excluded from the image by .dockerignore
391 if ($All) { $Client=$True; $Daemon=$True; $TestUnit=$True; }
392
393 # Handle the "-Binary" shortcut to build both client and daemon.
394 if ($Binary) { $Client = $True; $Daemon = $True }
395
396 # Default to building the daemon if not asked for anything explicitly.
397 if (-not($Client) -and -not($Daemon) -and -not($DCO) -and -not($PkgImports) -and -not($GoFormat) -and -not($TestUnit) -and -not($TestIntegration)) { $Daemon=$True }
398
399 # Verify git is installed
400 if ($(Get-Command git -ErrorAction SilentlyContinue) -eq $nil) { Throw "Git does not appear to be installed" }
401
402 # Verify go is installed
403 if ($(Get-Command go -ErrorAction SilentlyContinue) -eq $nil) { Throw "GoLang does not appear to be installed" }
404
405 # Get the git commit. This will also verify if we are in a repo or not. Then add a custom string if supplied.
406 $gitCommit=Get-GitCommit
407 if ($CommitSuffix -ne "") { $gitCommit += "-"+$CommitSuffix -Replace ' ', '' }
408
409 # Get the version of docker (eg 17.04.0-dev)
410 $dockerVersion="0.0.0-dev"
411 # Overwrite dockerVersion if VERSION Environment variable is available
412 if (Test-Path Env:\VERSION) { $dockerVersion=$env:VERSION }
413
414 # Give a warning if we are not running in a container and are building binaries or running unit tests.
415 # Not relevant for validation tests as these are fine to run outside of a container.
416 if ($Client -or $Daemon -or $TestUnit) { $inContainer=Check-InContainer }
417
418 # If we are not in a container, validate the version of GO that is installed.
419 if (-not $inContainer) { Verify-GoVersion }
420
421 # Verify GOPATH is set
422 if ($env:GOPATH.Length -eq 0) { Throw "Missing GOPATH environment variable. See https://golang.org/doc/code.html#GOPATH" }
423
424 # Run autogen if building binaries or running unit tests.
425 if ($Client -or $Daemon -or $TestUnit) {
426 Write-Host "INFO: Invoking autogen..."
427 Try { .\hack\make\.go-autogen.ps1 -CommitString $gitCommit -DockerVersion $dockerVersion -Platform "$env:PLATFORM" -Product "$env:PRODUCT" }
428 Catch [Exception] { Throw $_ }
429 }
430
431 # DCO, Package import and Go formatting tests.
432 if ($DCO -or $PkgImports -or $GoFormat) {
433 # We need the head and upstream commits for these
434 $headCommit=Get-HeadCommit
435 $upstreamCommit=Get-UpstreamCommit
436
437 # Run DCO validation
438 if ($DCO) { Validate-DCO $headCommit $upstreamCommit }
439
440 # Run `gofmt` validation
441 if ($GoFormat) { Validate-GoFormat $headCommit $upstreamCommit }
442
443 # Run pkg isolation validation
444 if ($PkgImports) { Validate-PkgImports $headCommit $upstreamCommit }
445 }
446
447 # Build the binaries
448 if ($Client -or $Daemon) {
449
450 # Perform the actual build
451 if ($Daemon) { Execute-Build "daemon" "daemon" "dockerd" }
452 if ($Client) {
453 # Get the Docker channel and version from the environment, or use the defaults.
454 if (-not ($channel = $env:DOCKERCLI_CHANNEL)) { $channel = "stable" }
455 if (-not ($version = $env:DOCKERCLI_VERSION)) { $version = "17.06.2-ce" }
456
457 # Download the zip file and extract the client executable.
458 Write-Host "INFO: Downloading docker/cli version $version from $channel..."
459 $url = "https://download.docker.com/win/static/$channel/x86_64/docker-$version.zip"
460 Invoke-WebRequest $url -OutFile "docker.zip"
461 Try {
462 Add-Type -AssemblyName System.IO.Compression.FileSystem
463 $zip = [System.IO.Compression.ZipFile]::OpenRead("$PWD\docker.zip")
464 Try {
465 if (-not ($entry = $zip.Entries | Where-Object { $_.Name -eq "docker.exe" })) {
466 Throw "Cannot find docker.exe in $url"
467 }
468 [System.IO.Compression.ZipFileExtensions]::ExtractToFile($entry, "$PWD\bundles\docker.exe", $true)
469 }
470 Finally {
471 $zip.Dispose()
472 }
473 }
474 Finally {
475 Remove-Item -Force "docker.zip"
476 }
477 }
478 }
479
480 # Run unit tests
481 if ($TestUnit) { Run-UnitTests }
482
483 # Run integration tests
484 if ($TestIntegration) { Run-IntegrationTests }
485
486 # Gratuitous ASCII art.
487 if ($Daemon -or $Client) {
488 Write-Host
489 Write-Host -ForegroundColor Green " ________ ____ __."
490 Write-Host -ForegroundColor Green " \_____ \ `| `|/ _`|"
491 Write-Host -ForegroundColor Green " / `| \`| `<"
492 Write-Host -ForegroundColor Green " / `| \ `| \"
493 Write-Host -ForegroundColor Green " \_______ /____`|__ \"
494 Write-Host -ForegroundColor Green " \/ \/"
495 Write-Host
496 }
497 }
498 Catch [Exception] {
499 Write-Host -ForegroundColor Red ("`nERROR: make.ps1 failed:`n$_")
500 Write-Host -ForegroundColor Red ($_.InvocationInfo.PositionMessage)
501
502 # More gratuitous ASCII art.
503 Write-Host
504 Write-Host -ForegroundColor Red "___________ .__.__ .___"
505 Write-Host -ForegroundColor Red "\_ _____/____ `|__`| `| ____ __`| _/"
506 Write-Host -ForegroundColor Red " `| __) \__ \ `| `| `| _/ __ \ / __ `| "
507 Write-Host -ForegroundColor Red " `| \ / __ \`| `| `|_\ ___// /_/ `| "
508 Write-Host -ForegroundColor Red " \___ / (____ /__`|____/\___ `>____ `| "
509 Write-Host -ForegroundColor Red " \/ \/ \/ \/ "
510 Write-Host
511
512 exit 1
513 }
514 Finally {
515 Pop-Location # As we pushed to the root of the repo as the very first thing
516 if ($global:pushed) { Pop-Location }
517 Write-Host -ForegroundColor Cyan "INFO: make.ps1 ended at $(Get-Date)"
518 }
519