maksit-hamode/utils/modules/Engine/ReleaseSupport.psm1

327 lines
12 KiB
PowerShell

#requires -Version 7.0
#requires -PSEdition Core
$modulesDir = Split-Path $PSScriptRoot -Parent
if (-not (Get-Command Write-Log -ErrorAction SilentlyContinue)) {
$loggingModulePath = Join-Path $modulesDir "Logging.psm1"
if (Test-Path $loggingModulePath -PathType Leaf) {
Import-Module $loggingModulePath -Force
}
}
if (-not (Get-Command Get-CurrentBranch -ErrorAction SilentlyContinue)) {
$gitToolsModulePath = Join-Path $modulesDir "GitTools.psm1"
if (Test-Path $gitToolsModulePath -PathType Leaf) {
Import-Module $gitToolsModulePath -Force
}
}
if (-not (Get-Command Get-PluginStageLabel -ErrorAction SilentlyContinue) -or -not (Get-Command Test-IsPublishPlugin -ErrorAction SilentlyContinue)) {
$pluginSupportModulePath = Join-Path $PSScriptRoot "PluginSupport.psm1"
if (Test-Path $pluginSupportModulePath -PathType Leaf) {
Import-Module $pluginSupportModulePath -Force
}
}
if (-not (Get-Command Resolve-RelativePaths -ErrorAction SilentlyContinue) -or -not (Get-Command Set-EngineState -ErrorAction SilentlyContinue)) {
$engineContextModulePath = Join-Path $PSScriptRoot "EngineContext.psm1"
if (Test-Path $engineContextModulePath -PathType Leaf) {
Import-Module $engineContextModulePath -Force
}
}
function Get-EnabledVersionPlugins {
param(
[Parameter(Mandatory = $true)]
[object[]]$Plugins,
[Parameter(Mandatory = $true)]
[string]$ScriptDir
)
$versionPlugins = @()
foreach ($plugin in $Plugins) {
if ($null -eq $plugin -or [string]::IsNullOrWhiteSpace([string]$plugin.name)) {
continue
}
if (-not $plugin.enabled) {
continue
}
$metadata = Get-PluginMetadataObject -Plugin $plugin -EngineDirectory $ScriptDir
if ($null -eq $metadata) {
continue
}
if (($metadata.PSObject.Properties.Name -contains 'providesVersion') -and [bool]$metadata.providesVersion) {
$versionPlugins += $plugin
}
}
return @($versionPlugins)
}
function Invoke-VersionPlugin {
param(
[Parameter(Mandatory = $true)]
$Plugin,
[Parameter(Mandatory = $true)]
[psobject]$Context,
[Parameter(Mandatory = $true)]
[string]$EngineDirectory
)
$modulePath = Resolve-PluginModulePath -Plugin $Plugin -EngineDirectory $EngineDirectory
if (-not (Test-Path $modulePath -PathType Leaf)) {
throw "Version plugin '$($Plugin.name)' module not found at: $modulePath"
}
$moduleInfo = Import-Module $modulePath -Force -PassThru -ErrorAction Stop
$invokeCommand = Get-Command -Name "Invoke-Plugin" -Module $moduleInfo.Name -ErrorAction Stop
$pluginSettings = New-PluginInvocationSettings -Plugin $Plugin -SharedSettings $Context
& $invokeCommand -Settings $pluginSettings
}
function Resolve-EngineContextVersion {
param(
[Parameter(Mandatory = $true)]
[object[]]$Plugins,
[Parameter(Mandatory = $true)]
[psobject]$Context,
[Parameter(Mandatory = $true)]
[string]$ScriptDir
)
$versionPlugins = @(Get-EnabledVersionPlugins -Plugins $Plugins -ScriptDir $ScriptDir)
if ($versionPlugins.Count -eq 0) {
throw "Configure exactly one enabled release version plugin (declares providesVersion in Get-PluginMetadata), e.g. DotNetReleaseVersion (projectFiles), NpmReleaseVersion (packageJsonPath), or FileReleaseVersion (versionFilePath)."
}
if ($versionPlugins.Count -gt 1) {
$names = ($versionPlugins | ForEach-Object { [string]$_.name }) -join ', '
throw "Configure only one enabled release version plugin. Found: $names."
}
$versionPlugin = $versionPlugins[0]
Invoke-VersionPlugin -Plugin $versionPlugin -Context $Context -EngineDirectory $ScriptDir
$version = Get-EngineState -Context $Context -Name 'version'
if ($null -eq $version -or [string]::IsNullOrWhiteSpace([string]$version)) {
throw "Version plugin '$($versionPlugin.name)' did not set a version on the engine context."
}
return [string]$versionPlugin.name
}
function Assert-WorkingTreeClean {
$gitStatus = Get-GitStatusShort
if (-not [string]::IsNullOrWhiteSpace([string]$gitStatus)) {
Write-Log -Level "WARN" -Message " Uncommitted changes detected (use ReleasePublishGuard requireCleanWorkingTree to block publish)."
foreach ($line in @([string]$gitStatus -split "`r?`n")) {
if (-not [string]::IsNullOrWhiteSpace($line)) {
Write-Log -Level "WARN" -Message " $line"
}
}
return
}
Write-Log -Level "OK" -Message " Working directory is clean."
}
function Initialize-ReleaseStageContext {
param(
[Parameter(Mandatory = $true)]
[psobject]$SharedSettings,
[Parameter(Mandatory = $true)]
[string]$ArtifactsDirectory
)
if (-not $SharedSettings.PSObject.Properties['releaseDir'] -or [string]::IsNullOrWhiteSpace([string]$SharedSettings.releaseDir)) {
if (Get-Command Set-EngineState -ErrorAction SilentlyContinue) {
Set-EngineState -Context $SharedSettings -Name 'releaseDir' -Value $ArtifactsDirectory
}
else {
$SharedSettings | Add-Member -NotePropertyName releaseDir -NotePropertyValue $ArtifactsDirectory -Force
}
}
}
function New-EngineContext {
param(
[Parameter(Mandatory = $true)]
[object[]]$Plugins,
[Parameter(Mandatory = $true)]
[string]$ScriptDir,
[Parameter(Mandatory = $true)]
[string]$SrcDir,
[Parameter(Mandatory = $false)]
[psobject]$Settings,
[Parameter(Mandatory = $false)]
[psobject]$ExtensionData
)
$releaseRelative = '..\..\..\releases'
$artifactsDirectory = [System.IO.Path]::GetFullPath((Join-Path $ScriptDir $releaseRelative))
$currentBranch = Get-CurrentBranch
$releaseBranches = @()
foreach ($p in $Plugins) {
if (-not $p.enabled) { continue }
if ([string]$p.name -ne 'ReleasePublishGuard') { continue }
foreach ($b in (Get-PluginBranches -Plugin $p)) {
$releaseBranches += $b
}
}
$releaseBranches = @($releaseBranches | Where-Object { $_ -ne '*' -and -not [string]::IsNullOrWhiteSpace($_) } | Select-Object -Unique)
if ($releaseBranches.Count -eq 0) {
foreach ($p in ($Plugins | Where-Object { Test-IsPublishPlugin -Plugin $_ })) {
if (-not $p.enabled) { continue }
foreach ($b in (Get-PluginBranches -Plugin $p)) {
$releaseBranches += $b
}
}
$releaseBranches = @($releaseBranches | Where-Object { $_ -ne '*' -and -not [string]::IsNullOrWhiteSpace($_) } | Select-Object -Unique)
}
if ($releaseBranches.Count -eq 0) {
$releaseBranches = @('main')
}
$isNonReleaseBranch = -not ($releaseBranches -contains $currentBranch)
Assert-WorkingTreeClean
$context = [pscustomobject]@{
scriptDir = $ScriptDir
srcDir = $SrcDir
utilsDir = $SrcDir
currentBranch = $currentBranch
artifactsDirectory = $artifactsDirectory
isNonReleaseBranch = $isNonReleaseBranch
releaseBranches = $releaseBranches
skipPublishPlugins = $false
facts = [ordered]@{}
}
$context = Copy-RepoUtilsSecretsEnvNamesToContext -Context $context -Settings $Settings
$versionSource = Resolve-EngineContextVersion -Plugins $Plugins -Context $context -ScriptDir $ScriptDir
$version = [string](Get-EngineState -Context $context -Name 'version' -Required)
$tag = "v$version"
Set-EngineState -Context $context -Name 'tag' -Value $tag
Write-Log -Level "INFO" -Message " Release tag default from ${versionSource}: $tag (ReleasePublishGuard may replace from git when publish is allowed)."
$dryRunPlugins = @(
$Plugins |
Where-Object {
$_.enabled -and
($_.PSObject.Properties.Name -contains 'dryRun') -and
$null -ne $_.dryRun -and
[bool]$_.dryRun -and
(Test-PluginMutatesRemote -Plugin $_ -EngineDirectory $ScriptDir)
} |
ForEach-Object { [string]$_.name }
)
if ($dryRunPlugins.Count -gt 0) {
Write-Log -Level "INFO" -Message " Plugin dryRun (validate only): $($dryRunPlugins -join ', ')"
}
$expandContext = Get-Command Expand-ExtensionEngineContext -ErrorAction SilentlyContinue
if ($expandContext) {
$expandParams = @{
Context = $context
ScriptDir = $ScriptDir
Settings = $Settings
}
if ($PSBoundParameters.ContainsKey('ExtensionData')) {
$expandParams['ExtensionData'] = $ExtensionData
}
return & $expandContext @expandParams
}
return $context
}
function Get-PreferredReleaseBranch {
param(
[Parameter(Mandatory = $true)]
[psobject]$EngineContext
)
if ($EngineContext.releaseBranches.Count -gt 0) {
return $EngineContext.releaseBranches[0]
}
return "main"
}
function Select-HelmInstallPlugins {
<#
.SYNOPSIS
Keeps the release version plugin and HelmSelfDeploy for an OCI install without build or push.
.DESCRIPTION
Install-Helm-*.bat passes -InstallOnly. The chart and images are already in OCI, so compile,
test, image build, image push, and HelmPush are not run. The version plugin still supplies
the chart tag (shared.version). HelmSelfDeploy dryRun is rejected: that path needs the local
package HelmPush keeps in the same run.
#>
param(
[Parameter(Mandatory = $true)]
[AllowEmptyCollection()]
[object[]]$Plugins,
[Parameter(Mandatory = $true)]
[string]$ScriptDir
)
$versionPlugins = @(Get-EnabledVersionPlugins -Plugins $Plugins -ScriptDir $ScriptDir)
if ($versionPlugins.Count -eq 0) {
throw "Install-only Helm requires exactly one enabled release version plugin (providesVersion) so the OCI chart tag can be resolved. Build and push plugins are not run."
}
if ($versionPlugins.Count -gt 1) {
$names = ($versionPlugins | ForEach-Object { [string]$_.name }) -join ', '
throw "Install-only Helm requires exactly one enabled release version plugin. Found: $names."
}
$deployPlugins = @($Plugins | Where-Object {
$null -ne $_ -and
$_.enabled -and
[string]$_.name -eq 'HelmSelfDeploy'
})
if ($deployPlugins.Count -eq 0) {
throw "Install-only Helm requires an enabled HelmSelfDeploy plugin. The chart is installed from OCI; image build, image push, and HelmPush are not run."
}
if ($deployPlugins.Count -gt 1) {
throw "Install-only Helm found multiple enabled HelmSelfDeploy plugins."
}
$deployPlugin = $deployPlugins[0]
if ($deployPlugin.PSObject.Properties.Name -contains 'dryRun' -and $null -ne $deployPlugin.dryRun -and [bool]$deployPlugin.dryRun) {
throw "Install-only Helm cannot use HelmSelfDeploy dryRun. That path needs the local package from HelmPush in the same run. Omit dryRun to install the chart already published to OCI."
}
$versionName = [string]$versionPlugins[0].name
return @($Plugins | Where-Object {
$null -ne $_ -and
$_.enabled -and
([string]$_.name -eq $versionName -or [string]$_.name -eq 'HelmSelfDeploy')
})
}
Export-ModuleMember -Function Assert-WorkingTreeClean, Initialize-ReleaseStageContext, New-EngineContext, Get-PreferredReleaseBranch, Select-HelmInstallPlugins