diff --git a/.gitignore b/.gitignore index 1b76725e95..aee4edab51 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,6 @@ work-pester-jenkins-windows/ /**/windows/**/jenkins-plugin-cli.ps1 /**/windows/**/jenkins-support.psm1 +build-windows_*.yaml + tests/**/Dockerfile\.* diff --git a/Jenkinsfile b/Jenkinsfile index 61a25a1b23..2518e6931b 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -47,11 +47,15 @@ stage('Build') { withEnv(envVars) { echo '= bake target: linux' - def windowsImageTypes = ['windowsservercore-ltsc2022'] + def windowsImageTypes = [ + 'windowsservercore-ltsc2022' + ] for (anImageType in windowsImageTypes) { def imageType = anImageType builds[imageType] = { - nodeWithTimeout('windows-2022') { + def windowsVersionNumber = imageType.split('-')[1].replace('ltsc', '') + def windowsLabel = "windows-${windowsVersionNumber}" + nodeWithTimeout(windowsLabel) { stage('Checkout') { checkout scm } @@ -63,13 +67,14 @@ stage('Build') { */ stage("Build ${imageType}") { infra.withDockerCredentials { - powershell './make.ps1 build' + powershell './make.ps1 build -ImageType ${env:IMAGE_TYPE}' + archiveArtifacts artifacts: 'build-windows_*.yaml', allowEmptyArchive: true } } stage("Test ${imageType}") { infra.withDockerCredentials { - def windowsTestStatus = powershell(script: './make.ps1 test', returnStatus: true) + def windowsTestStatus = powershell(script: './make.ps1 test -ImageType ${env:IMAGE_TYPE}', returnStatus: true) junit(allowEmptyResults: true, keepLongStdio: true, testResults: 'target/**/junit-results.xml') if (windowsTestStatus > 0) { // If something bad happened let's clean up the docker images @@ -102,8 +107,8 @@ stage('Build') { stage('Publish') { infra.withDockerCredentials { withEnv(['DOCKERHUB_ORGANISATION=jenkins', 'DOCKERHUB_REPO=jenkins']) { - powershell './make.ps1 build' - powershell './make.ps1 publish' + powershell './make.ps1 build -ImageType ${env:IMAGE_TYPE}' + powershell './make.ps1 publish -ImageType ${env:IMAGE_TYPE}' } } } @@ -116,8 +121,7 @@ stage('Build') { } if (!infra.isTrusted()) { - // This list can be updated with the following command: - // make show | jq -r '.target | keys[]' | sort + // An up to date list can be obtained with make list-linux def images = [ 'alpine_jdk21', 'alpine_jdk25', diff --git a/Makefile b/Makefile index 1b8a745233..4e52ea3057 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,17 @@ export BUILDKIT_PROGRESS=plain ## Required to have the commit SHA added as a Docker image label export COMMIT_SHA=$(shell git rev-parse HEAD) +current_os := $(shell uname -s) current_arch := $(shell uname -m) + +export OS ?= $(shell \ + case "$(current_os)" in \ + (Linux) echo linux ;; \ + (Darwin) echo linux ;; \ + (MINGW*|MSYS*|CYGWIN*) echo windows ;; \ + (*) echo unknown ;; \ + esac) + export ARCH ?= $(shell \ case $(current_arch) in \ (x86_64) echo "amd64" ;; \ @@ -31,11 +41,11 @@ TEST_SUITES ?= $(CURDIR)/tests ## Check the presence of a CLI in the current PATH check_cli = type "$(1)" >/dev/null 2>&1 || { echo "Error: command '$(1)' required but not found. Exiting." ; exit 1 ; } ## Check if a given image exists in the current manifest docker-bake.hcl -check_image = make --silent list | grep -w '$(1)' >/dev/null 2>&1 || { echo "Error: the image '$(1)' does not exist in manifest for the platform 'linux/$(ARCH)'. Please check the output of 'make list'. Exiting." ; exit 1 ; } +check_image = make --silent list | grep -w '$(1)' >/dev/null 2>&1 || { echo "Error: the image '$(1)' does not exist in manifest for the current platform '$(OS)/$(ARCH)'. Please check the output of 'make list'. Exiting." ; exit 1 ; } ## Base "docker buildx base" command to be reused everywhere bake_base_cli := docker buildx bake -f docker-bake.hcl --load ## Default bake target -bake_default_target := linux +bake_default_target := all check-reqs: ## Build requirements @@ -72,18 +82,18 @@ hadolint: shellcheck: @$(ROOT_DIR)/tools/shellcheck -e SC1091 jenkins-support *.sh tests/test_helpers.bash tools/hadolint tools/shellcheck .ci/publish.sh -# Build targets depending on the current architecture +# Build all targets with the current OS and architecture build: check-reqs target - @set -x; $(bake_base_cli) --metadata-file=target/build-result-metadata_$(bake_default_target).json --set '*.platform=linux/$(ARCH)' $(shell make --silent list) + @set -x; $(bake_base_cli) --metadata-file=target/build-result-metadata_$(bake_default_target).json --set '*.platform=$(OS)/$(ARCH)' $(shell make --silent list) -# Build targets depending on the architecture +# Build targets depending on the architecture (Linux only, no multiarch for Windows) buildarch-%: check-reqs target showarch-% @set -x; $(bake_base_cli) --metadata-file=target/build-result-metadata_$*.json --set '*.platform=linux/$*' $(shell make --silent listarch-$*) -# Build a specific target with the current architecture +# Build a specific target with the current OS and architecture build-%: check-reqs target show-% @$(call check_image,$*) - @set -x; $(bake_base_cli) --metadata-file=target/build-result-metadata_$*.json --set '*.platform=linux/$(ARCH)' '$*' + @set -x; $(bake_base_cli) --metadata-file=target/build-result-metadata_$*.json --set '*.platform=$(OS)/$(ARCH)' '$*' # Show all targets show: @@ -91,25 +101,37 @@ show: # Show a specific target show-%: - @set -x; $(bake_base_cli) --progress=quiet $* --print | jq + @set -x; $(bake_base_cli) --progress=quiet '$*' --print | jq # Show all targets depending on the architecture showarch-%: - @set -x; make --silent show | jq --arg arch "linux/$*" '.target |= with_entries(select(.value.platforms | index($$arch)))' + @set -x; make --silent show | jq --arg arch "$(OS)/$*" '.target |= with_entries(select(.value.platforms | index($$arch)))' -# List all tags +# List tags of all targets tags: - @set -x; make show | jq -r ' .target | to_entries[] | .key as $$name | .value.tags[] | "\(.) (\($$name))"' | LC_ALL=C sort -u + @set -x; make tags-$(bake_default_target) + +# List tags of a specific target +tags-%: + @set -x; make show-$* | jq -r ' .target | to_entries[] | .key as $$name | .value.tags[] | "\(.) (\($$name))"' | LC_ALL=C sort -u # List all platforms platforms: - @set -x; make show | jq -r ' .target | to_entries[] | .key as $$name | .value.platforms[] | "\($$name):\(.)"' | LC_ALL=C sort -u + @set -x; make platforms-$(bake_default_target) + +# List platforms of a specific target +platforms-%: + @set -x; make show-$* | jq -r ' .target | to_entries[] | .key as $$name | .value.platforms[] | "\($$name):\(.)"' | LC_ALL=C sort -u -# Return the list of targets depending on the current architecture +# Return the list of targets depending on the current OS and architecture list: check-reqs - @set -x; make --silent showarch-$(ARCH) | jq -r '.target | keys[]' + @set -x; make --silent listarch-$(ARCH) + +# Return the list of targets of a specific "target" (can be a docker bake group) +list-%: check-reqs + @set -x; make --silent show-$* | jq -r '.target | keys[]' -# Return the list of targets depending on the architecture +# Return the list of targets depending on the architecture (Linux only, no multiarch for Windows) listarch-%: check-reqs @set -x; make --silent showarch-$* | jq -r '.target | keys[]' diff --git a/build-windows.yaml b/build-windows.yaml deleted file mode 100644 index 1515a5397e..0000000000 --- a/build-windows.yaml +++ /dev/null @@ -1,35 +0,0 @@ -services: - jdk21: - image: ${DOCKERHUB_ORGANISATION}/${DOCKERHUB_REPO}${SEPARATOR_LTS_PREFIX}jdk21-hotspot-${WINDOWS_FLAVOR}-${WINDOWS_VERSION} - build: - context: ./ - dockerfile: ./windows/${WINDOWS_FLAVOR}/hotspot/Dockerfile - args: - COMMIT_SHA: ${COMMIT_SHA} - JAVA_HOME: "C:/openjdk-21" - JAVA_VERSION: 21.0.9_10 - WAR_SHA: ${WAR_SHA} - JENKINS_VERSION: ${JENKINS_VERSION} - WAR_URL: ${WAR_URL} - WINDOWS_VERSION: ${WINDOWS_VERSION} - tags: - - ${DOCKERHUB_ORGANISATION}/${DOCKERHUB_REPO}:${JENKINS_VERSION}-jdk21-hotspot-${WINDOWS_FLAVOR}-${WINDOWS_VERSION} - - ${DOCKERHUB_ORGANISATION}/${DOCKERHUB_REPO}:${JENKINS_VERSION}-${WINDOWS_FLAVOR}-${WINDOWS_VERSION} - - ${DOCKERHUB_ORGANISATION}/${DOCKERHUB_REPO}${SEPARATOR_LTS_PREFIX}${WINDOWS_FLAVOR}-${WINDOWS_VERSION} - jdk25: - image: ${DOCKERHUB_ORGANISATION}/${DOCKERHUB_REPO}${SEPARATOR_LTS_PREFIX}jdk25-hotspot-${WINDOWS_FLAVOR}-${WINDOWS_VERSION} - build: - context: ./ - dockerfile: ./windows/${WINDOWS_FLAVOR}/hotspot/Dockerfile - args: - COMMIT_SHA: ${COMMIT_SHA} - JAVA_HOME: "C:/openjdk-25" - JAVA_VERSION: 25.0.1_8 - WAR_SHA: ${WAR_SHA} - JENKINS_VERSION: ${JENKINS_VERSION} - WAR_URL: ${WAR_URL} - WINDOWS_VERSION: ${WINDOWS_VERSION} - tags: - - ${DOCKERHUB_ORGANISATION}/${DOCKERHUB_REPO}:${JENKINS_VERSION}-jdk25-hotspot-${WINDOWS_FLAVOR}-${WINDOWS_VERSION} - - ${DOCKERHUB_ORGANISATION}/${DOCKERHUB_REPO}:${JENKINS_VERSION}-${WINDOWS_FLAVOR}-${WINDOWS_VERSION} - - ${DOCKERHUB_ORGANISATION}/${DOCKERHUB_REPO}${SEPARATOR_LTS_PREFIX}${WINDOWS_FLAVOR}-${WINDOWS_VERSION} diff --git a/docker-bake.hcl b/docker-bake.hcl index cc8a77faa3..901dab9035 100644 --- a/docker-bake.hcl +++ b/docker-bake.hcl @@ -7,6 +7,14 @@ variable "jdks_to_build_for_weekly" { default = [21, 25] } +variable "windows_version_to_build_for_lts" { + default = ["windowsservercore-ltsc2019"] +} + +variable "windows_version_to_build_for_weekly" { + default = ["windowsservercore-ltsc2022"] +} + variable "default_jdk" { default = 21 } @@ -83,6 +91,11 @@ variable "RHEL_RELEASE_LINE" { default = "ubi9" } +# Set this value to a specific Windows version to override Windows versions to build returned by windowsversions function +variable "WINDOWS_VERSION_OVERRIDE" { + default = "" +} + ## Internal variables variable "jdk_versions" { default = { @@ -114,8 +127,8 @@ target "alpine" { WAR_URL = war_url() COMMIT_SHA = COMMIT_SHA PLUGIN_CLI_VERSION = PLUGIN_CLI_VERSION - ALPINE_TAG = ALPINE_FULL_TAG JAVA_VERSION = javaversion(jdk) + ALPINE_TAG = ALPINE_FULL_TAG } tags = linux_tags("alpine", jdk) platforms = platforms("alpine", jdk) @@ -135,10 +148,10 @@ target "debian" { WAR_URL = war_url() COMMIT_SHA = COMMIT_SHA PLUGIN_CLI_VERSION = PLUGIN_CLI_VERSION + JAVA_VERSION = javaversion(jdk) DEBIAN_RELEASE_LINE = DEBIAN_RELEASE_LINE DEBIAN_VERSION = DEBIAN_VERSION DEBIAN_VARIANT = is_debian_slim(variant) ? "-slim" : "" - JAVA_VERSION = javaversion(jdk) } tags = linux_tags(variant, jdk) platforms = platforms(variant, jdk) @@ -157,14 +170,36 @@ target "rhel" { WAR_URL = war_url() COMMIT_SHA = COMMIT_SHA PLUGIN_CLI_VERSION = PLUGIN_CLI_VERSION + JAVA_VERSION = javaversion(jdk) RHEL_TAG = RHEL_TAG RHEL_RELEASE_LINE = RHEL_RELEASE_LINE - JAVA_VERSION = javaversion(jdk) } tags = linux_tags(current_rhel, jdk) platforms = platforms(current_rhel, jdk) } +target "windowsservercore" { + matrix = { + jdk = jdks_to_build() + windows_version = windowsversions() + } + name = "${windows_version}_jdk${jdk}" + dockerfile = "windows/windowsservercore/hotspot/Dockerfile" + context = "." + args = { + JENKINS_VERSION = JENKINS_VERSION + WAR_SHA = WAR_SHA + WAR_URL = war_url() + COMMIT_SHA = COMMIT_SHA + PLUGIN_CLI_VERSION = PLUGIN_CLI_VERSION + JAVA_VERSION = javaversion(jdk) + JAVA_HOME = "C:/openjdk-${jdk}" + WINDOWS_VERSION = windows_version + } + tags = windows_tags(windows_version, jdk) + platforms = ["windows/amd64"] +} + ## Groups group "linux" { targets = [ @@ -174,6 +209,19 @@ group "linux" { ] } +group "windows" { + targets = [ + "windowsservercore" + ] +} + +group "all" { + targets = [ + "linux", + "windows", + ] +} + ## Common functions # return true if JENKINS_VERSION is a Weekly (one sequence of digits with a trailing literal '.') function "is_jenkins_version_weekly" { @@ -290,6 +338,25 @@ function "linux_tags" { ) } +# Return an array of tags depending on the agent type, the jdk +# and the flavor and version of Windows passed as parameters (ex: windowsservercore-ltsc2022) +function "windows_tags" { + params = [distribution, jdk] + result = [ + ## Always publish explicit jdk tag + tag(true, "jdk${jdk}-hotspot-${distribution}"), + tag_weekly(false, "jdk${jdk}-hotspot-${distribution}"), + tag_lts(false, "lts-jdk${jdk}-hotspot-${distribution}"), + + # ## Default JDK extra short tags + is_default_jdk(jdk) ? tag(true, "hotspot-${distribution}") : "", + is_default_jdk(jdk) ? tag_weekly(false, distribution) : "", + is_default_jdk(jdk) ? tag_weekly(true, distribution) : "", + is_default_jdk(jdk) ? tag_lts(false, "lts-${distribution}") : "", + is_default_jdk(jdk) ? tag_lts(true, distribution) : "", + ] +} + # Return if the distribution passed in parameter is Alpine function "is_alpine" { params = [distribution] @@ -351,3 +418,13 @@ function "debian_tags" { is_default_jdk(jdk) ? tag_lts(true, slim_suffix(variant, "lts")) : "", ] } + +# Return array of Windows version(s) to build +# Can be overriden by setting WINDOWS_VERSION_OVERRIDE to a specific Windows version +# Ex: WINDOWS_VERSION_OVERRIDE=ltsc2025 docker buildx bake windows +function "windowsversions" { + params = [] + result = (notequal(WINDOWS_VERSION_OVERRIDE, "") + ? [WINDOWS_VERSION_OVERRIDE] + : is_jenkins_version_weekly() ? windows_version_to_build_for_weekly : windows_version_to_build_for_lts) +} diff --git a/make.ps1 b/make.ps1 index 81620eabc8..22a8785970 100644 --- a/make.ps1 +++ b/make.ps1 @@ -1,17 +1,25 @@ [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' # - +$Organisation = 'jenkins' if(![String]::IsNullOrWhiteSpace($env:DOCKERHUB_REPO)) { $Repository = $env:DOCKERHUB_REPO @@ -32,103 +40,167 @@ if(![String]::IsNullOrWhiteSpace($env:IMAGE_TYPE)) { $env:DOCKERHUB_ORGANISATION = "$Organisation" $env:DOCKERHUB_REPO = "$Repository" $env:JENKINS_VERSION = "$JenkinsVersion" +$env:COMMIT_SHA = git rev-parse HEAD -# 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' -} - -# 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 -} - -$items = $ImageType.Split('-') -$env:WINDOWS_FLAVOR = $items[0] -$env:WINDOWS_VERSION = $items[1] - -# 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] - -$env:COMMIT_SHA=$(git rev-parse HEAD) - -$baseDockerCmd = 'docker-compose --file=build-windows.yaml' -$baseDockerBuildCmd = '{0} build --parallel --pull' -f $baseDockerCmd - -Write-Host "= PREPARE: List of $Organisation/$Repository images and tags to be processed:" -Invoke-Expression "$baseDockerCmd config" +# Check for required commands +Function Test-CommandExists { + Param ( + [String] $command + ) -if($target -eq 'build') { - Write-Host '= BUILD: Building all images...' - switch ($DryRun) { - $true { Write-Host "(dry-run) $baseDockerBuildCmd" } - $false { Invoke-Expression $baseDockerBuildCmd } + $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" + } + } } - Write-Host '= BUILD: Finished building all images.' - - if($lastExitCode -ne 0 -and !$DryRun) { - Write-Error "= BUILD: failed!" - exit $lastExitCode + Catch { + "$command does not exist" + } + Finally { + $ErrorActionPreference = $oldPreference } } function Test-Image { param ( - $ImageName + [String] $ImageName ) - Write-Host "= TEST: Testing image ${ImageName}:" + Write-Host "= TEST: Received ${ImageName} image name" + + $items = $ImageName.split(':') + $orgRepo = $items[0] -replace 'docker.io/', '' + $tag = $items[1] + + Write-Host "= TEST: Testing ${tag} tag of ${orgRepo} repository" - $env:CONTROLLER_IMAGE = $ImageName - $env:DOCKERFILE = 'windows/{0}/hotspot/Dockerfile' -f $env:WINDOWS_FLAVOR + $env:DOCKERHUB_ORG_REPO = $orgRepo + $env:CONTROLLER_TAG = $tag - if (Test-Path ".\target\$ImageName") { - Remove-Item -Recurse -Force ".\target\$ImageName" + $targetPath = '.\target\{0}' -f $tag + if (Test-Path $targetPath) { + Remove-Item -Recurse -Force $targetPath } - New-Item -Path ".\target\$ImageName" -Type Directory | Out-Null - $configuration.TestResult.OutputPath = ".\target\$ImageName\junit-results.xml" + New-Item -Path $targetPath -Type Directory | Out-Null + $configuration.TestResult.OutputPath = '{0}\junit-results.xml' -f $targetPath $TestResults = Invoke-Pester -Configuration $configuration $failed = $false if ($TestResults.FailedCount -gt 0) { - Write-Host "There were $($TestResults.FailedCount) failed tests in $ImageName" + Write-Host "There were $($TestResults.FailedCount) failed tests in $tag" $failed = $true } else { - Write-Host "There were $($TestResults.PassedCount) passed tests out of $($TestResults.TotalCount) in $ImageName" + Write-Host "There were $($TestResults.PassedCount) passed tests out of $($TestResults.TotalCount) in $tag" } - Remove-Item env:\CONTROLLER_IMAGE - Remove-Item env:\DOCKERFILE + Remove-Item env:\DOCKERHUB_ORG_REPO + Remove-Item env:\CONTROLLER_TAG return $failed } -if($target -eq 'test') { +function Initialize-DockerComposeFile { + param ( + [String] $ImageType, + [String] $DockerComposeFile + ) + + Write-Host "= PREPARE: Docker compose file generation for $ImageType" + + $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 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\": .}' + + # - Use docker buildx bake to output image definitions from the "" bake target + # - Convert with yq to the format expected by docker compose + # - Store the result in the docker compose file + docker buildx bake --progress=plain --file=docker-bake.hcl $windowsFlavor --print | + yq --prettyPrint $yqMainQuery | + yq $yqServicesQuery | + Out-File -FilePath $DockerComposeFile + + # Remove override + Remove-Item env:\WINDOWS_VERSION_OVERRIDE +} + +Test-CommandExists 'docker' +Test-CommandExists 'docker-compose' +Test-CommandExists 'docker buildx' +Test-CommandExists 'yq' + +# Sanity check +yq --version + +$dockerComposeFile = 'build-windows_{0}.yaml' -f $ImageType +$baseDockerCmd = 'docker-compose --file={0}' -f $dockerComposeFile +$baseDockerBuildCmd = '{0} build --parallel --pull' -f $baseDockerCmd + +# 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') { + Write-Host '= BUILD: Building all images...' + + switch ($DryRun) { + $true { Write-Host "(dry-run) $baseDockerBuildCmd" } + $false { Invoke-Expression $baseDockerBuildCmd } + } + + if ($lastExitCode -ne 0) { + exit $lastExitCode + } + + Write-Host '= BUILD: Finished building all images.' +} + +if ($target -eq 'test') { if ($DryRun) { - Write-Host '(dry-run) test' + Write-Host '= TEST: (dry-run) test harness skipped' } 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 @@ -142,35 +214,36 @@ if($target -eq 'test') { 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) } # Fail if any test failures - if($testFailed -ne $false) { - Write-Error 'Test stage failed!' + if ($testFailed -ne $false) { + Write-Error '= TEST: Test stage failed' exit 1 } else { - Write-Host 'Test stage passed!' + Write-Host '= TEST: Test stage passed!' } } } -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) { +if ($lastExitCode -ne 0 -and !$DryRun) { Write-Error 'Build failed!' } else { Write-Host 'Build finished successfully' diff --git a/tests/golden/expected_platforms.txt b/tests/golden/expected_platforms.txt index 9cecd8e103..0512ebc3db 100644 --- a/tests/golden/expected_platforms.txt +++ b/tests/golden/expected_platforms.txt @@ -20,3 +20,5 @@ rhel_jdk21:linux/ppc64le rhel_jdk25:linux/amd64 rhel_jdk25:linux/arm64 rhel_jdk25:linux/ppc64le +windowsservercore-ltsc2022_jdk21:windows/amd64 +windowsservercore-ltsc2022_jdk25:windows/amd64 diff --git a/tests/golden/expected_tags.txt b/tests/golden/expected_tags.txt index 555e6bd1c7..22de5c93a4 100644 --- a/tests/golden/expected_tags.txt +++ b/tests/golden/expected_tags.txt @@ -2,8 +2,11 @@ docker.io/jenkins/jenkins:2.534 (debian_jdk21) docker.io/jenkins/jenkins:2.534-alpine (alpine_jdk21) docker.io/jenkins/jenkins:2.534-alpine-jdk21 (alpine_jdk21) docker.io/jenkins/jenkins:2.534-alpine-jdk25 (alpine_jdk25) +docker.io/jenkins/jenkins:2.534-hotspot-windowsservercore-ltsc2022 (windowsservercore-ltsc2022_jdk21) docker.io/jenkins/jenkins:2.534-jdk21 (debian_jdk21) +docker.io/jenkins/jenkins:2.534-jdk21-hotspot-windowsservercore-ltsc2022 (windowsservercore-ltsc2022_jdk21) docker.io/jenkins/jenkins:2.534-jdk25 (debian_jdk25) +docker.io/jenkins/jenkins:2.534-jdk25-hotspot-windowsservercore-ltsc2022 (windowsservercore-ltsc2022_jdk25) docker.io/jenkins/jenkins:2.534-rhel-ubi9-jdk21 (rhel_jdk21) docker.io/jenkins/jenkins:2.534-rhel-ubi9-jdk25 (rhel_jdk25) docker.io/jenkins/jenkins:2.534-slim (debian-slim_jdk21) diff --git a/tests/golden/expected_tags_latest_lts.txt b/tests/golden/expected_tags_latest_lts.txt index 21a0359c96..f8865561ef 100644 --- a/tests/golden/expected_tags_latest_lts.txt +++ b/tests/golden/expected_tags_latest_lts.txt @@ -3,9 +3,13 @@ docker.io/jenkins/jenkins:2.504.3-alpine (alpine_jdk21) docker.io/jenkins/jenkins:2.504.3-alpine-jdk17 (alpine_jdk17) docker.io/jenkins/jenkins:2.504.3-alpine-jdk21 (alpine_jdk21) docker.io/jenkins/jenkins:2.504.3-alpine-jdk25 (alpine_jdk25) +docker.io/jenkins/jenkins:2.504.3-hotspot-windowsservercore-ltsc2019 (windowsservercore-ltsc2019_jdk21) docker.io/jenkins/jenkins:2.504.3-jdk17 (debian_jdk17) +docker.io/jenkins/jenkins:2.504.3-jdk17-hotspot-windowsservercore-ltsc2019 (windowsservercore-ltsc2019_jdk17) docker.io/jenkins/jenkins:2.504.3-jdk21 (debian_jdk21) +docker.io/jenkins/jenkins:2.504.3-jdk21-hotspot-windowsservercore-ltsc2019 (windowsservercore-ltsc2019_jdk21) docker.io/jenkins/jenkins:2.504.3-jdk25 (debian_jdk25) +docker.io/jenkins/jenkins:2.504.3-jdk25-hotspot-windowsservercore-ltsc2019 (windowsservercore-ltsc2019_jdk25) docker.io/jenkins/jenkins:2.504.3-lts (debian_jdk21) docker.io/jenkins/jenkins:2.504.3-lts-alpine (alpine_jdk21) docker.io/jenkins/jenkins:2.504.3-lts-jdk17 (debian_jdk17) @@ -22,14 +26,18 @@ docker.io/jenkins/jenkins:2.504.3-slim (debian-slim_jdk21) docker.io/jenkins/jenkins:2.504.3-slim-jdk17 (debian-slim_jdk17) docker.io/jenkins/jenkins:2.504.3-slim-jdk21 (debian-slim_jdk21) docker.io/jenkins/jenkins:2.504.3-slim-jdk25 (debian-slim_jdk25) +docker.io/jenkins/jenkins:2.504.3-windowsservercore-ltsc2019 (windowsservercore-ltsc2019_jdk21) docker.io/jenkins/jenkins:lts (debian_jdk21) docker.io/jenkins/jenkins:lts-alpine (alpine_jdk21) docker.io/jenkins/jenkins:lts-alpine-jdk17 (alpine_jdk17) docker.io/jenkins/jenkins:lts-alpine-jdk21 (alpine_jdk21) docker.io/jenkins/jenkins:lts-alpine-jdk25 (alpine_jdk25) docker.io/jenkins/jenkins:lts-jdk17 (debian_jdk17) +docker.io/jenkins/jenkins:lts-jdk17-hotspot-windowsservercore-ltsc2019 (windowsservercore-ltsc2019_jdk17) docker.io/jenkins/jenkins:lts-jdk21 (debian_jdk21) +docker.io/jenkins/jenkins:lts-jdk21-hotspot-windowsservercore-ltsc2019 (windowsservercore-ltsc2019_jdk21) docker.io/jenkins/jenkins:lts-jdk25 (debian_jdk25) +docker.io/jenkins/jenkins:lts-jdk25-hotspot-windowsservercore-ltsc2019 (windowsservercore-ltsc2019_jdk25) docker.io/jenkins/jenkins:lts-rhel-ubi9-jdk17 (rhel_jdk17) docker.io/jenkins/jenkins:lts-rhel-ubi9-jdk21 (rhel_jdk21) docker.io/jenkins/jenkins:lts-rhel-ubi9-jdk25 (rhel_jdk25) @@ -37,3 +45,4 @@ docker.io/jenkins/jenkins:lts-slim (debian-slim_jdk21) docker.io/jenkins/jenkins:lts-slim-jdk17 (debian-slim_jdk17) docker.io/jenkins/jenkins:lts-slim-jdk21 (debian-slim_jdk21) docker.io/jenkins/jenkins:lts-slim-jdk25 (debian-slim_jdk25) +docker.io/jenkins/jenkins:lts-windowsservercore-ltsc2019 (windowsservercore-ltsc2019_jdk21) diff --git a/tests/golden/expected_tags_latest_weekly.txt b/tests/golden/expected_tags_latest_weekly.txt index 22538ab512..ec0e509ea4 100644 --- a/tests/golden/expected_tags_latest_weekly.txt +++ b/tests/golden/expected_tags_latest_weekly.txt @@ -2,20 +2,26 @@ docker.io/jenkins/jenkins:2.534 (debian_jdk21) docker.io/jenkins/jenkins:2.534-alpine (alpine_jdk21) docker.io/jenkins/jenkins:2.534-alpine-jdk21 (alpine_jdk21) docker.io/jenkins/jenkins:2.534-alpine-jdk25 (alpine_jdk25) +docker.io/jenkins/jenkins:2.534-hotspot-windowsservercore-ltsc2022 (windowsservercore-ltsc2022_jdk21) docker.io/jenkins/jenkins:2.534-jdk21 (debian_jdk21) +docker.io/jenkins/jenkins:2.534-jdk21-hotspot-windowsservercore-ltsc2022 (windowsservercore-ltsc2022_jdk21) docker.io/jenkins/jenkins:2.534-jdk25 (debian_jdk25) +docker.io/jenkins/jenkins:2.534-jdk25-hotspot-windowsservercore-ltsc2022 (windowsservercore-ltsc2022_jdk25) docker.io/jenkins/jenkins:2.534-rhel-ubi9-jdk21 (rhel_jdk21) docker.io/jenkins/jenkins:2.534-rhel-ubi9-jdk25 (rhel_jdk25) docker.io/jenkins/jenkins:2.534-slim (debian-slim_jdk21) docker.io/jenkins/jenkins:2.534-slim-jdk21 (debian-slim_jdk21) docker.io/jenkins/jenkins:2.534-slim-jdk25 (debian-slim_jdk25) +docker.io/jenkins/jenkins:2.534-windowsservercore-ltsc2022 (windowsservercore-ltsc2022_jdk21) docker.io/jenkins/jenkins:alpine (alpine_jdk21) docker.io/jenkins/jenkins:alpine-jdk21 (alpine_jdk21) docker.io/jenkins/jenkins:alpine-jdk25 (alpine_jdk25) docker.io/jenkins/jenkins:alpine3.23-jdk21 (alpine_jdk21) docker.io/jenkins/jenkins:alpine3.23-jdk25 (alpine_jdk25) docker.io/jenkins/jenkins:jdk21 (debian_jdk21) +docker.io/jenkins/jenkins:jdk21-hotspot-windowsservercore-ltsc2022 (windowsservercore-ltsc2022_jdk21) docker.io/jenkins/jenkins:jdk25 (debian_jdk25) +docker.io/jenkins/jenkins:jdk25-hotspot-windowsservercore-ltsc2022 (windowsservercore-ltsc2022_jdk25) docker.io/jenkins/jenkins:latest (debian_jdk21) docker.io/jenkins/jenkins:latest-jdk21 (debian_jdk21) docker.io/jenkins/jenkins:latest-jdk25 (debian_jdk25) @@ -24,3 +30,4 @@ docker.io/jenkins/jenkins:rhel-ubi9-jdk25 (rhel_jdk25) docker.io/jenkins/jenkins:slim (debian-slim_jdk21) docker.io/jenkins/jenkins:slim-jdk21 (debian-slim_jdk21) docker.io/jenkins/jenkins:slim-jdk25 (debian-slim_jdk25) +docker.io/jenkins/jenkins:windowsservercore-ltsc2022 (windowsservercore-ltsc2022_jdk21) diff --git a/tests/jenkinsfile.bats b/tests/jenkinsfile.bats index 963f06d4fc..a25578adff 100644 --- a/tests/jenkinsfile.bats +++ b/tests/jenkinsfile.bats @@ -6,6 +6,6 @@ load test_helpers SUT_DESCRIPTION="Jenkinsfile" -@test "[${SUT_DESCRIPTION}] Default (weekly) targets from docker bake are taken in account in Jenkinsfile" { - [ "$(get_default_docker_bake_targets)" == "$(get_targets_from_jenkinsfile)" ] +@test "[${SUT_DESCRIPTION}] Default (weekly) Linux targets from docker bake are taken in account in Jenkinsfile" { + [ "$(get_default_docker_bake_linux_targets)" == "$(get_targets_from_jenkinsfile)" ] } diff --git a/tests/test_helpers.bash b/tests/test_helpers.bash index 195d669f45..70bcd457f7 100644 --- a/tests/test_helpers.bash +++ b/tests/test_helpers.bash @@ -122,8 +122,8 @@ function get_targets_from_jenkinsfile { | sort `# ensure constant output sort` } -function get_default_docker_bake_targets { - make --silent show | jq -r '.target | keys[]' | sort +function get_default_docker_bake_linux_targets { + make --silent show-linux | jq -r '.target | keys[]' | sort } function test_url { diff --git a/tests/test_helpers.psm1 b/tests/test_helpers.psm1 index 9e89f1fb7b..17bc0e3544 100644 --- a/tests/test_helpers.psm1 +++ b/tests/test_helpers.psm1 @@ -66,8 +66,9 @@ function Retry-Command { } function Get-SutImage { - $DOCKERFILE = Get-EnvOrDefault 'DOCKERFILE' '' - $IMAGENAME = Get-EnvOrDefault 'CONTROLLER_IMAGE' '' # Ex: jdk17-hotspot-windowsservercore-ltsc2022 + # TODO: don't hardcode Windows flavor + $DOCKERFILE = 'windows/windowsservercore/hotspot/Dockerfile' + $IMAGETAG = Get-EnvOrDefault 'CONTROLLER_TAG' '' $REAL_DOCKERFILE=Resolve-Path -Path "$PSScriptRoot/../${DOCKERFILE}" @@ -76,7 +77,7 @@ function Get-SutImage { exit 1 } - return "pester-jenkins-$IMAGENAME" + return "pester-jenkins-$IMAGETAG" } function Run-Program($cmd, $params, $verbose=$false) { @@ -105,11 +106,16 @@ function Run-Program($cmd, $params, $verbose=$false) { } function Build-Docker($tag) { - $exitCode, $stdout, $stderr = Run-Program 'docker-compose' '--file=build-windows.yaml build --parallel' + $windowsVersion = '2019' + if ($tag -match 'ltsc(\d+)$') { + $windowsVersion = $matches[1] + } + $composeParams = '--file=build-windows_windowsservercore-ltsc{0}.yaml build --parallel' -f $windowsVersion + $exitCode, $stdout, $stderr = Run-Program 'docker-compose' $composeParams if($exitCode -ne 0) { return $exitCode, $stdout, $stderr } - return(Run-Program 'docker' $('tag {0}/{1}:{2} {3}' -f $env:DOCKERHUB_ORGANISATION, $env:DOCKERHUB_REPO, $env:CONTROLLER_IMAGE, $tag)) + return(Run-Program 'docker' $('tag {0}:{1} {2}' -f $env:DOCKERHUB_ORG_REPO, $env:CONTROLLER_TAG, $tag)) } function Build-DockerChild($tag, $dir) { diff --git a/updatecli/updatecli.d/jdk21.yaml b/updatecli/updatecli.d/jdk21.yaml index 9ca71a425e..b44ad5545d 100644 --- a/updatecli/updatecli.d/jdk21.yaml +++ b/updatecli/updatecli.d/jdk21.yaml @@ -50,19 +50,6 @@ targets: path: variable.JAVA21_VERSION.default scmid: default - setJDK21VersionWindowsDockerCompose: - name: "Bump JDK21 version in build-windows.yaml" - kind: yaml - transformers: - - replacer: - from: "+" - to: "_" - spec: - files: - - build-windows.yaml - key: $.services.jdk21.build.args.JAVA_VERSION - scmid: default - actions: default: kind: github/pullrequest diff --git a/updatecli/updatecli.d/jdk25.yaml b/updatecli/updatecli.d/jdk25.yaml index ff428bb026..450dc4c4a9 100644 --- a/updatecli/updatecli.d/jdk25.yaml +++ b/updatecli/updatecli.d/jdk25.yaml @@ -50,19 +50,6 @@ targets: path: variable.JAVA25_VERSION.default scmid: default - setJDK25VersionWindowsDockerCompose: - name: "Bump JDK25 version in build-windows.yaml" - kind: yaml - transformers: - - replacer: - from: "+" - to: "_" - spec: - files: - - build-windows.yaml - key: $.services.jdk25.build.args.JAVA_VERSION - scmid: default - actions: default: kind: github/pullrequest