Skip to content

Commit 0979c2d

Browse files
committed
chore(windows): use docker bake to generate the docker compose file
diff --git a/make.ps1 b/make.ps1 index 3e6e34e..54c9f0c 100644 --- a/make.ps1 +++ b/make.ps1 @@ -1,26 +1,23 @@ [CmdletBinding()] Param( - [Parameter(Position=1)] + [Parameter(Position = 1)] + # Default script target [String] $Target = 'build', + # Jenkins version to include [String] $JenkinsVersion = '2.534', - [switch] $DryRun = $false + # Windows flavor and windows version to build + [String] $ImageType = 'windowsservercore-ltsc2022', + # Generate a docker compose file even if it already exists + [switch] $OverwriteDockerComposeFile = $false, + # Print the build and publish command instead of executing them if set + [switch] $DryRun = $false, + # Output debug info for tests: 'empty' (no additional test output), 'debug' (test cmd & stderr outputed), 'verbose' (test cmd, stderr, stdout outputed) + [String] $TestsDebug = '' ) $ErrorActionPreference = 'Stop' $ProgressPreference = 'SilentlyContinue' # Disable Progress bar for faster downloads -$Repository = 'jenkins' -$Organisation = 'jenkins4eval' -$ImageType = 'windowsservercore-ltsc2022' # <WINDOWS_FLAVOR>-<WINDOWS_VERSION> - -if(![String]::IsNullOrWhiteSpace($env:DOCKERHUB_REPO)) { - $Repository = $env:DOCKERHUB_REPO -} - -if(![String]::IsNullOrWhiteSpace($env:DOCKERHUB_ORGANISATION)) { - $Organisation = $env:DOCKERHUB_ORGANISATION -} - if(![String]::IsNullOrWhiteSpace($env:JENKINS_VERSION)) { $JenkinsVersion = $env:JENKINS_VERSION } @@ -29,28 +26,40 @@ if(![String]::IsNullOrWhiteSpace($env:IMAGE_TYPE)) { $ImageType = $env:IMAGE_TYPE } -$env:DOCKERHUB_ORGANISATION = "$Organisation" -$env:DOCKERHUB_REPO = "$Repository" $env:JENKINS_VERSION = "$JenkinsVersion" -# Add 'lts-' prefix to LTS tags not including Jenkins version -# Compared to weekly releases, LTS releases include an additional build number in their version -# Note: the ':' separator is included as trying to set an environment variable to empty on Windows unset it. -$env:SEPARATOR_LTS_PREFIX = ':' -$releaseLine = 'war' -if ($JenkinsVersion.Split('.').Count -eq 3) { - $env:SEPARATOR_LTS_PREFIX = ':lts-' - $releaseLine = 'war-stable' -} +# Check for required commands +Function Test-CommandExists { + # From https://devblogs.microsoft.com/scripting/use-a-powershell-function-to-see-if-a-command-exists/ + Param ( + [String] $command + ) -# If there is no WAR_URL set, using get.jenkins.io URL depending on the release line -if([String]::IsNullOrWhiteSpace($env:WAR_URL)) { - $env:WAR_URL = 'https://get.jenkins.io/{0}/{1}/jenkins.war' -f $releaseLine, $env:JENKINS_VERSION + $oldPreference = $ErrorActionPreference + $ErrorActionPreference = 'stop' + try { + # Special case to test "docker buildx" + if ($command.Contains(' ')) { + Invoke-Expression $command | Out-Null + Write-Debug "$command exists" + } else { + if(Get-Command $command){ + Write-Debug "$command exists" + } + } + } + Catch { + "$command does not exist" + } + Finally { + $ErrorActionPreference = $oldPreference + } } function Test-Image { param ( - $ImageName + [String] $ImageName, + [String] $JavaVersion ) Write-Host "= TEST: Testing image ${ImageName}:" @@ -79,56 +88,102 @@ function Test-Image { return $failed } -$items = $ImageType.Split('-') -$env:WINDOWS_FLAVOR = $items[0] -$env:WINDOWS_VERSION = $items[1] +function Initialize-DockerComposeFile { + param ( + [String] $ImageType, + [String] $DockerComposeFile + ) + + $baseDockerBakeCmd = 'docker buildx bake --progress=plain --file=docker-bake.hcl' + + $items = $ImageType.Split('-') + $windowsFlavor = $items[0] + $windowsVersion = $items[1] + + # Override the list of Windows versions taken defined in docker-bake.hcl by the version from image type + $env:WINDOWS_VERSION_OVERRIDE = $windowsVersion -# Retrieve the sha256 corresponding to the war file -$warShaURL = '{0}.sha256' -f $env:WAR_URL -$webClient = New-Object System.Net.WebClient -$env:WAR_SHA = $webClient.DownloadString($warShaURL).Split(' ')[0] + # Retrieve the targets from docker buildx bake --print output + # Remove the 'output' section (unsupported by docker compose) + # For each target name as service key, return a map consisting of: + # - 'image' set to the first tag value + # - 'build' set to the content of the bake target + $yqMainQuery = '''.target[]' + ` + ' | del(.output)' + ` + ' | {(. | key): {\"image\": .tags[0], \"build\": .}}''' + # Encapsulate under a top level 'services' map + $yqServicesQuery = '''{\"services\": .}''' -$env:COMMIT_SHA=$(git rev-parse HEAD) + # - Use docker buildx bake to output image definitions from the "<windowsFlavor>" bake target + # - Convert with yq to the format expected by docker compose + # - Store the result in the docker compose file + $generateDockerComposeFileCmd = ' {0} {1} --print' -f $baseDockerBakeCmd, $windowsFlavor + ` + ' | yq --prettyPrint {0} | yq {1}' -f $yqMainQuery, $yqServicesQuery + ` + ' | Out-File -FilePath {0}' -f $DockerComposeFile -$baseDockerCmd = 'docker-compose --file=build-windows.yaml' + Write-Host "= PREPARE: Docker compose file generation command`n$generateDockerComposeFileCmd" + + Invoke-Expression $generateDockerComposeFileCmd + + # Remove override + Remove-Item env:\WINDOWS_VERSION_OVERRIDE +} + +Test-CommandExists 'docker' +Test-CommandExists 'docker-compose' +Test-CommandExists 'docker buildx' +Test-CommandExists 'yq' + +$dockerComposeFile = 'build-windows_{1}.yaml' -f $ImageType +$baseDockerCmd = 'docker-compose --file={0}' -f $dockerComposeFile $baseDockerBuildCmd = '{0} build --parallel --pull' -f $baseDockerCmd -Write-Host "= PREPARE: List of $Organisation/$Repository images and tags to be processed:" +# Generate the docker compose file if it doesn't exists or if the parameter OverwriteDockerComposeFile is set +if ((Test-Path $dockerComposeFile) -and -not $OverwriteDockerComposeFile) { + Write-Host "= PREPARE: The docker compose file '$dockerComposeFile' containing the image definitions already exists." +} else { + Write-Host "= PREPARE: Initialize the docker compose file '$dockerComposeFile' containing the image definitions." + Initialize-DockerComposeFile -ImageType $ImageType -DockerComposeFile $dockerComposeFile +} + +Write-Host '= PREPARE: List of images and tags to be processed:' Invoke-Expression "$baseDockerCmd config" -if($target -eq 'build') { +if ($target -eq 'build') { Write-Host '= BUILD: Building all images...' + switch ($DryRun) { $true { Write-Host "(dry-run) $baseDockerBuildCmd" } $false { Invoke-Expression $baseDockerBuildCmd } } - Write-Host '= BUILD: Finished building all images.' - if($lastExitCode -ne 0 -and !$DryRun) { - Write-Error "= BUILD: failed!" + if ($lastExitCode -ne 0) { exit $lastExitCode } + + Write-Host '= BUILD: Finished building all images.' } -if($target -eq 'test') { +if ($target -eq 'test') { if ($DryRun) { - Write-Host '(dry-run) test' + Write-Host '= TEST: (dry-run) test harness' } else { - # Only fail the run afterwards in case of any test failures - $testFailed = $false + Write-Host '= TEST: Starting test harness' + $mod = Get-InstalledModule -Name Pester -MinimumVersion 5.3.0 -MaximumVersion 5.3.3 -ErrorAction SilentlyContinue - if($null -eq $mod) { - $module = 'c:\Program Files\WindowsPowerShell\Modules\Pester' - if(Test-Path $module) { + if ($null -eq $mod) { + Write-Host '= TEST: Pester 5.3.x not found: installing...' + $module = 'C:\Program Files\WindowsPowerShell\Modules\Pester' + if (Test-Path $module) { takeown /F $module /A /R icacls $module /reset icacls $module /grant Administrators:'F' /inheritance:d /T Remove-Item -Path $module -Recurse -Force -Confirm:$false } - Install-Module -Force -Name Pester -Verbose -MaximumVersion 5.3.3 + Install-Module -Force -Name Pester -MaximumVersion 5.3.3 } - Import-Module -Verbose Pester + Import-Module Pester Write-Host '= TEST: Setting up Pester environment...' $configuration = [PesterConfiguration]::Default $configuration.Run.PassThru = $true @@ -139,40 +194,34 @@ if($target -eq 'test') { $configuration.Output.Verbosity = 'Diagnostic' $configuration.CodeCoverage.Enabled = $false - Write-Host '= TEST: Testing all images...' + Write-Host "= TEST: Testing all images..." # Only fail the run afterwards in case of any test failures $testFailed = $false - Invoke-Expression "$baseDockerCmd config" | yq '.services[].image' | ForEach-Object { - $testFailed = $testFailed -or (Test-Image $_.split(':')[1]) + $imageDefinitions = Invoke-Expression "$baseDockerCmd config" | yq --unwrapScalar --output-format json '.services' | ConvertFrom-Json + foreach ($imageDefinition in $imageDefinitions.PSObject.Properties) { + $testFailed = $testFailed -or (Test-Image -ImageName $imageDefinition.Value.image -JavaVersion $imageDefinition.Value.build.args.JAVA_VERSION) } # Fail if any test failures - if($testFailed -ne $false) { - Write-Error 'Test stage failed!' + if ($testFailed -ne $false) { + Write-Error "Test stage failed for ${agentType}!" exit 1 } else { - Write-Host 'Test stage passed!' + Write-Host "= TEST: stage passed for ${agentType}!" } } } -if($target -eq "publish") { - Write-Host "= PUBLISH: push all images and tags" +if ($target -eq 'publish') { + Write-Host '= PUBLISH: push all images and tags' switch($DryRun) { $true { Write-Host "(dry-run) $baseDockerCmd push" } $false { Invoke-Expression "$baseDockerCmd push" } } # Fail if any issues when publising the docker images - if($lastExitCode -ne 0) { - Write-Error "= PUBLISH: failed!" + if ($lastExitCode -ne 0) { + Write-Error '= PUBLISH: failed!' exit 1 } } - -if($lastExitCode -ne 0 -and !$DryRun) { - Write-Error 'Build failed!' -} else { - Write-Host 'Build finished successfully' -} -exit $lastExitCode
1 parent d83432b commit 0979c2d

File tree

1 file changed

+118
-69
lines changed

1 file changed

+118
-69
lines changed

make.ps1

Lines changed: 118 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,23 @@
11
[CmdletBinding()]
22
Param(
3-
[Parameter(Position=1)]
3+
[Parameter(Position = 1)]
4+
# Default script target
45
[String] $Target = 'build',
6+
# Jenkins version to include
57
[String] $JenkinsVersion = '2.534',
6-
[switch] $DryRun = $false
8+
# Windows flavor and windows version to build
9+
[String] $ImageType = 'windowsservercore-ltsc2022',
10+
# Generate a docker compose file even if it already exists
11+
[switch] $OverwriteDockerComposeFile = $false,
12+
# Print the build and publish command instead of executing them if set
13+
[switch] $DryRun = $false,
14+
# Output debug info for tests: 'empty' (no additional test output), 'debug' (test cmd & stderr outputed), 'verbose' (test cmd, stderr, stdout outputed)
15+
[String] $TestsDebug = ''
716
)
817

918
$ErrorActionPreference = 'Stop'
1019
$ProgressPreference = 'SilentlyContinue' # Disable Progress bar for faster downloads
1120

12-
$Repository = 'jenkins'
13-
$Organisation = 'jenkins4eval'
14-
$ImageType = 'windowsservercore-ltsc2022' # <WINDOWS_FLAVOR>-<WINDOWS_VERSION>
15-
16-
if(![String]::IsNullOrWhiteSpace($env:DOCKERHUB_REPO)) {
17-
$Repository = $env:DOCKERHUB_REPO
18-
}
19-
20-
if(![String]::IsNullOrWhiteSpace($env:DOCKERHUB_ORGANISATION)) {
21-
$Organisation = $env:DOCKERHUB_ORGANISATION
22-
}
23-
2421
if(![String]::IsNullOrWhiteSpace($env:JENKINS_VERSION)) {
2522
$JenkinsVersion = $env:JENKINS_VERSION
2623
}
@@ -29,28 +26,40 @@ if(![String]::IsNullOrWhiteSpace($env:IMAGE_TYPE)) {
2926
$ImageType = $env:IMAGE_TYPE
3027
}
3128

32-
$env:DOCKERHUB_ORGANISATION = "$Organisation"
33-
$env:DOCKERHUB_REPO = "$Repository"
3429
$env:JENKINS_VERSION = "$JenkinsVersion"
3530

36-
# Add 'lts-' prefix to LTS tags not including Jenkins version
37-
# Compared to weekly releases, LTS releases include an additional build number in their version
38-
# Note: the ':' separator is included as trying to set an environment variable to empty on Windows unset it.
39-
$env:SEPARATOR_LTS_PREFIX = ':'
40-
$releaseLine = 'war'
41-
if ($JenkinsVersion.Split('.').Count -eq 3) {
42-
$env:SEPARATOR_LTS_PREFIX = ':lts-'
43-
$releaseLine = 'war-stable'
44-
}
31+
# Check for required commands
32+
Function Test-CommandExists {
33+
# From https://devblogs.microsoft.com/scripting/use-a-powershell-function-to-see-if-a-command-exists/
34+
Param (
35+
[String] $command
36+
)
4537

46-
# If there is no WAR_URL set, using get.jenkins.io URL depending on the release line
47-
if([String]::IsNullOrWhiteSpace($env:WAR_URL)) {
48-
$env:WAR_URL = 'https://get.jenkins.io/{0}/{1}/jenkins.war' -f $releaseLine, $env:JENKINS_VERSION
38+
$oldPreference = $ErrorActionPreference
39+
$ErrorActionPreference = 'stop'
40+
try {
41+
# Special case to test "docker buildx"
42+
if ($command.Contains(' ')) {
43+
Invoke-Expression $command | Out-Null
44+
Write-Debug "$command exists"
45+
} else {
46+
if(Get-Command $command){
47+
Write-Debug "$command exists"
48+
}
49+
}
50+
}
51+
Catch {
52+
"$command does not exist"
53+
}
54+
Finally {
55+
$ErrorActionPreference = $oldPreference
56+
}
4957
}
5058

5159
function Test-Image {
5260
param (
53-
$ImageName
61+
[String] $ImageName,
62+
[String] $JavaVersion
5463
)
5564

5665
Write-Host "= TEST: Testing image ${ImageName}:"
@@ -79,56 +88,102 @@ function Test-Image {
7988
return $failed
8089
}
8190

82-
$items = $ImageType.Split('-')
83-
$env:WINDOWS_FLAVOR = $items[0]
84-
$env:WINDOWS_VERSION = $items[1]
91+
function Initialize-DockerComposeFile {
92+
param (
93+
[String] $ImageType,
94+
[String] $DockerComposeFile
95+
)
96+
97+
$baseDockerBakeCmd = 'docker buildx bake --progress=plain --file=docker-bake.hcl'
98+
99+
$items = $ImageType.Split('-')
100+
$windowsFlavor = $items[0]
101+
$windowsVersion = $items[1]
102+
103+
# Override the list of Windows versions taken defined in docker-bake.hcl by the version from image type
104+
$env:WINDOWS_VERSION_OVERRIDE = $windowsVersion
85105

86-
# Retrieve the sha256 corresponding to the war file
87-
$warShaURL = '{0}.sha256' -f $env:WAR_URL
88-
$webClient = New-Object System.Net.WebClient
89-
$env:WAR_SHA = $webClient.DownloadString($warShaURL).Split(' ')[0]
106+
# Retrieve the targets from docker buildx bake --print output
107+
# Remove the 'output' section (unsupported by docker compose)
108+
# For each target name as service key, return a map consisting of:
109+
# - 'image' set to the first tag value
110+
# - 'build' set to the content of the bake target
111+
$yqMainQuery = '''.target[]' + `
112+
' | del(.output)' + `
113+
' | {(. | key): {\"image\": .tags[0], \"build\": .}}'''
114+
# Encapsulate under a top level 'services' map
115+
$yqServicesQuery = '''{\"services\": .}'''
90116

91-
$env:COMMIT_SHA=$(git rev-parse HEAD)
117+
# - Use docker buildx bake to output image definitions from the "<windowsFlavor>" bake target
118+
# - Convert with yq to the format expected by docker compose
119+
# - Store the result in the docker compose file
120+
$generateDockerComposeFileCmd = ' {0} {1} --print' -f $baseDockerBakeCmd, $windowsFlavor + `
121+
' | yq --prettyPrint {0} | yq {1}' -f $yqMainQuery, $yqServicesQuery + `
122+
' | Out-File -FilePath {0}' -f $DockerComposeFile
92123

93-
$baseDockerCmd = 'docker-compose --file=build-windows.yaml'
124+
Write-Host "= PREPARE: Docker compose file generation command`n$generateDockerComposeFileCmd"
125+
126+
Invoke-Expression $generateDockerComposeFileCmd
127+
128+
# Remove override
129+
Remove-Item env:\WINDOWS_VERSION_OVERRIDE
130+
}
131+
132+
Test-CommandExists 'docker'
133+
Test-CommandExists 'docker-compose'
134+
Test-CommandExists 'docker buildx'
135+
Test-CommandExists 'yq'
136+
137+
$dockerComposeFile = 'build-windows_{1}.yaml' -f $ImageType
138+
$baseDockerCmd = 'docker-compose --file={0}' -f $dockerComposeFile
94139
$baseDockerBuildCmd = '{0} build --parallel --pull' -f $baseDockerCmd
95140

96-
Write-Host "= PREPARE: List of $Organisation/$Repository images and tags to be processed:"
141+
# Generate the docker compose file if it doesn't exists or if the parameter OverwriteDockerComposeFile is set
142+
if ((Test-Path $dockerComposeFile) -and -not $OverwriteDockerComposeFile) {
143+
Write-Host "= PREPARE: The docker compose file '$dockerComposeFile' containing the image definitions already exists."
144+
} else {
145+
Write-Host "= PREPARE: Initialize the docker compose file '$dockerComposeFile' containing the image definitions."
146+
Initialize-DockerComposeFile -ImageType $ImageType -DockerComposeFile $dockerComposeFile
147+
}
148+
149+
Write-Host '= PREPARE: List of images and tags to be processed:'
97150
Invoke-Expression "$baseDockerCmd config"
98151

99-
if($target -eq 'build') {
152+
if ($target -eq 'build') {
100153
Write-Host '= BUILD: Building all images...'
154+
101155
switch ($DryRun) {
102156
$true { Write-Host "(dry-run) $baseDockerBuildCmd" }
103157
$false { Invoke-Expression $baseDockerBuildCmd }
104158
}
105-
Write-Host '= BUILD: Finished building all images.'
106159

107-
if($lastExitCode -ne 0 -and !$DryRun) {
108-
Write-Error "= BUILD: failed!"
160+
if ($lastExitCode -ne 0) {
109161
exit $lastExitCode
110162
}
163+
164+
Write-Host '= BUILD: Finished building all images.'
111165
}
112166

113-
if($target -eq 'test') {
167+
if ($target -eq 'test') {
114168
if ($DryRun) {
115-
Write-Host '(dry-run) test'
169+
Write-Host '= TEST: (dry-run) test harness'
116170
} else {
117-
# Only fail the run afterwards in case of any test failures
118-
$testFailed = $false
171+
Write-Host '= TEST: Starting test harness'
172+
119173
$mod = Get-InstalledModule -Name Pester -MinimumVersion 5.3.0 -MaximumVersion 5.3.3 -ErrorAction SilentlyContinue
120-
if($null -eq $mod) {
121-
$module = 'c:\Program Files\WindowsPowerShell\Modules\Pester'
122-
if(Test-Path $module) {
174+
if ($null -eq $mod) {
175+
Write-Host '= TEST: Pester 5.3.x not found: installing...'
176+
$module = 'C:\Program Files\WindowsPowerShell\Modules\Pester'
177+
if (Test-Path $module) {
123178
takeown /F $module /A /R
124179
icacls $module /reset
125180
icacls $module /grant Administrators:'F' /inheritance:d /T
126181
Remove-Item -Path $module -Recurse -Force -Confirm:$false
127182
}
128-
Install-Module -Force -Name Pester -Verbose -MaximumVersion 5.3.3
183+
Install-Module -Force -Name Pester -MaximumVersion 5.3.3
129184
}
130185

131-
Import-Module -Verbose Pester
186+
Import-Module Pester
132187
Write-Host '= TEST: Setting up Pester environment...'
133188
$configuration = [PesterConfiguration]::Default
134189
$configuration.Run.PassThru = $true
@@ -139,40 +194,34 @@ if($target -eq 'test') {
139194
$configuration.Output.Verbosity = 'Diagnostic'
140195
$configuration.CodeCoverage.Enabled = $false
141196

142-
Write-Host '= TEST: Testing all images...'
197+
Write-Host "= TEST: Testing all images..."
143198
# Only fail the run afterwards in case of any test failures
144199
$testFailed = $false
145-
Invoke-Expression "$baseDockerCmd config" | yq '.services[].image' | ForEach-Object {
146-
$testFailed = $testFailed -or (Test-Image $_.split(':')[1])
200+
$imageDefinitions = Invoke-Expression "$baseDockerCmd config" | yq --unwrapScalar --output-format json '.services' | ConvertFrom-Json
201+
foreach ($imageDefinition in $imageDefinitions.PSObject.Properties) {
202+
$testFailed = $testFailed -or (Test-Image -ImageName $imageDefinition.Value.image -JavaVersion $imageDefinition.Value.build.args.JAVA_VERSION)
147203
}
148204

149205
# Fail if any test failures
150-
if($testFailed -ne $false) {
151-
Write-Error 'Test stage failed!'
206+
if ($testFailed -ne $false) {
207+
Write-Error "Test stage failed for ${agentType}!"
152208
exit 1
153209
} else {
154-
Write-Host 'Test stage passed!'
210+
Write-Host "= TEST: stage passed for ${agentType}!"
155211
}
156212
}
157213
}
158214

159-
if($target -eq "publish") {
160-
Write-Host "= PUBLISH: push all images and tags"
215+
if ($target -eq 'publish') {
216+
Write-Host '= PUBLISH: push all images and tags'
161217
switch($DryRun) {
162218
$true { Write-Host "(dry-run) $baseDockerCmd push" }
163219
$false { Invoke-Expression "$baseDockerCmd push" }
164220
}
165221

166222
# Fail if any issues when publising the docker images
167-
if($lastExitCode -ne 0) {
168-
Write-Error "= PUBLISH: failed!"
223+
if ($lastExitCode -ne 0) {
224+
Write-Error '= PUBLISH: failed!'
169225
exit 1
170226
}
171227
}
172-
173-
if($lastExitCode -ne 0 -and !$DryRun) {
174-
Write-Error 'Build failed!'
175-
} else {
176-
Write-Host 'Build finished successfully'
177-
}
178-
exit $lastExitCode

0 commit comments

Comments
 (0)