#Requires -Version 5.1 <# .SYNOPSIS Build and push a CI image to the Harbor registry using podman. .EXAMPLE .\build-and-push.ps1 -Image ci/java-builder #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$Image, [Parameter(Mandatory = $false)] [string]$Version ) $ErrorActionPreference = 'Stop' # --- Load .env --- $envFile = Join-Path $PSScriptRoot '.env' if (-not (Test-Path $envFile)) { throw ".env not found at $envFile" } Get-Content $envFile | ForEach-Object { if ($_ -match '^\s*([^#=\s]+)\s*=\s*(.*?)\s*$') { $value = $matches[2].TrimEnd("`r") if ($value.StartsWith('"') -and $value.EndsWith('"')) { $value = $value.Substring(1, $value.Length - 2) } elseif ($value.StartsWith("'") -and $value.EndsWith("'")) { $value = $value.Substring(1, $value.Length - 2) } [Environment]::SetEnvironmentVariable($matches[1], $value) } } foreach ($v in 'REGISTRY', 'REGISTRY_USER', 'REGISTRY_PASS') { if (-not (Get-Item "env:$v" -ErrorAction SilentlyContinue).Value) { throw "$v not set in .env" } } # --- Resolve paths --- # $Image is the path to the image directory relative to the repo root, and # also the image name pushed to the registry (e.g. "ci/java-builder"). $regHost = $env:REGISTRY -replace '^https?://', '' -replace '/$', '' $imageName = ($Image -replace '\\', '/').Trim('/') $imageDir = Join-Path $PSScriptRoot ($imageName -replace '/', [IO.Path]::DirectorySeparatorChar) if (-not (Test-Path $imageDir)) { throw "Image directory not found: $imageDir" } $repo = "$regHost/kollect-tools/$imageName" $date = Get-Date -Format 'yyyyMMdd' # --- Resolve version --- # If -Version isn't given, parse the Dockerfile: find the variable referenced # in the FROM line and read its ARG default (e.g. JAVA_VERSION=25 -> "25"). if (-not $Version) { $dockerfile = Join-Path $imageDir 'Dockerfile' $content = Get-Content $dockerfile -Raw if ($content -match '(?m)^\s*FROM\s+\S*?\$\{(\w+)\}') { $varName = $matches[1] if ($content -match "(?m)^\s*ARG\s+$varName\s*=\s*`"?([^`"\s]+)`"?") { $Version = $matches[1] } } if (-not $Version) { throw "Could not auto-detect version from $dockerfile. Pass -Version explicitly." } Write-Host "=> Auto-detected version: $Version (from $varName)" } $tags = @('latest', $Version, $date) # --- Login --- # Use .NET Process so stdin gets the raw password with no trailing newline # (PowerShell's native pipe appends CRLF, which breaks --password-stdin). Write-Host "=> Logging in to $regHost" $psi = New-Object System.Diagnostics.ProcessStartInfo $psi.FileName = 'podman' $psi.Arguments = "login $regHost --username `"$($env:REGISTRY_USER)`" --password-stdin" $psi.RedirectStandardInput = $true $psi.UseShellExecute = $false $proc = [System.Diagnostics.Process]::Start($psi) $proc.StandardInput.Write($env:REGISTRY_PASS) $proc.StandardInput.Close() $proc.WaitForExit() if ($proc.ExitCode -ne 0) { throw "podman login failed (exit $($proc.ExitCode))" } # --- Build --- Write-Host "=> Building ${repo}:latest" $buildArgs = @('build', '-t', "${repo}:latest") if ($env:NVD_API_KEY) { $buildArgs += @('--build-arg', "NVD_API_KEY=$env:NVD_API_KEY") } $buildArgs += $imageDir & podman @buildArgs if ($LASTEXITCODE -ne 0) { throw "podman build failed" } # --- Tag --- foreach ($tag in $Version, $date) { Write-Host "=> Tagging ${repo}:$tag" podman tag "${repo}:latest" "${repo}:$tag" if ($LASTEXITCODE -ne 0) { throw "podman tag failed for $tag" } } # --- Push --- foreach ($tag in $tags) { Write-Host "=> Pushing ${repo}:$tag" podman push "${repo}:$tag" if ($LASTEXITCODE -ne 0) { throw "podman push failed for $tag" } } Write-Host "Done. Pushed $($tags.Count) tags to $repo"