From f08a32627d5d85f1ae91ce75b2065a3806479161 Mon Sep 17 00:00:00 2001 From: SalmanDeveloperz Date: Wed, 10 Jun 2026 01:11:17 +0500 Subject: [PATCH] Add environment variable substitution support to jenkins-support.psm1 Windows parity for #2250. Adds Invoke-EnvVarSubstitution function supporting ${VAR} and ${VAR:-default} syntax in .xml, .conf, .properties, and .groovy files during container initialization. Opt-in via JENKINS_ENABLE_ENV_SUBST=true. Zero behavior change for existing users. Closes #2350 --- jenkins-support.psm1 | 31 +++++++++++++++++++++++++++++++ tests/functions.Tests.ps1 | 18 ++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/jenkins-support.psm1 b/jenkins-support.psm1 index af5070b538..b7fe4185a4 100644 --- a/jenkins-support.psm1 +++ b/jenkins-support.psm1 @@ -106,6 +106,28 @@ function Get-PluginVersion($archive) { return $version.Trim() } +# Substitute environment variables in file content +# Supports ${VAR} and ${VAR:-default} syntax +# Enabled only when JENKINS_ENABLE_ENV_SUBST=true +function Invoke-EnvVarSubstitution($file) { + $content = Get-Content -Raw $file + $pattern = '\$\{([A-Za-z_][A-Za-z0-9_]*)(:-([^}]*))?\}' + $content = [regex]::Replace($content, $pattern, { + param($match) + $varName = $match.Groups[1].Value + $defaultValue = $match.Groups[3].Value + $varValue = [System.Environment]::GetEnvironmentVariable($varName) + if ([string]::IsNullOrEmpty($varValue)) { + if (![string]::IsNullOrEmpty($defaultValue)) { + return $defaultValue + } + return '' + } + return $varValue + }) + Set-Content -Path $file -Value $content -NoNewline +} + # Copy files from C:/ProgramData/Jenkins/Reference/ into $JENKINS_HOME # So the initial JENKINS-HOME is set with expected content. # Don't override, as this is just a reference setup, and use from UI @@ -208,6 +230,15 @@ function Copy-ReferenceFile($file) { New-Item -ItemType Directory (Join-Path $env:JENKINS_HOME (Split-Path -Parent $rel)) } Copy-Item $file (Join-Path $env:JENKINS_HOME $rel) + # Perform environment variable substitution on config files (opt-in) + if ((Get-EnvOrDefault 'JENKINS_ENABLE_ENV_SUBST' 'false') -eq 'true') { + if ($rel -match '\.(xml|conf|properties|groovy)$') { + $destFile = Join-Path $env:JENKINS_HOME $rel + if (Test-Path $destFile) { + Invoke-EnvVarSubstitution $destFile + } + } + } } else { $action="SKIPPED" } diff --git a/tests/functions.Tests.ps1 b/tests/functions.Tests.ps1 index 3cf089b34f..73d728ed56 100644 --- a/tests/functions.Tests.ps1 +++ b/tests/functions.Tests.ps1 @@ -95,6 +95,24 @@ Describe "[functions > $global:TEST_TAG] Copy-ReferenceFile" -Skip:(-not $global $stdout | Should -Match "test.override" } + It 'substitute_env_vars replaces variable with env value' { + $exitCode, $stdout, $stderr = Run-Program 'docker' "run --rm -e JENKINS_ENABLE_ENV_SUBST=true -e JENKINS_URL=http://prod.example.com $global:SUT_IMAGE powershell -C `"Import-Module -DisableNameChecking -Force C:/ProgramData/Jenkins/jenkins-support.psm1 ; `$tmp = New-TemporaryFile ; Set-Content -Path `$tmp -Value '`${JENKINS_URL:-http://localhost:8080/}' ; Invoke-EnvVarSubstitution `$tmp ; Get-Content `$tmp`"" + $exitCode | Should -Be 0 + $stdout | Should -Match 'http://prod.example.com' + } + + It 'substitute_env_vars uses default when variable unset' { + $exitCode, $stdout, $stderr = Run-Program 'docker' "run --rm -e JENKINS_ENABLE_ENV_SUBST=true $global:SUT_IMAGE powershell -C `"Import-Module -DisableNameChecking -Force C:/ProgramData/Jenkins/jenkins-support.psm1 ; `$tmp = New-TemporaryFile ; Set-Content -Path `$tmp -Value '`${JENKINS_URL:-http://localhost:8080/}' ; Invoke-EnvVarSubstitution `$tmp ; Get-Content `$tmp`"" + $exitCode | Should -Be 0 + $stdout | Should -Match 'http://localhost:8080/' + } + + It 'copy_reference_file skips substitution when JENKINS_ENABLE_ENV_SUBST is unset' { + $exitCode, $stdout, $stderr = Run-Program 'docker' "run --rm -e JENKINS_ENABLE_ENV_SUBST=false -e JENKINS_URL=http://prod.example.com $global:SUT_IMAGE powershell -C `"Import-Module -DisableNameChecking -Force C:/ProgramData/Jenkins/jenkins-support.psm1 ; `$tmp = New-TemporaryFile ; Set-Content -Path `$tmp -Value '`${JENKINS_URL:-http://localhost:8080/}' ; if((Get-EnvOrDefault 'JENKINS_ENABLE_ENV_SUBST' 'false') -eq 'true') { Invoke-EnvVarSubstitution `$tmp } ; Get-Content `$tmp`"" + $exitCode | Should -Be 0 + $stdout | Should -Match '\$\{JENKINS_URL:-http://localhost:8080/\}' + } + It 'cleanup container' { Cleanup $global:SUT_CONTAINER | Out-Null }