From fb12350d97f278fee626cc5599724919ee8ddee4 Mon Sep 17 00:00:00 2001 From: Ryan Richter Date: Fri, 14 Nov 2025 10:55:10 -0500 Subject: [PATCH 1/5] Update Jenkins plugins to latest versions as of Nov. 14th 2025 --- files/jenkins.json | 49 +++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/files/jenkins.json b/files/jenkins.json index ba32c66..cbf3151 100644 --- a/files/jenkins.json +++ b/files/jenkins.json @@ -1,33 +1,34 @@ { "plugins": [ { "name": "apache-httpcomponents-client-4-api", "version": "4.5.14-269.vfa_2321039a_83" }, - { "name": "asm-api", "version": "9.8-135.vb_2239d08ee90" }, - { "name": "bouncycastle-api", "version": "2.30.1.80-256.vf98926042a_9b_" }, - { "name": "branch-api", "version": "2.1217.v43d8b_b_d8b_2c7" }, - { "name": "caffeine-api", "version": "3.2.0-166.v72a_6d74b_870f" }, - { "name": "cloudbees-folder", "version": "6.1012.v79a_86a_1ea_c1f" }, - { "name": "display-url-api", "version": "2.209.v582ed814ff2f" }, - { "name": "durable-task", "version": "587.v84b_877235b_45" }, - { "name": "instance-identity", "version": "201.vd2a_b_5a_468a_a_6" }, - { "name": "ionicons-api", "version": "82.v0597178874e1" }, + { "name": "asm-api", "version": "9.9-185.va_6c6b_3348b_c3" }, + { "name": "bouncycastle-api", "version": "2.30.1.82-277.v70ca_0b_877184" }, + { "name": "branch-api", "version": "2.1259.v45c101731c76" }, + { "name": "caffeine-api", "version": "3.2.3-194.v31a_b_f7a_b_5a_81" }, + { "name": "cloudbees-folder", "version": "6.1073.va_7888eb_dd514" }, + { "name": "commons-lang3-api", "version": "3.19.0-104.v12125f33a_255" }, + { "name": "display-url-api", "version": "2.217.va_6b_de84cc74b_" }, + { "name": "durable-task", "version": "635.v3733cef34b_5e" }, + { "name": "instance-identity", "version": "203.v15e81a_1b_7a_38" }, + { "name": "ionicons-api", "version": "94.vcc3065403257" }, { "name": "jakarta-activation-api", "version": "2.1.3-2" }, - { "name": "jakarta-mail-api", "version": "2.1.3-2" }, + { "name": "jakarta-mail-api", "version": "2.1.3-3" }, { "name": "javax-activation-api", "version": "1.2.0-8" }, { "name": "javax-mail-api", "version": "1.6.2-11" }, - { "name": "mailer", "version": "489.vd4b_25144138f" }, - { "name": "pipeline-groovy-lib", "version": "752.vdddedf804e72" }, - { "name": "scm-api", "version": "704.v3ce5c542825a_" }, - { "name": "script-security", "version": "1373.vb_b_4a_a_c26fa_00" }, - { "name": "structs", "version": "343.vdcf37b_a_c81d5" }, + { "name": "mailer", "version": "522.va_995fa_cfb_8b_d" }, + { "name": "pipeline-groovy-lib", "version": "776.vfee5327b_b_a_5b_" }, + { "name": "scm-api", "version": "712.v8846fdd68c88" }, + { "name": "script-security", "version": "1385.v7d2d9ec4d909" }, + { "name": "structs", "version": "353.v261ea_40a_80fb_" }, { "name": "variant", "version": "70.va_d9f17f859e0" }, - { "name": "workflow-api", "version": "1371.ve334280b_d611" }, - { "name": "workflow-basic-steps", "version": "1079.vce64b_a_929c5a_" }, - { "name": "workflow-cps", "version": "4046.v90b_1b_9edec67" }, - { "name": "workflow-durable-task-step", "version": "1405.v1fcd4a_d00096" }, - { "name": "workflow-job", "version": "1520.v56d65e3b_4566" }, - { "name": "workflow-multibranch", "version": "806.vb_b_688f609ee9" }, - { "name": "workflow-scm-step", "version": "437.v05a_f66b_e5ef8" }, - { "name": "workflow-step-api", "version": "700.v6e45cb_a_5a_a_21" }, - { "name": "workflow-support", "version": "968.v8f17397e87b_8" } + { "name": "workflow-api", "version": "1398.v67030756d3fb_" }, + { "name": "workflow-basic-steps", "version": "1098.v808b_fd7f8cf4" }, + { "name": "workflow-cps", "version": "4218.vff679a_5c0f3a_" }, + { "name": "workflow-durable-task-step", "version": "1464.v2d3f5c68f84c" }, + { "name": "workflow-job", "version": "1559.va_a_533730b_ea_d" }, + { "name": "workflow-multibranch", "version": "821.vc3b_4ea_780798" }, + { "name": "workflow-scm-step", "version": "466.va_d69e602552b_" }, + { "name": "workflow-step-api", "version": "710.v3e456cc85233" }, + { "name": "workflow-support", "version": "1004.veee3a_d67cdb_9" } ] } \ No newline at end of file From 5ead1bcba278f025b495ee5661dca349fac6b21f Mon Sep 17 00:00:00 2001 From: Ryan Richter Date: Fri, 14 Nov 2025 11:04:07 -0500 Subject: [PATCH 2/5] Update .NET CCM Dependency packages to latest versions as of Nov. 14th 2025 --- files/chocolatey.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/files/chocolatey.json b/files/chocolatey.json index 34e1130..3cd6724 100644 --- a/files/chocolatey.json +++ b/files/chocolatey.json @@ -14,9 +14,9 @@ { "name": "chocolatey" }, { "name": "chocolateygui.extension" }, { "name": "chocolateygui" }, - { "name": "dotnet-8.0-aspnetruntime", "version": "8.0.16" }, - { "name": "dotnet-8.0-runtime", "version": "8.0.16" }, - { "name": "dotnet-aspnetcoremodule-v2", "version": "18.0.25074" }, + { "name": "dotnet-8.0-aspnetruntime", "version": "8.0.22" }, + { "name": "dotnet-8.0-runtime", "version": "8.0.22" }, + { "name": "dotnet-aspnetcoremodule-v2", "version": "18.0.25301" }, { "name": "dotnetfx" }, { "name": "jenkins" }, { "name": "KB2919355", "internalize": false }, From 0761224239d7b62a816861ecf04af77fd21230ad Mon Sep 17 00:00:00 2001 From: James Ruskin Date: Tue, 18 Nov 2025 14:28:40 +0000 Subject: [PATCH 3/5] (#306) Adds Jenkins tests to ensure Jobs and Plugins are present In some circumstances, the tests we had would not accurately show that Jenkins had successfully loaded jobs and plugins. These tests aim to validate that: - The Jenkins plugins have all been installed successfully, and are loaded correctly. - The Jenkins jobs have all been loaded correctly, and are available for use. This should allow our automated testing to better reflect the state of the environment. --- modules/C4B-Environment/C4B-Environment.psm1 | 65 +++++++++++++++++++- tests/jenkins.tests.ps1 | 56 ++++++++++------- 2 files changed, 98 insertions(+), 23 deletions(-) diff --git a/modules/C4B-Environment/C4B-Environment.psm1 b/modules/C4B-Environment/C4B-Environment.psm1 index c8ce91f..ce05736 100644 --- a/modules/C4B-Environment/C4B-Environment.psm1 +++ b/modules/C4B-Environment/C4B-Environment.psm1 @@ -1207,7 +1207,7 @@ The host name of the C4B instance. } Copy-Item $PSScriptRoot\ReadmeTemplate.html.j2 -Destination $env:Public\Desktop\Readme.html -Force - + # Working around the existing j2 template, so we can keep them roughly in sync Invoke-TextReplacementInFile -Path $env:Public\Desktop\Readme.html -Replacement @{ # CCM Values @@ -1298,4 +1298,67 @@ if ( Invoke-Choco feature enable --name='useFipsCompliantChecksums' } +function Invoke-JenkinsApi { + <# + .Synopsis + Invokes an existing job on a Jenkins server + .Example + Invoke-JenkinsApi + #> + param( + # The name of the job to invoke + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Slug, + + # The URI of the Jenkins server, including protocols and port if required + [ValidateNotNullOrEmpty()] + [string]$Uri = 'http://localhost:8080/jenkins', + + [string]$Method = "GET", + + # The Jenkins credential to authenticate with + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [System.Management.Automation.PSCredential] + [System.Management.Automation.CredentialAttribute()] + $Credential, + + [switch]$RequiresCrumb + ) + $RequestParams = @{} + + $Header = @{} + $Header['Authorization'] = 'Basic {0}' -f ([Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("$($Credential.UserName):$($Credential.GetNetworkCredential().Password)"))) + $RequestParams['Headers'] = $Header + + if ($RequiresCrumb) { + $JenkinsWebSession = New-Object Microsoft.PowerShell.Commands.WebRequestSession + + $RequestParams['Uri'] = '{0}/crumbIssuer/api/json' -f $Uri + $RequestParams['Method'] = 'GET' + + $RequestParams['WebSession'] = $JenkinsWebSession + + $CrumbResponse = Invoke-RestMethod @RequestParams + + $Header['Jenkins-Crumb'] = $CrumbResponse.crumb + $RequestParams['Uri'] = '{0}/me/descriptorByName/jenkins.security.ApiTokenProperty/generateNewToken?newTokenName=GHA' -f $Uri + $RequestParams['Method'] = 'POST' + $RequestParams['Headers'] = $Header + + $Token = (Invoke-RestMethod @RequestParams).data.tokenValue + $RequestParams.Headers['Authorization'] = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("$($Credential.UserName):$($Token)")) + } + + $RequestParams['Uri'] = '{0}/{1}' -f $Uri.TrimEnd('/'), $Slug.TrimStart('/') + $RequestParams['Method'] = $Method + + if ($Parameters) { + $RequestParams['Body'] = $Parameters + } + + Invoke-RestMethod @RequestParams +} + Export-ModuleMember -Function "*" \ No newline at end of file diff --git a/tests/jenkins.tests.ps1 b/tests/jenkins.tests.ps1 index acea70e..60cece9 100644 --- a/tests/jenkins.tests.ps1 +++ b/tests/jenkins.tests.ps1 @@ -1,8 +1,16 @@ +#requires -Modules C4B-Environment [CmdletBinding()] -Param( +param( [Parameter(Mandatory)] - [String] - $Fqdn + [String]$Fqdn, + + [PSCredential]$JenkinsCredential = $( + if (Get-ChocoEnvironmentProperty JenkinsCredential) { + Get-ChocoEnvironmentProperty JenkinsCredential + } else { + Get-Credential -UserName admin -Message "Jenkins Account" + } + ) ) Describe "Jenkins Configuration" { @@ -12,7 +20,7 @@ Describe "Jenkins Configuration" { $service = Get-Service jenkins } - It "Jenkins is installed" { + It "Jenkins package is installed" { $jenkins | Should -Not -BeNullOrEmpty } @@ -23,7 +31,6 @@ Describe "Jenkins Configuration" { It "Service is running" { $service.Status | Should -Be 'Running' } - } Context "Required Scripts" { @@ -44,21 +51,17 @@ Describe "Jenkins Configuration" { } } - Context "Required Jobs" { + Context "Required Jobs" -Skip:$(-not $JenkinsCredential) { BeforeAll { - $jobs = (Get-ChildItem 'C:\ProgramData\Jenkins\.jenkins\jobs\' -Directory).Name - } - - It "'Internalize packages from the Community Repository' is present" { - 'Internalize packages from the Community Repository' -in $jobs | Should -Be $true + $Jobs = (Invoke-JenkinsApi -Uri "https://$($FQDN):7443" -Slug "/api/json" -Credential $JenkinsCredential).jobs } - It "'Update Production Repository' is present" { - 'Update Production Repository' -in $jobs | Should -Be $true - } - - It "'Update test repository from Chocolatey Community Repository' is present" { - 'Update test repository from Chocolatey Community Repository' -in $jobs | Should -Be $true + It "'<_>' is present" -ForEach @( + 'Internalize packages from the Community Repository' + 'Update Production Repository' + 'Update test repository from Chocolatey Community Repository' + ) { + $Jobs.Name | Should -Contain $_ } } @@ -68,17 +71,26 @@ Describe "Jenkins Configuration" { } } - Context "Required Plugins" { + Context "Required Plugins" -Skip:$(-not $JenkinsCredential) { BeforeDiscovery { - $ExpectedPlugins = (Get-Content $PSScriptRoot\..\files\jenkins.json | ConvertFrom-Json).plugins.name + $ExpectedPlugins = (Get-Content $PSScriptRoot\..\files\jenkins.json | ConvertFrom-Json).plugins + $InstalledPlugins = (Invoke-JenkinsApi -Uri "https://$($Fqdn):7443" -Slug "/manage/pluginManager/api/json?depth=1" -Credential $JenkinsCredential).plugins } BeforeAll { - $plugins = (Get-ChildItem 'C:\ProgramData\Jenkins\.jenkins\plugins\' -Directory).Name + $InstalledPlugins = (Invoke-JenkinsApi -Uri "https://$($Fqdn):7443" -Slug "/manage/pluginManager/api/json?depth=1" -Credential $JenkinsCredential).plugins + } + + It "<_.name> plugin is installed" -ForEach $ExpectedPlugins { + $PluginShortName, $PluginVersion = $_.name, $_.version + $InstalledPlugins.Where{ + $_.shortName -eq $PluginShortName + }.version | Should -Be $PluginVersion -Because "$($PluginShortName) should have been version '$($PluginVersion)'" } - It "<_> plugin is installed" -ForEach $ExpectedPlugins { - $_ -in $plugins | Should -be $true + # During our builds, we should check we're not merging outdated plugins - but on customer systems, that may be the case. + It "<_.shortName> is not outdated" -ForEach $InstalledPlugins -Skip:$(-not $env:CI) { + $_.hasUpdate | Should -Be $false -Because "$($_.longName) ('$($_.shortName)') should not have an available update" } } } \ No newline at end of file From 73ae4aa5a57880cf09a5b8b59a4e8317cf2b5c67 Mon Sep 17 00:00:00 2001 From: James Ruskin Date: Tue, 18 Nov 2025 14:30:21 +0000 Subject: [PATCH 4/5] (#306) Update Jenkins plugins to latest versions as of Nov. 18th 2025 A few plugins were slightly outdated. This brings them up to the latest version supported by Jenkins LTS. --- files/jenkins.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/files/jenkins.json b/files/jenkins.json index cbf3151..2d1ef7c 100644 --- a/files/jenkins.json +++ b/files/jenkins.json @@ -16,12 +16,12 @@ { "name": "javax-activation-api", "version": "1.2.0-8" }, { "name": "javax-mail-api", "version": "1.6.2-11" }, { "name": "mailer", "version": "522.va_995fa_cfb_8b_d" }, - { "name": "pipeline-groovy-lib", "version": "776.vfee5327b_b_a_5b_" }, + { "name": "pipeline-groovy-lib", "version": "787.ve2fef0efdca_6" }, { "name": "scm-api", "version": "712.v8846fdd68c88" }, { "name": "script-security", "version": "1385.v7d2d9ec4d909" }, - { "name": "structs", "version": "353.v261ea_40a_80fb_" }, + { "name": "structs", "version": "362.va_b_695ef4fdf9" }, { "name": "variant", "version": "70.va_d9f17f859e0" }, - { "name": "workflow-api", "version": "1398.v67030756d3fb_" }, + { "name": "workflow-api", "version": "1384.vdc05a_48f535f" }, { "name": "workflow-basic-steps", "version": "1098.v808b_fd7f8cf4" }, { "name": "workflow-cps", "version": "4218.vff679a_5c0f3a_" }, { "name": "workflow-durable-task-step", "version": "1464.v2d3f5c68f84c" }, From 5dc9b485f2c5b4306262f8e1691327fb00dbc191 Mon Sep 17 00:00:00 2001 From: James Ruskin Date: Tue, 18 Nov 2025 16:16:11 +0000 Subject: [PATCH 5/5] (maint) Fix Jenkins Plugin Download URL Some users were seeing issues downloading the from the existing URL. This should work better. --- OfflineInstallPreparation.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OfflineInstallPreparation.ps1 b/OfflineInstallPreparation.ps1 index 838c77c..7a3b60f 100644 --- a/OfflineInstallPreparation.ps1 +++ b/OfflineInstallPreparation.ps1 @@ -150,7 +150,7 @@ foreach ($Plugin in (Get-Content $PSScriptRoot\files\jenkins.json | ConvertFrom- OutFile = Join-Path $PluginsWorkingDirectory "$($Plugin.Name).hpi" } if ($Plugin.Version -and $Plugin.Version -ne 'latest') { - $RestArgs.Uri = "https://updates.jenkins-ci.org/download/plugins/$($Plugin.Name)/$($Plugin.Version)/$($Plugin.Name).hpi" + $RestArgs.Uri = "https://updates.jenkins.io/download/plugins/$($Plugin.Name)/$($Plugin.Version)/$($Plugin.Name).hpi" } if (-not (Test-Path $RestArgs.OutFile)) { Invoke-WebRequest @RestArgs -UseBasicParsing