mirror of
https://github.com/MAKS-IT-COM/podman-client-dotnet.git
synced 2026-08-15 06:38:10 +02:00
(chore): update deps and sync RepoUtils
This commit is contained in:
parent
9f6284ba6f
commit
8f42706221
@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [1.3.1] - 2026-08-14
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated **MaksIT.Results** (2.0.4), **Microsoft.Extensions.*** (10.0.11), and **System.Management.Automation** (7.6.5) package references.
|
||||
- Updated test dependencies: **Microsoft.NET.Test.Sdk** (18.9.0), **Microsoft.Extensions.Logging.Console** (10.0.11).
|
||||
- Synced **RepoUtils** (`utils/`): engine/plugin updates; removed obsolete `Update-RepoUtils` tooling (local-copy sync only).
|
||||
|
||||
## [1.3.0] - 2026-07-27
|
||||
|
||||
### Added
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<RootNamespace>MaksIT.PodmanClientDotNet.PowerShell</RootNamespace>
|
||||
<AssemblyName>MaksIT.PodmanClientDotNet.PowerShell</AssemblyName>
|
||||
<Description>PowerShell module with cmdlets for PodmanClient.DotNet.</Description>
|
||||
<Version>1.3.0</Version>
|
||||
<Version>1.3.1</Version>
|
||||
<Authors>Maksym Sadovnychyy</Authors>
|
||||
<Company>MAKS-IT</Company>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
@ -16,8 +16,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Management.Automation" Version="7.6.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.9" />
|
||||
<PackageReference Include="System.Management.Automation" Version="7.6.5" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.11" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
|
||||
<!-- NuGet package metadata -->
|
||||
<PackageId>PodmanClient.DotNet</PackageId>
|
||||
<Version>1.3.0</Version>
|
||||
<Version>1.3.1</Version>
|
||||
<Authors>Maksym Sadovnychyy</Authors>
|
||||
<Company>MAKS-IT</Company>
|
||||
<Product>PodmanClient.DotNet</Product>
|
||||
@ -49,11 +49,11 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MaksIT.Results" Version="2.0.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.9" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="10.0.9" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.9" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.9" />
|
||||
<PackageReference Include="MaksIT.Results" Version="2.0.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.11" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="10.0.11" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.11" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.11" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@ -14,8 +14,8 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="10.0.9" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.7.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="10.0.11" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.9.0" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.4.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
@echo off
|
||||
pwsh -NoProfile -ExecutionPolicy Bypass -File "%~dp0tools\Update-RepoUtils\Update-RepoUtils.ps1" %*
|
||||
pause
|
||||
@ -5,21 +5,14 @@
|
||||
.SYNOPSIS
|
||||
Plugin-driven release engine entry script.
|
||||
|
||||
.PARAMETER DryRun
|
||||
When set, plugins that declare mutatesRemote in Get-PluginMetadata validate only (no registry push, GitHub release, or cluster deploy).
|
||||
|
||||
.PARAMETER Mode
|
||||
Optional override for HelmSelfDeploy values resolution (single or ha).
|
||||
When omitted, HelmSelfDeploy.deployMode from scriptSettings.json is used (CI/CD default).
|
||||
Manual installs: Invoke-ReleasePackage-Single.bat / Invoke-ReleasePackage-HA.bat.
|
||||
.NOTES
|
||||
Per-plugin dryRun on mutatesRemote plugins validates without remote mutation.
|
||||
There is no engine-wide dryRun switch — set dryRun on each plugin as needed.
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[switch]$DryRun,
|
||||
[ValidateSet('single', 'ha')]
|
||||
[string]$Mode
|
||||
)
|
||||
# Keep a plain param block so unbound launcher arguments remain in $args for
|
||||
# optional Initialize-ReleaseExtension (advanced binding would reject them).
|
||||
param()
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
Set-Location $PSScriptRoot
|
||||
@ -29,20 +22,43 @@ $srcDir = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path
|
||||
. (Join-Path $srcDir 'modules/Engine/Import-EngineModules.ps1')
|
||||
Import-EngineModules -Engine Release
|
||||
|
||||
$settings = Get-ScriptSettings -ScriptDir $PSScriptRoot
|
||||
$resolveModeParams = @{ Settings = $settings }
|
||||
if ($PSBoundParameters.ContainsKey('Mode')) {
|
||||
$resolveModeParams['ModeOverride'] = $Mode
|
||||
$releaseExtension = $null
|
||||
if (Get-Command Initialize-ReleaseExtension -ErrorAction SilentlyContinue) {
|
||||
$releaseExtension = Initialize-ReleaseExtension -ScriptDir $PSScriptRoot -ArgumentList $args
|
||||
}
|
||||
$deployMode = Get-DeployModeFromSettings @resolveModeParams
|
||||
|
||||
$settings = if ($null -ne $releaseExtension) {
|
||||
$releaseExtension.Settings
|
||||
}
|
||||
else {
|
||||
Get-ScriptSettings -ScriptDir $PSScriptRoot
|
||||
}
|
||||
|
||||
$configuredPlugins = Get-ConfiguredPlugins -Settings $settings
|
||||
|
||||
$releaseBanner = if ($null -ne $releaseExtension) {
|
||||
$releaseExtension.StepBanner
|
||||
}
|
||||
else {
|
||||
'RELEASE ENGINE'
|
||||
}
|
||||
|
||||
Write-Log -Level 'STEP' -Message '=================================================='
|
||||
Write-Log -Level 'STEP' -Message "RELEASE ENGINE (deploy mode: $deployMode)"
|
||||
Write-Log -Level 'STEP' -Message $releaseBanner
|
||||
Write-Log -Level 'STEP' -Message '=================================================='
|
||||
|
||||
$plugins = $configuredPlugins
|
||||
$engineContext = New-EngineContext -Plugins $plugins -ScriptDir $PSScriptRoot -SrcDir $srcDir -Settings $settings -DryRun:$DryRun -DeployMode $deployMode
|
||||
$newContextParams = @{
|
||||
Plugins = $plugins
|
||||
ScriptDir = $PSScriptRoot
|
||||
SrcDir = $srcDir
|
||||
Settings = $settings
|
||||
}
|
||||
if ($null -ne $releaseExtension) {
|
||||
$newContextParams['ExtensionData'] = $releaseExtension.ContextData
|
||||
}
|
||||
|
||||
$engineContext = New-EngineContext @newContextParams
|
||||
|
||||
Write-Log -Level 'OK' -Message 'All pre-flight checks passed!'
|
||||
$sharedPluginSettings = $engineContext
|
||||
@ -59,14 +75,14 @@ else {
|
||||
|
||||
if ((Test-IsPublishPlugin -Plugin $plugin -EngineDirectory $PSScriptRoot) -and -not $releaseStageInitialized) {
|
||||
if (Test-PluginRunnable -Plugin $plugin -SharedSettings $sharedPluginSettings -EngineDirectory $PSScriptRoot -WriteLogs:$false) {
|
||||
$remainingPlugins = @($plugins[$pluginIndex..($plugins.Count - 1)])
|
||||
Initialize-ReleaseStageContext -RemainingPlugins $remainingPlugins -SharedSettings $sharedPluginSettings -ArtifactsDirectory $engineContext.artifactsDirectory -Version $engineContext.version
|
||||
Initialize-ReleaseStageContext -SharedSettings $sharedPluginSettings -ArtifactsDirectory $engineContext.artifactsDirectory
|
||||
$releaseStageInitialized = $true
|
||||
}
|
||||
}
|
||||
|
||||
$pluginSucceeded = Invoke-ConfiguredPlugin -Plugin $plugin -SharedSettings $sharedPluginSettings -EngineDirectory $PSScriptRoot -ContinueOnError:$false
|
||||
if (-not $pluginSucceeded) {
|
||||
$pluginSucceeded = Invoke-ConfiguredPlugin -Plugin $plugin -SharedSettings $sharedPluginSettings -EngineDirectory $PSScriptRoot
|
||||
# Exact $true only: polluted arrays (CLI stdout + $false) are truthy under -not.
|
||||
if ($pluginSucceeded -ne $true) {
|
||||
$releaseHadPluginFailures = $true
|
||||
break
|
||||
}
|
||||
@ -88,9 +104,6 @@ elseif ($engineContext.PSObject.Properties.Name -contains 'skipPublishPlugins' -
|
||||
elseif ($engineContext.isNonReleaseBranch) {
|
||||
Write-Log -Level 'OK' -Message 'NON-RELEASE RUN COMPLETE'
|
||||
}
|
||||
elseif ($engineContext.dryRun) {
|
||||
Write-Log -Level 'OK' -Message 'DRY RUN COMPLETE'
|
||||
}
|
||||
else {
|
||||
Write-Log -Level 'OK' -Message 'RELEASE COMPLETE'
|
||||
}
|
||||
|
||||
@ -6,6 +6,8 @@
|
||||
Plugin-driven test and coverage engine entry script.
|
||||
#>
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
$srcDir = (Resolve-Path (Join-Path $scriptDir '..\..')).Path
|
||||
|
||||
@ -29,8 +31,9 @@ if ($configuredPlugins.Count -eq 0) {
|
||||
$testHadPluginFailures = $false
|
||||
|
||||
foreach ($plugin in $configuredPlugins) {
|
||||
$pluginSucceeded = Invoke-ConfiguredPlugin -Plugin $plugin -SharedSettings $engineContext -EngineDirectory $scriptDir -ContinueOnError:$false
|
||||
if (-not $pluginSucceeded) {
|
||||
$pluginSucceeded = Invoke-ConfiguredPlugin -Plugin $plugin -SharedSettings $engineContext -EngineDirectory $scriptDir
|
||||
# Exact $true only: polluted arrays (CLI stdout + $false) are truthy under -not.
|
||||
if ($pluginSucceeded -ne $true) {
|
||||
$testHadPluginFailures = $true
|
||||
break
|
||||
}
|
||||
|
||||
@ -3,13 +3,14 @@
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Helpers to resolve engine semver and relative paths from plugin configuration.
|
||||
Generic engine helpers: path resolution and the shared context facts API.
|
||||
|
||||
.DESCRIPTION
|
||||
Used by New-EngineContext and version plugins:
|
||||
- DotNetReleaseVersion plugin -> projectFiles (.csproj <Version>)
|
||||
- NpmReleaseVersion plugin -> packageJsonPath (package.json version)
|
||||
- FileReleaseVersion plugin -> versionFilePath (repo-root VERSION file)
|
||||
Engine-owned state stays on the context object (version, tag, skipPublishPlugins, …).
|
||||
Plugin-published values go under $context.facts[namespace][name] via Set/Get/Test-EngineFact.
|
||||
During migration, -LegacyProperty on Set dual-writes flat properties; Get falls back to them.
|
||||
|
||||
Version plugins declare providesVersion = $true; New-EngineContext discovers the single enabled one.
|
||||
#>
|
||||
|
||||
if (-not (Get-Command Write-Log -ErrorAction SilentlyContinue)) {
|
||||
@ -19,6 +20,16 @@ if (-not (Get-Command Write-Log -ErrorAction SilentlyContinue)) {
|
||||
}
|
||||
}
|
||||
|
||||
$script:EngineStateAllowlist = @(
|
||||
'version'
|
||||
'tag'
|
||||
'skipPublishPlugins'
|
||||
'releaseDir'
|
||||
'artifactsDirectory'
|
||||
'deployMode'
|
||||
'orchestrator'
|
||||
)
|
||||
|
||||
function Resolve-RelativePaths {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
@ -52,251 +63,270 @@ function Resolve-RelativePaths {
|
||||
return @($resolved)
|
||||
}
|
||||
|
||||
function Get-CsprojPropertyValue {
|
||||
function Initialize-EngineFactsBag {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[xml]$Csproj,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$PropertyName
|
||||
[psobject]$Context
|
||||
)
|
||||
|
||||
# SDK-style .csproj files can have multiple PropertyGroup nodes.
|
||||
# Use the first group that defines the requested property.
|
||||
$propNode = $Csproj.Project.PropertyGroup |
|
||||
Where-Object { $_.$PropertyName } |
|
||||
Select-Object -First 1
|
||||
if (-not ($Context.PSObject.Properties.Name -contains 'facts') -or $null -eq $Context.facts) {
|
||||
$Context | Add-Member -NotePropertyName facts -NotePropertyValue ([ordered]@{}) -Force
|
||||
}
|
||||
}
|
||||
|
||||
if ($propNode) {
|
||||
return $propNode.$PropertyName
|
||||
function Assert-EngineFactNamespace {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Namespace
|
||||
)
|
||||
|
||||
if ($Namespace -eq 'engine') {
|
||||
throw "Namespace 'engine' is reserved; use Set-EngineState / Get-EngineState for engine-owned fields."
|
||||
}
|
||||
|
||||
if ($Namespace -cnotmatch '^[a-z][a-z0-9]*$') {
|
||||
throw "Invalid facts namespace '$Namespace'. Use lowercase alphanumeric starting with a letter (e.g. 'test', 'dotnet')."
|
||||
}
|
||||
}
|
||||
|
||||
function Assert-EngineFactName {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Name
|
||||
)
|
||||
|
||||
if ($Name -cnotmatch '^[a-zA-Z][a-zA-Z0-9_]*$') {
|
||||
throw "Invalid fact name '$Name'. Use letters, digits, underscore; must start with a letter."
|
||||
}
|
||||
}
|
||||
|
||||
function Test-EngineFactValuePresent {
|
||||
param(
|
||||
[AllowNull()]
|
||||
$Value
|
||||
)
|
||||
|
||||
if ($null -eq $Value) {
|
||||
return $false
|
||||
}
|
||||
|
||||
if ($Value -is [string] -and [string]::IsNullOrWhiteSpace($Value)) {
|
||||
return $false
|
||||
}
|
||||
|
||||
return $true
|
||||
}
|
||||
|
||||
function Set-EngineFact {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[psobject]$Context,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Namespace,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Name,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[AllowNull()]
|
||||
$Value,
|
||||
|
||||
[ValidateSet('Error', 'Replace', 'Keep')]
|
||||
[string]$Overwrite = 'Error',
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[string]$LegacyProperty
|
||||
)
|
||||
|
||||
Assert-EngineFactNamespace -Namespace $Namespace
|
||||
Assert-EngineFactName -Name $Name
|
||||
Initialize-EngineFactsBag -Context $Context
|
||||
|
||||
if (-not $Context.facts.Contains($Namespace)) {
|
||||
$Context.facts[$Namespace] = [ordered]@{}
|
||||
}
|
||||
|
||||
$bag = $Context.facts[$Namespace]
|
||||
$exists = $bag.Contains($Name)
|
||||
if ($exists) {
|
||||
if ($Overwrite -eq 'Keep') {
|
||||
if (-not [string]::IsNullOrWhiteSpace($LegacyProperty) -and -not ($Context.PSObject.Properties.Name -contains $LegacyProperty)) {
|
||||
$Context | Add-Member -NotePropertyName $LegacyProperty -NotePropertyValue $bag[$Name] -Force
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if ($Overwrite -eq 'Error') {
|
||||
throw "Fact ${Namespace}.${Name} already set (use -Overwrite Replace or Keep)."
|
||||
}
|
||||
}
|
||||
|
||||
$bag[$Name] = $Value
|
||||
|
||||
if (-not [string]::IsNullOrWhiteSpace($LegacyProperty)) {
|
||||
$Context | Add-Member -NotePropertyName $LegacyProperty -NotePropertyValue $Value -Force
|
||||
}
|
||||
}
|
||||
|
||||
function Get-EngineFact {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[psobject]$Context,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Namespace,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Name,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
$Default,
|
||||
|
||||
[switch]$Required,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[string[]]$LegacyProperty
|
||||
)
|
||||
|
||||
Assert-EngineFactNamespace -Namespace $Namespace
|
||||
Assert-EngineFactName -Name $Name
|
||||
Initialize-EngineFactsBag -Context $Context
|
||||
|
||||
if ($Context.facts.Contains($Namespace) -and $Context.facts[$Namespace].Contains($Name)) {
|
||||
$value = $Context.facts[$Namespace][$Name]
|
||||
if (Test-EngineFactValuePresent -Value $value) {
|
||||
return $value
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($propertyName in @($LegacyProperty)) {
|
||||
if ([string]::IsNullOrWhiteSpace($propertyName)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if ($Context.PSObject.Properties.Name -contains $propertyName) {
|
||||
$legacyValue = $Context.$propertyName
|
||||
if (Test-EngineFactValuePresent -Value $legacyValue) {
|
||||
return $legacyValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($Required) {
|
||||
throw "Required fact ${Namespace}.${Name} is missing."
|
||||
}
|
||||
|
||||
if ($PSBoundParameters.ContainsKey('Default')) {
|
||||
return $Default
|
||||
}
|
||||
|
||||
return $null
|
||||
}
|
||||
|
||||
function Get-CsprojVersions {
|
||||
function Test-EngineFact {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string[]]$ProjectFiles
|
||||
[psobject]$Context,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Namespace,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Name,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[string[]]$LegacyProperty
|
||||
)
|
||||
|
||||
Write-Log -Level "INFO" -Message "Reading version(s) from SDK-style project files (projectFiles)..."
|
||||
$projectVersions = @{}
|
||||
$value = Get-EngineFact -Context $Context -Namespace $Namespace -Name $Name -LegacyProperty $LegacyProperty
|
||||
return (Test-EngineFactValuePresent -Value $value)
|
||||
}
|
||||
|
||||
foreach ($projectPath in $ProjectFiles) {
|
||||
if (-not (Test-Path $projectPath -PathType Leaf)) {
|
||||
Write-Error "Project file not found at: $projectPath"
|
||||
exit 1
|
||||
function Set-EngineState {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[psobject]$Context,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Name,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[AllowNull()]
|
||||
$Value
|
||||
)
|
||||
|
||||
if ($script:EngineStateAllowlist -notcontains $Name) {
|
||||
throw "Engine state key '$Name' is not allowlisted. Use Set-EngineFact for plugin outputs, or extend the engine allowlist for documented engine fields."
|
||||
}
|
||||
|
||||
$Context | Add-Member -NotePropertyName $Name -NotePropertyValue $Value -Force
|
||||
}
|
||||
|
||||
function Add-EnginePublishCompletion {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[psobject]$Context,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Publisher
|
||||
)
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($Publisher)) {
|
||||
throw "Publisher name is required."
|
||||
}
|
||||
|
||||
$completedBy = @(Get-EngineFact -Context $Context -Namespace 'publish' -Name 'completedBy' -Default @())
|
||||
if ($completedBy -notcontains $Publisher) {
|
||||
$completedBy += $Publisher
|
||||
}
|
||||
|
||||
Set-EngineFact -Context $Context -Namespace 'publish' -Name 'completedBy' -Value $completedBy -Overwrite Replace
|
||||
Set-EngineFact -Context $Context -Namespace 'publish' -Name 'completed' -Value $true -Overwrite Replace -LegacyProperty 'publishCompleted'
|
||||
}
|
||||
|
||||
function Get-EngineState {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[psobject]$Context,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Name,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
$Default,
|
||||
|
||||
[switch]$Required
|
||||
)
|
||||
|
||||
if ($script:EngineStateAllowlist -notcontains $Name) {
|
||||
throw "Engine state key '$Name' is not allowlisted."
|
||||
}
|
||||
|
||||
if ($Context.PSObject.Properties.Name -contains $Name) {
|
||||
$value = $Context.$Name
|
||||
if (Test-EngineFactValuePresent -Value $value) {
|
||||
return $value
|
||||
}
|
||||
|
||||
if ([System.IO.Path]::GetExtension($projectPath) -ne ".csproj") {
|
||||
Write-Error "Configured project file is not a .csproj file: $projectPath"
|
||||
exit 1
|
||||
}
|
||||
|
||||
[xml]$csproj = Get-Content $projectPath
|
||||
$version = Get-CsprojPropertyValue -Csproj $csproj -PropertyName "Version"
|
||||
|
||||
if (-not $version) {
|
||||
Write-Error "Version not found in $projectPath"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$projectVersions[$projectPath] = $version
|
||||
Write-Log -Level "OK" -Message " $([System.IO.Path]::GetFileName($projectPath)): $version"
|
||||
}
|
||||
|
||||
return $projectVersions
|
||||
if ($Required) {
|
||||
throw "Required engine state '$Name' is missing."
|
||||
}
|
||||
|
||||
if ($PSBoundParameters.ContainsKey('Default')) {
|
||||
return $Default
|
||||
}
|
||||
|
||||
return $null
|
||||
}
|
||||
|
||||
function Resolve-DotNetReleaseVersion {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[object[]]$Plugins,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$ScriptDir
|
||||
)
|
||||
|
||||
$releaseVersionPlugin = @($Plugins | Where-Object { $_.name -eq 'DotNetReleaseVersion' } | Select-Object -First 1)
|
||||
if ($releaseVersionPlugin.Count -eq 0 -or $null -eq $releaseVersionPlugin[0]) {
|
||||
Write-Error "Configure a DotNetReleaseVersion plugin in scriptSettings.json with projectFiles."
|
||||
exit 1
|
||||
}
|
||||
|
||||
$releaseVersionSettings = $releaseVersionPlugin[0]
|
||||
$projectFiles = @(Resolve-RelativePaths -Value $releaseVersionSettings.projectFiles -BasePath $ScriptDir)
|
||||
|
||||
if ($projectFiles.Count -eq 0) {
|
||||
Write-Error "Configure release version via DotNetReleaseVersion.projectFiles (first .csproj with <Version>)."
|
||||
exit 1
|
||||
}
|
||||
|
||||
$projectVersions = Get-CsprojVersions -ProjectFiles $projectFiles
|
||||
$version = $projectVersions[$projectFiles[0]]
|
||||
|
||||
return [pscustomobject]@{
|
||||
version = $version
|
||||
source = 'DotNetReleaseVersion'
|
||||
}
|
||||
}
|
||||
|
||||
function Resolve-NpmReleaseVersion {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[object[]]$Plugins,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$ScriptDir
|
||||
)
|
||||
|
||||
$releaseVersionPlugin = @($Plugins | Where-Object { $_.name -eq 'NpmReleaseVersion' } | Select-Object -First 1)
|
||||
if ($releaseVersionPlugin.Count -eq 0 -or $null -eq $releaseVersionPlugin[0]) {
|
||||
Write-Error "Configure an NpmReleaseVersion plugin in scriptSettings.json with packageJsonPath."
|
||||
exit 1
|
||||
}
|
||||
|
||||
$releaseVersionSettings = $releaseVersionPlugin[0]
|
||||
$packageJsonPaths = @(Resolve-RelativePaths -Value $releaseVersionSettings.packageJsonPath -BasePath $ScriptDir)
|
||||
|
||||
if ($packageJsonPaths.Count -eq 0) {
|
||||
Write-Error "Configure release version via NpmReleaseVersion.packageJsonPath."
|
||||
exit 1
|
||||
}
|
||||
|
||||
$packageJsonPath = $packageJsonPaths[0]
|
||||
if (-not (Test-Path $packageJsonPath -PathType Leaf)) {
|
||||
Write-Error "NpmReleaseVersion: package.json not found at: $packageJsonPath"
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Log -Level "INFO" -Message "Reading version from npm package.json (packageJsonPath)..."
|
||||
$json = Get-Content -Path $packageJsonPath -Raw -Encoding UTF8 | ConvertFrom-Json
|
||||
$version = [string]$json.version
|
||||
if ([string]::IsNullOrWhiteSpace($version)) {
|
||||
Write-Error "NpmReleaseVersion: 'version' is missing in '$packageJsonPath'."
|
||||
exit 1
|
||||
}
|
||||
|
||||
if ($version -notmatch '^\d+\.\d+\.\d+') {
|
||||
Write-Error "NpmReleaseVersion: version '$version' in '$packageJsonPath' is not a valid semver."
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Log -Level "OK" -Message " $([System.IO.Path]::GetFileName($packageJsonPath)): $version"
|
||||
|
||||
return [pscustomobject]@{
|
||||
version = $version
|
||||
source = 'NpmReleaseVersion'
|
||||
}
|
||||
}
|
||||
|
||||
function Get-VersionFileSemver {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$VersionFilePath
|
||||
)
|
||||
|
||||
if (-not (Test-Path $VersionFilePath -PathType Leaf)) {
|
||||
Write-Error "FileReleaseVersion: VERSION file not found at: $VersionFilePath"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$version = (Get-Content -Path $VersionFilePath -Raw -Encoding UTF8).Trim()
|
||||
if ([string]::IsNullOrWhiteSpace($version)) {
|
||||
Write-Error "FileReleaseVersion: VERSION file is empty at: $VersionFilePath"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$version = $version -replace '^[vV]', ''
|
||||
if ($version -notmatch '^\d+\.\d+\.\d+') {
|
||||
Write-Error "FileReleaseVersion: version '$version' in '$VersionFilePath' is not a valid semver."
|
||||
exit 1
|
||||
}
|
||||
|
||||
return $version
|
||||
}
|
||||
|
||||
function Resolve-FileReleaseVersion {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[object[]]$Plugins,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$ScriptDir
|
||||
)
|
||||
|
||||
$releaseVersionPlugin = @($Plugins | Where-Object { $_.name -eq 'FileReleaseVersion' } | Select-Object -First 1)
|
||||
if ($releaseVersionPlugin.Count -eq 0 -or $null -eq $releaseVersionPlugin[0]) {
|
||||
Write-Error "Configure a FileReleaseVersion plugin in scriptSettings.json with versionFilePath."
|
||||
exit 1
|
||||
}
|
||||
|
||||
$releaseVersionSettings = $releaseVersionPlugin[0]
|
||||
$versionFileSetting = if ($releaseVersionSettings.versionFilePath) {
|
||||
$releaseVersionSettings.versionFilePath
|
||||
}
|
||||
else {
|
||||
'..\\..\\..\\VERSION'
|
||||
}
|
||||
|
||||
$versionFilePaths = @(Resolve-RelativePaths -Value $versionFileSetting -BasePath $ScriptDir)
|
||||
if ($versionFilePaths.Count -eq 0) {
|
||||
Write-Error "Configure release version via FileReleaseVersion.versionFilePath (repo-root VERSION file)."
|
||||
exit 1
|
||||
}
|
||||
|
||||
$versionFilePath = $versionFilePaths[0]
|
||||
Write-Log -Level "INFO" -Message "Reading version from VERSION file (versionFilePath)..."
|
||||
$version = Get-VersionFileSemver -VersionFilePath $versionFilePath
|
||||
Write-Log -Level "OK" -Message " $([System.IO.Path]::GetFileName($versionFilePath)): $version"
|
||||
|
||||
return [pscustomobject]@{
|
||||
version = $version
|
||||
source = 'FileReleaseVersion'
|
||||
}
|
||||
}
|
||||
|
||||
function Resolve-ReleaseVersion {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[object[]]$Plugins,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$ScriptDir
|
||||
)
|
||||
|
||||
$dotnetPlugin = @($Plugins | Where-Object { $_.name -eq 'DotNetReleaseVersion' -and $_.enabled -ne $false })
|
||||
$npmPlugin = @($Plugins | Where-Object { $_.name -eq 'NpmReleaseVersion' -and $_.enabled -ne $false })
|
||||
$filePlugin = @($Plugins | Where-Object { $_.name -eq 'FileReleaseVersion' -and $_.enabled -ne $false })
|
||||
|
||||
$enabledVersionPlugins = @()
|
||||
if ($dotnetPlugin.Count -gt 0) { $enabledVersionPlugins += 'DotNetReleaseVersion' }
|
||||
if ($npmPlugin.Count -gt 0) { $enabledVersionPlugins += 'NpmReleaseVersion' }
|
||||
if ($filePlugin.Count -gt 0) { $enabledVersionPlugins += 'FileReleaseVersion' }
|
||||
|
||||
if ($enabledVersionPlugins.Count -gt 1) {
|
||||
Write-Error "Configure only one release version plugin: $($enabledVersionPlugins -join ', ')."
|
||||
exit 1
|
||||
}
|
||||
|
||||
if ($dotnetPlugin.Count -gt 0) {
|
||||
return Resolve-DotNetReleaseVersion -Plugins $Plugins -ScriptDir $ScriptDir
|
||||
}
|
||||
|
||||
if ($npmPlugin.Count -gt 0) {
|
||||
return Resolve-NpmReleaseVersion -Plugins $Plugins -ScriptDir $ScriptDir
|
||||
}
|
||||
|
||||
if ($filePlugin.Count -gt 0) {
|
||||
return Resolve-FileReleaseVersion -Plugins $Plugins -ScriptDir $ScriptDir
|
||||
}
|
||||
|
||||
Write-Error "Configure a DotNetReleaseVersion (projectFiles), NpmReleaseVersion (packageJsonPath), or FileReleaseVersion (versionFilePath) plugin in scriptSettings.json."
|
||||
exit 1
|
||||
}
|
||||
|
||||
Export-ModuleMember -Function Get-CsprojPropertyValue, Get-CsprojVersions, Get-VersionFileSemver, Resolve-RelativePaths, Resolve-DotNetReleaseVersion, Resolve-NpmReleaseVersion, Resolve-FileReleaseVersion, Resolve-ReleaseVersion
|
||||
|
||||
|
||||
|
||||
Export-ModuleMember -Function `
|
||||
Resolve-RelativePaths, `
|
||||
Initialize-EngineFactsBag, `
|
||||
Set-EngineFact, `
|
||||
Get-EngineFact, `
|
||||
Test-EngineFact, `
|
||||
Set-EngineState, `
|
||||
Add-EnginePublishCompletion, `
|
||||
Get-EngineState
|
||||
|
||||
@ -32,4 +32,9 @@ function Import-EngineModules {
|
||||
|
||||
Import-Module $modulePath -Force
|
||||
}
|
||||
|
||||
$extensionImport = Join-Path $modulesDir 'Extensions' 'Import-ExtensionModules.ps1'
|
||||
if (Test-Path -LiteralPath $extensionImport -PathType Leaf) {
|
||||
. $extensionImport
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@ function Test-IsEngineRuntimeModuleName {
|
||||
[string]$ModuleName
|
||||
)
|
||||
|
||||
# Engine runtime under modules/ only — never dual-homed under plugins/.
|
||||
# Host engine runtime under modules/ (and optional modules/Extensions/) — never dual-homed under plugins/.
|
||||
$engineNames = [System.Collections.Generic.HashSet[string]]::new(
|
||||
[string[]]@(
|
||||
'ChangelogSupport',
|
||||
@ -34,11 +34,7 @@ function Test-IsEngineRuntimeModuleName {
|
||||
'EngineContext',
|
||||
'PluginSupport',
|
||||
'ReleaseSupport',
|
||||
'TestSupport',
|
||||
'DeployConfig',
|
||||
'EngineContextSupport',
|
||||
'OrchestratorSupport',
|
||||
'PluginPathSupport'
|
||||
'TestSupport'
|
||||
),
|
||||
[System.StringComparer]::OrdinalIgnoreCase
|
||||
)
|
||||
@ -46,6 +42,34 @@ function Test-IsEngineRuntimeModuleName {
|
||||
return $engineNames.Contains($ModuleName)
|
||||
}
|
||||
|
||||
function Get-PluginDependencyGroupDirectories {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$PluginsRoot
|
||||
)
|
||||
|
||||
if (-not (Test-Path -LiteralPath $PluginsRoot -PathType Container)) {
|
||||
return @()
|
||||
}
|
||||
|
||||
# Prefer Shared (helpers), then stock host groups; any other plugins/{Group}/ is discovered.
|
||||
$preferred = @('Shared', 'Platform', 'DotNet', 'Npm')
|
||||
$dirs = [System.Collections.Generic.List[string]]::new()
|
||||
foreach ($name in $preferred) {
|
||||
$path = Join-Path $PluginsRoot $name
|
||||
if (Test-Path -LiteralPath $path -PathType Container) {
|
||||
$dirs.Add($path)
|
||||
}
|
||||
}
|
||||
|
||||
Get-ChildItem -LiteralPath $PluginsRoot -Directory -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.Name -notin $preferred } |
|
||||
Sort-Object Name |
|
||||
ForEach-Object { $dirs.Add($_.FullName) }
|
||||
|
||||
return @($dirs)
|
||||
}
|
||||
|
||||
function Import-PluginDependency {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
@ -66,16 +90,16 @@ function Import-PluginDependency {
|
||||
$candidatePaths = [System.Collections.Generic.List[string]]::new()
|
||||
|
||||
if (Test-IsEngineRuntimeModuleName -ModuleName $ModuleName) {
|
||||
# Engine runtime: modules/ only (no plugins/ fallback).
|
||||
# Engine runtime: modules/ only (no plugins/ fallback). Optional Extensions/ for layered hosts.
|
||||
$candidatePaths.Add((Join-Path $modulesDir "$ModuleName.psm1"))
|
||||
$candidatePaths.Add((Join-Path $engineModuleDir "$ModuleName.psm1"))
|
||||
$extensionsDir = Join-Path $modulesDir 'Extensions'
|
||||
$candidatePaths.Add((Join-Path $extensionsDir "$ModuleName.psm1"))
|
||||
}
|
||||
else {
|
||||
# Plugin helpers: plugins/ only (no modules/ legacy shadow).
|
||||
foreach ($group in @('Shared', 'Platform', 'DotNet', 'Npm', 'Helm', 'Docker', 'Podman')) {
|
||||
$candidatePaths.Add((Join-Path (Join-Path $pluginsRoot $group) "$ModuleName.psm1"))
|
||||
# Plugin helpers: plugins/{Group}/ only (no modules/ legacy shadow). Groups are discovered.
|
||||
foreach ($groupDir in Get-PluginDependencyGroupDirectories -PluginsRoot $pluginsRoot) {
|
||||
$candidatePaths.Add((Join-Path $groupDir "$ModuleName.psm1"))
|
||||
}
|
||||
}
|
||||
|
||||
@ -344,8 +368,8 @@ function Get-RegistryCredentialsFromRuntime {
|
||||
|
||||
.DESCRIPTION
|
||||
Looks up the environment variable named by SecretName. The value must be
|
||||
Base64(UTF8('username:password')). Used by Docker/Podman/Helm registry login
|
||||
and image-pull secret creation — never pass the password itself as a parameter.
|
||||
Base64(UTF8('username:password')). Used by registry login and image-pull
|
||||
secret creation — never pass the password itself as a parameter.
|
||||
|
||||
.PARAMETER SecretName
|
||||
Logical secret name (environment variable name), not a password or token.
|
||||
@ -671,10 +695,7 @@ function Invoke-ConfiguredPlugin {
|
||||
[psobject]$SharedSettings,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$EngineDirectory,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[bool]$ContinueOnError = $false
|
||||
[string]$EngineDirectory
|
||||
)
|
||||
|
||||
if (-not (Test-PluginRunnable -Plugin $Plugin -SharedSettings $SharedSettings -EngineDirectory $EngineDirectory -WriteLogs:$true)) {
|
||||
@ -716,12 +737,16 @@ function Invoke-ConfiguredPlugin {
|
||||
$pluginModulePath = Resolve-PluginModulePath -Plugin $Plugin -EngineDirectory $EngineDirectory
|
||||
Write-Log -Level "STEP" -Message "Running plugin '$($Plugin.name)'..."
|
||||
|
||||
# Sink plugin success-stream output to the host so it cannot pollute this
|
||||
# function's return value. Otherwise `return $false` after CLI stdout becomes
|
||||
# @("helm-line…", $false), which is truthy under `if (-not $result)` and
|
||||
# causes RELEASE COMPLETE / exit 0 after a failed plugin.
|
||||
try {
|
||||
$moduleInfo = Import-Module $pluginModulePath -Force -PassThru -ErrorAction Stop
|
||||
$invokeCommand = Get-Command -Name "Invoke-Plugin" -Module $moduleInfo.Name -ErrorAction Stop
|
||||
$pluginSettings = New-PluginInvocationSettings -Plugin $Plugin -SharedSettings $SharedSettings
|
||||
|
||||
& $invokeCommand -Settings $pluginSettings
|
||||
& $invokeCommand -Settings $pluginSettings | ForEach-Object { Write-Host $_ }
|
||||
Write-Log -Level "OK" -Message " Plugin '$($Plugin.name)' completed."
|
||||
return $true
|
||||
}
|
||||
|
||||
@ -24,13 +24,101 @@ if (-not (Get-Command Get-PluginStageLabel -ErrorAction SilentlyContinue) -or -n
|
||||
}
|
||||
}
|
||||
|
||||
if (-not (Get-Command Resolve-ReleaseVersion -ErrorAction SilentlyContinue)) {
|
||||
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)) {
|
||||
@ -48,21 +136,20 @@ function Assert-WorkingTreeClean {
|
||||
|
||||
function Initialize-ReleaseStageContext {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[object[]]$RemainingPlugins,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[psobject]$SharedSettings,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$ArtifactsDirectory,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Version
|
||||
[string]$ArtifactsDirectory
|
||||
)
|
||||
|
||||
if (-not $SharedSettings.PSObject.Properties['releaseDir'] -or [string]::IsNullOrWhiteSpace([string]$SharedSettings.releaseDir)) {
|
||||
$SharedSettings | Add-Member -NotePropertyName releaseDir -NotePropertyValue $ArtifactsDirectory -Force
|
||||
if (Get-Command Set-EngineState -ErrorAction SilentlyContinue) {
|
||||
Set-EngineState -Context $SharedSettings -Name 'releaseDir' -Value $ArtifactsDirectory
|
||||
}
|
||||
else {
|
||||
$SharedSettings | Add-Member -NotePropertyName releaseDir -NotePropertyValue $ArtifactsDirectory -Force
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,15 +167,10 @@ function New-EngineContext {
|
||||
[Parameter(Mandatory = $false)]
|
||||
[psobject]$Settings,
|
||||
|
||||
[switch]$DryRun,
|
||||
|
||||
[ValidateSet('single', 'ha')]
|
||||
[string]$DeployMode = 'ha'
|
||||
[Parameter(Mandatory = $false)]
|
||||
[psobject]$ExtensionData
|
||||
)
|
||||
|
||||
$resolvedVersion = Resolve-ReleaseVersion -Plugins $Plugins -ScriptDir $ScriptDir
|
||||
$version = $resolvedVersion.version
|
||||
$versionSource = $resolvedVersion.source
|
||||
$releaseRelative = '..\..\..\releases'
|
||||
$artifactsDirectory = [System.IO.Path]::GetFullPath((Join-Path $ScriptDir $releaseRelative))
|
||||
|
||||
@ -116,48 +198,58 @@ function New-EngineContext {
|
||||
$releaseBranches = @('main')
|
||||
}
|
||||
|
||||
$isReleaseBranch = $releaseBranches -contains $currentBranch
|
||||
$isNonReleaseBranch = -not $isReleaseBranch
|
||||
$isNonReleaseBranch = -not ($releaseBranches -contains $currentBranch)
|
||||
|
||||
Assert-WorkingTreeClean
|
||||
|
||||
$tag = "v$version"
|
||||
Write-Log -Level "INFO" -Message " Release tag default from ${versionSource}: $tag (ReleasePublishGuard may replace from git when publish is allowed)."
|
||||
|
||||
$dryRun = $false
|
||||
if ($DryRun) {
|
||||
$dryRun = $true
|
||||
}
|
||||
else {
|
||||
$dryRun = Get-EngineDryRun -Settings $Settings
|
||||
}
|
||||
Write-Log -Level "INFO" -Message " Dry run (remote mutations only): $dryRun"
|
||||
|
||||
$orchestrator = Get-MaksitOrchestrator
|
||||
if ($orchestrator) {
|
||||
Write-Log -Level "INFO" -Message " Orchestrator: $orchestrator (plugin profile filtering active)"
|
||||
}
|
||||
else {
|
||||
Write-Log -Level "INFO" -Message " Orchestrator: not set (dev mode — all plugins eligible; engine probe still selects docker vs podman)"
|
||||
}
|
||||
|
||||
return [pscustomobject]@{
|
||||
$context = [pscustomobject]@{
|
||||
scriptDir = $ScriptDir
|
||||
srcDir = $SrcDir
|
||||
utilsDir = $SrcDir
|
||||
currentBranch = $currentBranch
|
||||
version = $version
|
||||
tag = $tag
|
||||
artifactsDirectory = $artifactsDirectory
|
||||
isReleaseBranch = $isReleaseBranch
|
||||
isNonReleaseBranch = $isNonReleaseBranch
|
||||
releaseBranches = $releaseBranches
|
||||
publishCompleted = $false
|
||||
skipPublishPlugins = $false
|
||||
dryRun = $dryRun
|
||||
deployMode = $DeployMode
|
||||
orchestrator = $orchestrator
|
||||
facts = [ordered]@{}
|
||||
}
|
||||
|
||||
$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 {
|
||||
|
||||
@ -10,6 +10,13 @@ if (-not (Get-Command Write-Log -ErrorAction SilentlyContinue)) {
|
||||
}
|
||||
}
|
||||
|
||||
if (-not (Get-Command Initialize-EngineFactsBag -ErrorAction SilentlyContinue)) {
|
||||
$engineContextModulePath = Join-Path $PSScriptRoot "EngineContext.psm1"
|
||||
if (Test-Path $engineContextModulePath -PathType Leaf) {
|
||||
Import-Module $engineContextModulePath -Force
|
||||
}
|
||||
}
|
||||
|
||||
function New-EngineContext {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
@ -21,22 +28,32 @@ function New-EngineContext {
|
||||
[Parameter(Mandatory = $false)]
|
||||
[psobject]$Settings,
|
||||
|
||||
[ValidateSet('single', 'ha')]
|
||||
[string]$DeployMode = 'ha'
|
||||
[Parameter(Mandatory = $false)]
|
||||
[psobject]$ExtensionData
|
||||
)
|
||||
|
||||
$badgesDir = $null
|
||||
if ($Settings -and $Settings.PSObject.Properties['paths'] -and $Settings.paths.badgesDir) {
|
||||
$badgesDir = [System.IO.Path]::GetFullPath((Join-Path $ScriptDir ([string]$Settings.paths.badgesDir)))
|
||||
}
|
||||
|
||||
return [pscustomobject]@{
|
||||
$context = [pscustomobject]@{
|
||||
scriptDir = $ScriptDir
|
||||
srcDir = $SrcDir
|
||||
utilsDir = $SrcDir
|
||||
badgesDir = $badgesDir
|
||||
deployMode = $DeployMode
|
||||
facts = [ordered]@{}
|
||||
}
|
||||
|
||||
$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
|
||||
}
|
||||
|
||||
Export-ModuleMember -Function New-EngineContext
|
||||
|
||||
@ -1,6 +1,16 @@
|
||||
#requires -Version 7.0
|
||||
#requires -PSEdition Core
|
||||
|
||||
<#
|
||||
Runs native CLIs (dotnet, git, helm, …) and keeps $LASTEXITCODE intact.
|
||||
By default throws on non-zero exit so callers cannot forget to check.
|
||||
Pass -ThrowOnError:$false when you need the exit code / output yourself
|
||||
(e.g. TestRunner Success objects, logging full container build output first).
|
||||
|
||||
Test hooks: Set-ExternalCommandTestHandler / Set-ExternalCommandAvailability
|
||||
let Pester stub CLIs without touching PATH.
|
||||
#>
|
||||
|
||||
$script:ExternalCommandTestHandler = $null
|
||||
$script:ExternalCommandAvailability = @{}
|
||||
|
||||
@ -40,7 +50,10 @@ function Invoke-ExternalCommand {
|
||||
|
||||
[string]$InputObject,
|
||||
|
||||
[switch]$MergeErrorOutput
|
||||
[switch]$MergeErrorOutput,
|
||||
|
||||
# Default true: fail fast. Soft callers (tests, nested loggers) pass $false.
|
||||
[bool]$ThrowOnError = $true
|
||||
)
|
||||
|
||||
$previousLocation = $null
|
||||
@ -51,6 +64,7 @@ function Invoke-ExternalCommand {
|
||||
|
||||
try {
|
||||
$effectiveWorkingDirectory = (Get-Location).Path
|
||||
$output = @()
|
||||
|
||||
if ($null -ne $script:ExternalCommandTestHandler) {
|
||||
$handlerResult = & $script:ExternalCommandTestHandler `
|
||||
@ -62,31 +76,46 @@ function Invoke-ExternalCommand {
|
||||
|
||||
$global:LASTEXITCODE = [int]$handlerResult.ExitCode
|
||||
if ($null -eq $handlerResult.Output) {
|
||||
return @()
|
||||
$output = @()
|
||||
}
|
||||
|
||||
if ($handlerResult.Output -is [System.Collections.IEnumerable] -and -not ($handlerResult.Output -is [string])) {
|
||||
return @($handlerResult.Output)
|
||||
elseif ($handlerResult.Output -is [System.Collections.IEnumerable] -and -not ($handlerResult.Output -is [string])) {
|
||||
$output = @($handlerResult.Output)
|
||||
}
|
||||
else {
|
||||
$output = @([string]$handlerResult.Output)
|
||||
}
|
||||
|
||||
return @([string]$handlerResult.Output)
|
||||
}
|
||||
|
||||
if ($script:ExternalCommandAvailability.ContainsKey($Name) -and -not $script:ExternalCommandAvailability[$Name]) {
|
||||
throw "External command '$Name' is marked unavailable."
|
||||
}
|
||||
|
||||
if (-not [string]::IsNullOrWhiteSpace($InputObject)) {
|
||||
$output = $InputObject | & $Name @ArgumentList 2>&1
|
||||
}
|
||||
elseif ($MergeErrorOutput) {
|
||||
$output = & $Name @ArgumentList 2>&1
|
||||
}
|
||||
else {
|
||||
$output = & $Name @ArgumentList
|
||||
if ($script:ExternalCommandAvailability.ContainsKey($Name) -and -not $script:ExternalCommandAvailability[$Name]) {
|
||||
throw "External command '$Name' is marked unavailable."
|
||||
}
|
||||
|
||||
if (-not [string]::IsNullOrWhiteSpace($InputObject)) {
|
||||
$raw = $InputObject | & $Name @ArgumentList 2>&1
|
||||
}
|
||||
elseif ($MergeErrorOutput) {
|
||||
$raw = & $Name @ArgumentList 2>&1
|
||||
}
|
||||
else {
|
||||
$raw = & $Name @ArgumentList
|
||||
}
|
||||
|
||||
$output = @($raw)
|
||||
}
|
||||
|
||||
return @($output)
|
||||
$exitCode = [int]$global:LASTEXITCODE
|
||||
if ($ThrowOnError -and $exitCode -ne 0) {
|
||||
$preview = ($output | ForEach-Object {
|
||||
if ($_ -is [System.Management.Automation.ErrorRecord]) { $_.ToString() } else { [string]$_ }
|
||||
} | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | Select-Object -First 8) -join ' '
|
||||
if ([string]::IsNullOrWhiteSpace($preview)) {
|
||||
throw "External command '$Name' failed with exit code $exitCode."
|
||||
}
|
||||
|
||||
throw "External command '$Name' failed with exit code $exitCode. $preview"
|
||||
}
|
||||
|
||||
return $output
|
||||
}
|
||||
finally {
|
||||
if ($null -ne $previousLocation) {
|
||||
|
||||
@ -51,7 +51,33 @@ function Invoke-GitInternal {
|
||||
[string]$ErrorMessage = "Git command failed"
|
||||
)
|
||||
|
||||
if (-not (Get-Command Invoke-ExternalCommand -ErrorAction SilentlyContinue)) {
|
||||
$srcDir = Split-Path $PSScriptRoot -Parent
|
||||
$externalModule = Join-Path $PSScriptRoot 'ExternalCommandSupport.psm1'
|
||||
if (Test-Path -LiteralPath $externalModule -PathType Leaf) {
|
||||
Import-Module $externalModule -Global
|
||||
}
|
||||
elseif (Test-Path -LiteralPath (Join-Path $srcDir 'modules' 'ExternalCommandSupport.psm1') -PathType Leaf) {
|
||||
Import-Module (Join-Path $srcDir 'modules' 'ExternalCommandSupport.psm1') -Global
|
||||
}
|
||||
}
|
||||
|
||||
if ($CaptureOutput) {
|
||||
if (Get-Command Invoke-ExternalCommand -ErrorAction SilentlyContinue) {
|
||||
$output = Invoke-ExternalCommand -Name git -ArgumentList $Arguments -MergeErrorOutput -ThrowOnError:$false
|
||||
$exitCode = $LASTEXITCODE
|
||||
if ($exitCode -ne 0) {
|
||||
Write-Error "$ErrorMessage (exit code: $exitCode)"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if ($null -eq $output) {
|
||||
return ""
|
||||
}
|
||||
|
||||
return ($output -join "`n").Trim()
|
||||
}
|
||||
|
||||
$output = & git @Arguments 2>&1
|
||||
$exitCode = $LASTEXITCODE
|
||||
if ($exitCode -ne 0) {
|
||||
@ -66,7 +92,13 @@ function Invoke-GitInternal {
|
||||
return ($output -join "`n").Trim()
|
||||
}
|
||||
|
||||
& git @Arguments
|
||||
if (Get-Command Invoke-ExternalCommand -ErrorAction SilentlyContinue) {
|
||||
Invoke-ExternalCommand -Name git -ArgumentList $Arguments -MergeErrorOutput -ThrowOnError:$false | Out-Null
|
||||
}
|
||||
else {
|
||||
& git @Arguments
|
||||
}
|
||||
|
||||
$exitCode = $LASTEXITCODE
|
||||
if ($exitCode -ne 0) {
|
||||
Write-Error "$ErrorMessage (exit code: $exitCode)"
|
||||
|
||||
@ -7,25 +7,11 @@ function Get-ScriptSettings {
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$ScriptDir,
|
||||
|
||||
[ValidateSet('single', 'ha')]
|
||||
[string]$Mode,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[string]$SettingsFileName = 'scriptSettings.json'
|
||||
)
|
||||
|
||||
$settingsPath = if ($PSBoundParameters.ContainsKey('Mode')) {
|
||||
$modePath = Join-Path $ScriptDir "scriptSettings.$Mode.json"
|
||||
if (Test-Path -LiteralPath $modePath -PathType Leaf) {
|
||||
$modePath
|
||||
}
|
||||
else {
|
||||
Join-Path $ScriptDir $SettingsFileName
|
||||
}
|
||||
}
|
||||
else {
|
||||
Join-Path $ScriptDir $SettingsFileName
|
||||
}
|
||||
$settingsPath = Join-Path $ScriptDir $SettingsFileName
|
||||
|
||||
if (-not (Test-Path -LiteralPath $settingsPath -PathType Leaf)) {
|
||||
throw "Settings file not found: $settingsPath"
|
||||
@ -34,92 +20,6 @@ function Get-ScriptSettings {
|
||||
return Get-Content -LiteralPath $settingsPath -Raw | ConvertFrom-Json
|
||||
}
|
||||
|
||||
function Get-HelmSelfDeployPluginFromSettings {
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[psobject]$Settings
|
||||
)
|
||||
|
||||
if (-not ($Settings.PSObject.Properties.Name -contains 'plugins') -or -not $Settings.plugins) {
|
||||
return $null
|
||||
}
|
||||
|
||||
foreach ($plugin in @($Settings.plugins)) {
|
||||
if ($plugin.name -eq 'HelmSelfDeploy') {
|
||||
return $plugin
|
||||
}
|
||||
}
|
||||
|
||||
return $null
|
||||
}
|
||||
|
||||
function Get-DeployModeFromSettings {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Resolves cluster deploy profile from HelmSelfDeploy plugin settings or an explicit CLI override.
|
||||
|
||||
.DESCRIPTION
|
||||
CI/CD pipelines omit -Mode and read deployMode on the HelmSelfDeploy plugin.
|
||||
Manual installs use Invoke-ReleasePackage-Single.bat / Invoke-ReleasePackage-HA.bat.
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[psobject]$Settings,
|
||||
|
||||
[ValidateSet('single', 'ha')]
|
||||
[string]$ModeOverride
|
||||
)
|
||||
|
||||
if ($PSBoundParameters.ContainsKey('ModeOverride')) {
|
||||
return $ModeOverride
|
||||
}
|
||||
|
||||
$helmSelfDeploy = Get-HelmSelfDeployPluginFromSettings -Settings $Settings
|
||||
if ($null -ne $helmSelfDeploy -and $helmSelfDeploy.PSObject.Properties.Name -contains 'deployMode') {
|
||||
$mode = [string]$helmSelfDeploy.deployMode
|
||||
if (-not [string]::IsNullOrWhiteSpace($mode)) {
|
||||
$mode = $mode.Trim().ToLowerInvariant()
|
||||
if ($mode -in @('single', 'ha')) {
|
||||
return $mode
|
||||
}
|
||||
|
||||
throw "HelmSelfDeploy.deployMode must be 'single' or 'ha' (got '$mode')."
|
||||
}
|
||||
}
|
||||
|
||||
return 'ha'
|
||||
}
|
||||
|
||||
function Resolve-DeployValuesFilePath {
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$DeploySettingsDir,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$ValuesFile,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[ValidateSet('single', 'ha')]
|
||||
[string]$DeployMode
|
||||
)
|
||||
|
||||
$baseName = [System.IO.Path]::GetFileNameWithoutExtension($ValuesFile)
|
||||
$extension = [System.IO.Path]::GetExtension($ValuesFile)
|
||||
if ([string]::IsNullOrEmpty($extension)) {
|
||||
$extension = '.yaml'
|
||||
}
|
||||
|
||||
$valuesPath = Join-Path $DeploySettingsDir "$baseName.$DeployMode$extension"
|
||||
if (Test-Path -LiteralPath $valuesPath -PathType Leaf) {
|
||||
return $valuesPath
|
||||
}
|
||||
|
||||
throw "Deploy values file not found: '$valuesPath'. HelmSelfDeploy expects values.single.yaml or values.ha.yaml beside scriptSettings.json."
|
||||
}
|
||||
|
||||
function Assert-Command {
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
@ -132,4 +32,4 @@ function Assert-Command {
|
||||
}
|
||||
}
|
||||
|
||||
Export-ModuleMember -Function Get-ScriptSettings, Get-DeployModeFromSettings, Resolve-DeployValuesFilePath, Assert-Command
|
||||
Export-ModuleMember -Function Get-ScriptSettings, Assert-Command
|
||||
|
||||
@ -19,10 +19,8 @@ function Import-ExternalCommandSupportInternal {
|
||||
return
|
||||
}
|
||||
|
||||
$srcDir = Split-Path $PSScriptRoot -Parent
|
||||
$candidates = @(
|
||||
(Join-Path $PSScriptRoot 'ExternalCommandSupport.psm1'),
|
||||
(Join-Path $srcDir 'plugins' 'Shared' 'ExternalCommandSupport.psm1')
|
||||
(Join-Path $PSScriptRoot 'ExternalCommandSupport.psm1')
|
||||
)
|
||||
foreach ($modulePath in $candidates) {
|
||||
if (Test-Path -LiteralPath $modulePath -PathType Leaf) {
|
||||
@ -175,10 +173,10 @@ function Invoke-TestsWithCoverage {
|
||||
|
||||
Import-ExternalCommandSupportInternal
|
||||
if ($Silent) {
|
||||
$null = Invoke-ExternalCommand -Name dotnet -ArgumentList $dotnetArgs -MergeErrorOutput
|
||||
$null = Invoke-ExternalCommand -Name dotnet -ArgumentList $dotnetArgs -MergeErrorOutput -ThrowOnError:$false
|
||||
}
|
||||
else {
|
||||
Invoke-ExternalCommand -Name dotnet -ArgumentList $dotnetArgs | Out-Default
|
||||
Invoke-ExternalCommand -Name dotnet -ArgumentList $dotnetArgs -ThrowOnError:$false | Out-Default
|
||||
}
|
||||
|
||||
$testExitCode = $LASTEXITCODE
|
||||
@ -356,10 +354,10 @@ function Invoke-NpmJestTestsWithCoverage {
|
||||
$npmArgs = @('run', $TestScript, '--', '--coverage', '--coverageReporters=json-summary', '--coverageReporters=text')
|
||||
Import-ExternalCommandSupportInternal
|
||||
if ($Silent) {
|
||||
$null = Invoke-ExternalCommand -Name npm -ArgumentList $npmArgs -MergeErrorOutput
|
||||
$null = Invoke-ExternalCommand -Name npm -ArgumentList $npmArgs -MergeErrorOutput -ThrowOnError:$false
|
||||
}
|
||||
else {
|
||||
Invoke-ExternalCommand -Name npm -ArgumentList $npmArgs | Out-Default
|
||||
Invoke-ExternalCommand -Name npm -ArgumentList $npmArgs -ThrowOnError:$false | Out-Default
|
||||
}
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
|
||||
@ -26,6 +26,7 @@ function Invoke-Plugin {
|
||||
)
|
||||
|
||||
Import-PluginDependency -ModuleName "Logging" -RequiredCommand "Write-Log"
|
||||
Import-PluginDependency -ModuleName "EngineContext" -RequiredCommand "Set-EngineFact"
|
||||
|
||||
$pluginSettings = $Settings
|
||||
$sharedSettings = $Settings.context
|
||||
@ -33,13 +34,18 @@ function Invoke-Plugin {
|
||||
$version = $sharedSettings.version
|
||||
$archiveInputs = @()
|
||||
|
||||
if ($sharedSettings.PSObject.Properties['releaseArchiveInputs'] -and $sharedSettings.releaseArchiveInputs) {
|
||||
$archiveInputs = @($sharedSettings.releaseArchiveInputs)
|
||||
$fromFact = Get-EngineFact -Context $sharedSettings -Namespace 'release' -Name 'archiveInputs' -LegacyProperty @('releaseArchiveInputs')
|
||||
if ($null -ne $fromFact) {
|
||||
$archiveInputs = @($fromFact)
|
||||
}
|
||||
elseif ($sharedSettings.PSObject.Properties['packageFile'] -and $sharedSettings.packageFile) {
|
||||
$archiveInputs = @($sharedSettings.packageFile.FullName)
|
||||
if ($sharedSettings.PSObject.Properties['symbolsPackageFile'] -and $sharedSettings.symbolsPackageFile) {
|
||||
$archiveInputs += $sharedSettings.symbolsPackageFile.FullName
|
||||
else {
|
||||
$packageFile = Get-EngineFact -Context $sharedSettings -Namespace 'dotnet' -Name 'packageFile' -LegacyProperty @('packageFile')
|
||||
if ($null -ne $packageFile) {
|
||||
$archiveInputs = @($packageFile.FullName)
|
||||
$symbolsPackageFile = Get-EngineFact -Context $sharedSettings -Namespace 'dotnet' -Name 'symbolsPackageFile' -LegacyProperty @('symbolsPackageFile')
|
||||
if ($null -ne $symbolsPackageFile) {
|
||||
$archiveInputs += $symbolsPackageFile.FullName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,16 +85,18 @@ function Invoke-Plugin {
|
||||
Write-Log -Level "OK" -Message " Release archive ready: $zipPath"
|
||||
|
||||
$releaseAssetPaths = @($zipPath)
|
||||
if ($sharedSettings.PSObject.Properties['packageFile'] -and $sharedSettings.packageFile) {
|
||||
$releaseAssetPaths += $sharedSettings.packageFile.FullName
|
||||
$packageFile = Get-EngineFact -Context $sharedSettings -Namespace 'dotnet' -Name 'packageFile' -LegacyProperty @('packageFile')
|
||||
if ($null -ne $packageFile) {
|
||||
$releaseAssetPaths += $packageFile.FullName
|
||||
}
|
||||
if ($sharedSettings.PSObject.Properties['symbolsPackageFile'] -and $sharedSettings.symbolsPackageFile) {
|
||||
$releaseAssetPaths += $sharedSettings.symbolsPackageFile.FullName
|
||||
$symbolsPackageFile = Get-EngineFact -Context $sharedSettings -Namespace 'dotnet' -Name 'symbolsPackageFile' -LegacyProperty @('symbolsPackageFile')
|
||||
if ($null -ne $symbolsPackageFile) {
|
||||
$releaseAssetPaths += $symbolsPackageFile.FullName
|
||||
}
|
||||
|
||||
$sharedSettings | Add-Member -NotePropertyName releaseDir -NotePropertyValue $artifactsDirectory -Force
|
||||
$sharedSettings | Add-Member -NotePropertyName releaseArchivePath -NotePropertyValue $zipPath -Force
|
||||
$sharedSettings | Add-Member -NotePropertyName releaseAssetPaths -NotePropertyValue $releaseAssetPaths -Force
|
||||
Set-EngineState -Context $sharedSettings -Name 'releaseDir' -Value $artifactsDirectory
|
||||
Set-EngineFact -Context $sharedSettings -Namespace 'release' -Name 'archivePath' -Value $zipPath -Overwrite Replace -LegacyProperty 'releaseArchivePath'
|
||||
Set-EngineFact -Context $sharedSettings -Namespace 'release' -Name 'assetPaths' -Value $releaseAssetPaths -Overwrite Replace -LegacyProperty 'releaseAssetPaths'
|
||||
}
|
||||
|
||||
Export-ModuleMember -Function Invoke-Plugin
|
||||
|
||||
@ -76,7 +76,8 @@ function Invoke-Plugin {
|
||||
}
|
||||
|
||||
Write-Log -Level "OK" -Message " NuGet push completed."
|
||||
$sharedSettings | Add-Member -NotePropertyName publishCompleted -NotePropertyValue $true -Force
|
||||
Import-PluginDependency -ModuleName "EngineContext" -RequiredCommand "Add-EnginePublishCompletion"
|
||||
Add-EnginePublishCompletion -Context $sharedSettings -Publisher 'DotNetNuGet'
|
||||
}
|
||||
|
||||
function Get-PluginMetadata {
|
||||
|
||||
@ -30,7 +30,8 @@ function Invoke-Plugin {
|
||||
|
||||
Import-PluginDependency -ModuleName "Logging" -RequiredCommand "Write-Log"
|
||||
Import-PluginDependency -ModuleName "ScriptConfig" -RequiredCommand "Assert-Command"
|
||||
Import-PluginDependency -ModuleName "EngineContext" -RequiredCommand "Resolve-RelativePaths"
|
||||
Import-PluginDependency -ModuleName "EngineContext" -RequiredCommand "Set-EngineFact"
|
||||
Import-PluginDependency -ModuleName "DotNetArtifactSupport" -RequiredCommand "Resolve-DotNetPackageArtifacts"
|
||||
|
||||
$sharedSettings = $Settings.context
|
||||
$scriptDir = $sharedSettings.scriptDir
|
||||
@ -48,12 +49,12 @@ function Invoke-Plugin {
|
||||
|
||||
if ($Settings.PSObject.Properties['artifactsDir'] -and -not [string]::IsNullOrWhiteSpace([string]$Settings.artifactsDir)) {
|
||||
$artifactsDirectory = [System.IO.Path]::GetFullPath((Join-Path $scriptDir ([string]$Settings.artifactsDir)))
|
||||
Set-EngineState -Context $sharedSettings -Name 'artifactsDirectory' -Value $artifactsDirectory
|
||||
Set-EngineState -Context $sharedSettings -Name 'releaseDir' -Value $artifactsDirectory
|
||||
}
|
||||
else {
|
||||
$artifactsDirectory = $sharedSettings.artifactsDirectory
|
||||
}
|
||||
$packageProjectPath = $null
|
||||
$releaseArchiveInputs = @()
|
||||
|
||||
Assert-Command dotnet
|
||||
|
||||
@ -79,50 +80,19 @@ function Invoke-Plugin {
|
||||
throw "dotnet pack failed for $packageProjectPath."
|
||||
}
|
||||
|
||||
# dotnet pack can leave older packages in the artifacts directory.
|
||||
# Pick the newest file matching the current version rather than assuming a clean folder.
|
||||
$packageFile = $null
|
||||
$newestNupkgWrite = [datetime]::MinValue
|
||||
$nupkgCandidates = Get-ChildItem -Path $outputDir -Filter "*.nupkg"
|
||||
foreach ($candidate in $nupkgCandidates) {
|
||||
if (($candidate.Name -like "*$version*.nupkg") -and ($candidate.Name -notlike "*.symbols.nupkg") -and ($candidate.Name -notlike "*.snupkg")) {
|
||||
if ($candidate.LastWriteTime -gt $newestNupkgWrite) {
|
||||
$newestNupkgWrite = $candidate.LastWriteTime
|
||||
$packageFile = $candidate
|
||||
}
|
||||
}
|
||||
}
|
||||
$resolved = Resolve-DotNetPackageArtifacts -ArtifactsDirectory $outputDir -Version $version
|
||||
|
||||
if (-not $packageFile) {
|
||||
throw "Could not locate generated NuGet package for version $version in: $outputDir"
|
||||
}
|
||||
|
||||
Write-Log -Level "OK" -Message " Package ready: $($packageFile.FullName)"
|
||||
$releaseArchiveInputs = @($packageFile.FullName)
|
||||
|
||||
$symbolsPackageFile = $null
|
||||
$newestSnupkgWrite = [datetime]::MinValue
|
||||
$snupkgCandidates = Get-ChildItem -Path $outputDir -Filter "*.snupkg"
|
||||
foreach ($candidate in $snupkgCandidates) {
|
||||
if ($candidate.Name -like "*$version*.snupkg") {
|
||||
if ($candidate.LastWriteTime -gt $newestSnupkgWrite) {
|
||||
$newestSnupkgWrite = $candidate.LastWriteTime
|
||||
$symbolsPackageFile = $candidate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($symbolsPackageFile) {
|
||||
Write-Log -Level "OK" -Message " Symbols package ready: $($symbolsPackageFile.FullName)"
|
||||
$releaseArchiveInputs += $symbolsPackageFile.FullName
|
||||
Write-Log -Level "OK" -Message " Package ready: $($resolved.PackageFile.FullName)"
|
||||
if ($resolved.SymbolsPackageFile) {
|
||||
Write-Log -Level "OK" -Message " Symbols package ready: $($resolved.SymbolsPackageFile.FullName)"
|
||||
}
|
||||
else {
|
||||
Write-Log -Level "WARN" -Message " Symbols package (.snupkg) not found for version $version."
|
||||
}
|
||||
|
||||
$sharedSettings | Add-Member -NotePropertyName packageFile -NotePropertyValue $packageFile -Force
|
||||
$sharedSettings | Add-Member -NotePropertyName symbolsPackageFile -NotePropertyValue $symbolsPackageFile -Force
|
||||
$sharedSettings | Add-Member -NotePropertyName releaseArchiveInputs -NotePropertyValue $releaseArchiveInputs -Force
|
||||
Set-EngineFact -Context $sharedSettings -Namespace 'dotnet' -Name 'packageFile' -Value $resolved.PackageFile -Overwrite Replace -LegacyProperty 'packageFile'
|
||||
Set-EngineFact -Context $sharedSettings -Namespace 'dotnet' -Name 'symbolsPackageFile' -Value $resolved.SymbolsPackageFile -Overwrite Replace -LegacyProperty 'symbolsPackageFile'
|
||||
Set-EngineFact -Context $sharedSettings -Namespace 'release' -Name 'archiveInputs' -Value $resolved.ReleaseArchiveInputs -Overwrite Replace -LegacyProperty 'releaseArchiveInputs'
|
||||
}
|
||||
|
||||
Export-ModuleMember -Function Invoke-Plugin
|
||||
|
||||
@ -27,18 +27,21 @@ function Invoke-Plugin {
|
||||
|
||||
Import-PluginDependency -ModuleName "Logging" -RequiredCommand "Write-Log"
|
||||
Import-PluginDependency -ModuleName "ScriptConfig" -RequiredCommand "Assert-Command"
|
||||
Import-PluginDependency -ModuleName "EngineContext" -RequiredCommand "Set-EngineFact"
|
||||
|
||||
$sharedSettings = $Settings.context
|
||||
$projectFiles = $sharedSettings.projectFiles
|
||||
$projectFiles = Get-EngineFact -Context $sharedSettings -Namespace 'dotnet' -Name 'projectFiles' -LegacyProperty @('projectFiles')
|
||||
$artifactsDirectory = $sharedSettings.artifactsDirectory
|
||||
$publishProjectPath = $null
|
||||
|
||||
Assert-Command dotnet
|
||||
|
||||
if (-not $sharedSettings.PSObject.Properties['projectFiles'] -or $projectFiles.Count -eq 0) {
|
||||
if ($null -eq $projectFiles -or @($projectFiles).Count -eq 0) {
|
||||
throw "DotNetPublish plugin requires project files in the shared context."
|
||||
}
|
||||
|
||||
$projectFiles = @($projectFiles)
|
||||
|
||||
if (!(Test-Path $artifactsDirectory)) {
|
||||
New-Item -ItemType Directory -Path $artifactsDirectory | Out-Null
|
||||
}
|
||||
@ -64,9 +67,9 @@ function Invoke-Plugin {
|
||||
|
||||
Write-Log -Level "OK" -Message " Published artifact ready: $publishDir"
|
||||
|
||||
$sharedSettings | Add-Member -NotePropertyName packageFile -NotePropertyValue $null -Force
|
||||
$sharedSettings | Add-Member -NotePropertyName symbolsPackageFile -NotePropertyValue $null -Force
|
||||
$sharedSettings | Add-Member -NotePropertyName releaseArchiveInputs -NotePropertyValue @($publishDir) -Force
|
||||
Set-EngineFact -Context $sharedSettings -Namespace 'dotnet' -Name 'packageFile' -Value $null -Overwrite Replace -LegacyProperty 'packageFile'
|
||||
Set-EngineFact -Context $sharedSettings -Namespace 'dotnet' -Name 'symbolsPackageFile' -Value $null -Overwrite Replace -LegacyProperty 'symbolsPackageFile'
|
||||
Set-EngineFact -Context $sharedSettings -Namespace 'release' -Name 'archiveInputs' -Value @($publishDir) -Overwrite Replace -LegacyProperty 'releaseArchiveInputs'
|
||||
}
|
||||
|
||||
Export-ModuleMember -Function Invoke-Plugin
|
||||
|
||||
@ -3,11 +3,13 @@
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Loads release version into shared context.
|
||||
Loads release version from an SDK-style .csproj into shared context.
|
||||
|
||||
.DESCRIPTION
|
||||
Dedicated version-loading plugin. It reads .csproj version via
|
||||
EngineContext helpers and writes Version into the shared runtime context.
|
||||
Dedicated version-loading plugin. Reads <Version> from the first configured
|
||||
projectFiles entry and writes it (plus the resolved projectFiles) to the
|
||||
shared runtime context. Declares providesVersion = $true so the engine can
|
||||
discover it as the single release version source.
|
||||
#>
|
||||
|
||||
if (-not (Get-Command Import-PluginDependency -ErrorAction SilentlyContinue)) {
|
||||
@ -18,6 +20,56 @@ if (-not (Get-Command Import-PluginDependency -ErrorAction SilentlyContinue)) {
|
||||
}
|
||||
}
|
||||
|
||||
function Get-CsprojPropertyValueInternal {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[xml]$Csproj,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$PropertyName
|
||||
)
|
||||
|
||||
# SDK-style .csproj files can have multiple PropertyGroup nodes.
|
||||
# Use the first group that defines the requested property.
|
||||
$propNode = $Csproj.Project.PropertyGroup |
|
||||
Where-Object { $_.$PropertyName } |
|
||||
Select-Object -First 1
|
||||
|
||||
if ($propNode) {
|
||||
return $propNode.$PropertyName
|
||||
}
|
||||
|
||||
return $null
|
||||
}
|
||||
|
||||
function Get-CsprojVersionInternal {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$ProjectPath
|
||||
)
|
||||
|
||||
if (-not (Test-Path $ProjectPath -PathType Leaf)) {
|
||||
throw "DotNetReleaseVersion: project file not found at '$ProjectPath'."
|
||||
}
|
||||
|
||||
if ([System.IO.Path]::GetExtension($ProjectPath) -ne ".csproj") {
|
||||
throw "DotNetReleaseVersion: configured project file is not a .csproj file: '$ProjectPath'."
|
||||
}
|
||||
|
||||
[xml]$csproj = Get-Content $ProjectPath
|
||||
$version = Get-CsprojPropertyValueInternal -Csproj $csproj -PropertyName "Version"
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace([string]$version)) {
|
||||
throw "DotNetReleaseVersion: <Version> not found in '$ProjectPath'."
|
||||
}
|
||||
|
||||
return [string]$version
|
||||
}
|
||||
|
||||
function Get-PluginMetadata {
|
||||
return [pscustomobject]@{ providesVersion = $true }
|
||||
}
|
||||
|
||||
function Invoke-Plugin {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
@ -25,17 +77,20 @@ function Invoke-Plugin {
|
||||
)
|
||||
|
||||
Import-PluginDependency -ModuleName "Logging" -RequiredCommand "Write-Log"
|
||||
Import-PluginDependency -ModuleName "EngineContext" -RequiredCommand "Resolve-DotNetReleaseVersion"
|
||||
Import-PluginDependency -ModuleName "EngineContext" -RequiredCommand "Set-EngineState"
|
||||
|
||||
$shared = $Settings.context
|
||||
$resolved = Resolve-DotNetReleaseVersion -Plugins @($Settings) -ScriptDir $shared.scriptDir
|
||||
$projectFiles = @(Resolve-RelativePaths -Value $Settings.projectFiles -BasePath $shared.scriptDir)
|
||||
if ($projectFiles.Count -eq 0) {
|
||||
throw "DotNetReleaseVersion plugin requires 'projectFiles' (first .csproj with <Version>) in scriptSettings.json."
|
||||
}
|
||||
|
||||
$shared | Add-Member -NotePropertyName version -NotePropertyValue $resolved.version -Force
|
||||
$shared | Add-Member -NotePropertyName projectFiles -NotePropertyValue $projectFiles -Force
|
||||
Write-Log -Level "OK" -Message " Release version loaded by DotNetReleaseVersion plugin: $($shared.version)"
|
||||
Write-Log -Level "INFO" -Message "Reading version from SDK-style project file (projectFiles)..."
|
||||
$version = Get-CsprojVersionInternal -ProjectPath $projectFiles[0]
|
||||
|
||||
Set-EngineState -Context $shared -Name 'version' -Value $version
|
||||
Set-EngineFact -Context $shared -Namespace 'dotnet' -Name 'projectFiles' -Value $projectFiles -Overwrite Replace -LegacyProperty 'projectFiles'
|
||||
Write-Log -Level "OK" -Message " Release version loaded by DotNetReleaseVersion plugin: $version"
|
||||
}
|
||||
|
||||
Export-ModuleMember -Function Invoke-Plugin
|
||||
|
||||
|
||||
Export-ModuleMember -Function Invoke-Plugin, Get-PluginMetadata
|
||||
|
||||
@ -58,20 +58,12 @@ function Invoke-Plugin {
|
||||
throw "Tests failed. $($testResult.Error)"
|
||||
}
|
||||
|
||||
$sharedSettings | Add-Member -NotePropertyName npmWorkspaceRoot -NotePropertyValue $workspaceRoot -Force
|
||||
$sharedSettings | Add-Member -NotePropertyName testResult -NotePropertyValue $testResult -Force
|
||||
$sharedSettings | Add-Member -NotePropertyName qualityLineCoverage -NotePropertyValue $testResult.LineRate -Force
|
||||
$sharedSettings | Add-Member -NotePropertyName coverageLineRate -NotePropertyValue $testResult.LineRate -Force
|
||||
$sharedSettings | Add-Member -NotePropertyName coverageBranchRate -NotePropertyValue $testResult.BranchRate -Force
|
||||
$sharedSettings | Add-Member -NotePropertyName coverageMethodRate -NotePropertyValue $testResult.MethodRate -Force
|
||||
$sharedSettings | Add-Member -NotePropertyName coverageTotalMethods -NotePropertyValue $testResult.TotalMethods -Force
|
||||
$sharedSettings | Add-Member -NotePropertyName coverageCoveredMethods -NotePropertyValue $testResult.CoveredMethods -Force
|
||||
|
||||
if (($testResult.PSObject.Properties.Name -contains 'ResultsDirectory') -and $testResult.ResultsDirectory) {
|
||||
$sharedSettings | Add-Member -NotePropertyName testResultsDirectory -NotePropertyValue $testResult.ResultsDirectory -Force
|
||||
}
|
||||
Import-PluginDependency -ModuleName "EngineContext" -RequiredCommand "Set-EngineFact"
|
||||
Import-PluginDependency -ModuleName "TestRunner" -RequiredCommand "Publish-CoverageMetricsToSharedContext"
|
||||
Set-EngineFact -Context $sharedSettings -Namespace 'npm' -Name 'workspaceRoot' -Value $workspaceRoot -Overwrite Replace -LegacyProperty 'npmWorkspaceRoot'
|
||||
Publish-CoverageMetricsToSharedContext -SharedSettings $sharedSettings -TestResult $testResult
|
||||
if (($testResult.PSObject.Properties.Name -contains 'CoverageSummaryFile') -and $testResult.CoverageSummaryFile) {
|
||||
$sharedSettings | Add-Member -NotePropertyName coverageSummaryFile -NotePropertyValue $testResult.CoverageSummaryFile -Force
|
||||
Set-EngineFact -Context $sharedSettings -Namespace 'test' -Name 'coverageSummaryFile' -Value $testResult.CoverageSummaryFile -Overwrite Replace -LegacyProperty 'coverageSummaryFile'
|
||||
}
|
||||
|
||||
Write-Log -Level "OK" -Message " All tests passed!"
|
||||
|
||||
@ -27,7 +27,7 @@ function Invoke-Plugin {
|
||||
|
||||
Import-PluginDependency -ModuleName "Logging" -RequiredCommand "Write-Log"
|
||||
Import-PluginDependency -ModuleName "ScriptConfig" -RequiredCommand "Assert-Command"
|
||||
Import-PluginDependency -ModuleName "EngineContext" -RequiredCommand "Resolve-RelativePaths"
|
||||
Import-PluginDependency -ModuleName "EngineContext" -RequiredCommand "Set-EngineFact"
|
||||
|
||||
$pluginSettings = $Settings
|
||||
$shared = $Settings.context
|
||||
@ -49,6 +49,7 @@ function Invoke-Plugin {
|
||||
$artifactsDirectory = $null
|
||||
if ($pluginSettings.PSObject.Properties['artifactsDir'] -and -not [string]::IsNullOrWhiteSpace([string]$pluginSettings.artifactsDir)) {
|
||||
$artifactsDirectory = [System.IO.Path]::GetFullPath((Join-Path $shared.scriptDir ([string]$pluginSettings.artifactsDir)))
|
||||
Set-EngineState -Context $shared -Name 'artifactsDirectory' -Value $artifactsDirectory
|
||||
}
|
||||
elseif ($shared.PSObject.Properties['artifactsDirectory'] -and -not [string]::IsNullOrWhiteSpace([string]$shared.artifactsDirectory)) {
|
||||
$artifactsDirectory = [string]$shared.artifactsDirectory
|
||||
@ -125,10 +126,11 @@ function Invoke-Plugin {
|
||||
Pop-Location
|
||||
}
|
||||
|
||||
$shared | Add-Member -NotePropertyName releaseDir -NotePropertyValue $artifactsDirectory -Force
|
||||
$shared | Add-Member -NotePropertyName releaseAssetPaths -NotePropertyValue $releaseAssetPaths -Force
|
||||
Set-EngineState -Context $shared -Name 'releaseDir' -Value $artifactsDirectory
|
||||
Set-EngineFact -Context $shared -Namespace 'release' -Name 'assetPaths' -Value $releaseAssetPaths -Overwrite Replace -LegacyProperty 'releaseAssetPaths'
|
||||
if ($releaseAssetPaths.Count -gt 0) {
|
||||
$shared | Add-Member -NotePropertyName packageFile -NotePropertyValue (Get-Item -LiteralPath $releaseAssetPaths[0]) -Force
|
||||
$packageItem = Get-Item -LiteralPath $releaseAssetPaths[0]
|
||||
Set-EngineFact -Context $shared -Namespace 'npm' -Name 'packageFile' -Value $packageItem -Overwrite Replace -LegacyProperty 'packageFile'
|
||||
}
|
||||
|
||||
Write-Log -Level "OK" -Message " npm pack completed ($($releaseAssetPaths.Count) tarball(s))."
|
||||
|
||||
@ -127,7 +127,8 @@ registry=$registry
|
||||
}
|
||||
|
||||
Write-Log -Level "OK" -Message " npm publish completed."
|
||||
$shared | Add-Member -NotePropertyName publishCompleted -NotePropertyValue $true -Force
|
||||
Import-PluginDependency -ModuleName "EngineContext" -RequiredCommand "Add-EnginePublishCompletion"
|
||||
Add-EnginePublishCompletion -Context $shared -Publisher 'NpmPublish'
|
||||
}
|
||||
finally {
|
||||
if (Test-Path $tempNpmRcPath -PathType Leaf) {
|
||||
|
||||
@ -57,6 +57,10 @@ function Set-PackageJsonVersionInternal {
|
||||
($json | ConvertTo-Json -Depth 100) + [Environment]::NewLine | Set-Content -Path $PackageJsonPath -Encoding UTF8 -NoNewline
|
||||
}
|
||||
|
||||
function Get-PluginMetadata {
|
||||
return [pscustomobject]@{ providesVersion = $true }
|
||||
}
|
||||
|
||||
function Invoke-Plugin {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
@ -64,7 +68,7 @@ function Invoke-Plugin {
|
||||
)
|
||||
|
||||
Import-PluginDependency -ModuleName "Logging" -RequiredCommand "Write-Log"
|
||||
Import-PluginDependency -ModuleName "EngineContext" -RequiredCommand "Resolve-RelativePaths"
|
||||
Import-PluginDependency -ModuleName "EngineContext" -RequiredCommand "Set-EngineState"
|
||||
|
||||
$pluginSettings = $Settings
|
||||
$shared = $Settings.context
|
||||
@ -90,10 +94,11 @@ function Invoke-Plugin {
|
||||
Write-Log -Level "OK" -Message " Synchronized workspace package versions to $version."
|
||||
}
|
||||
|
||||
$shared | Add-Member -NotePropertyName version -NotePropertyValue $version -Force
|
||||
$shared | Add-Member -NotePropertyName npmWorkspaceRoot -NotePropertyValue (Split-Path -Parent $packageJsonPath) -Force
|
||||
$shared | Add-Member -NotePropertyName npmPackageJsonPath -NotePropertyValue $packageJsonPath -Force
|
||||
$npmWorkspaceRoot = Split-Path -Parent $packageJsonPath
|
||||
Set-EngineState -Context $shared -Name 'version' -Value $version
|
||||
Set-EngineFact -Context $shared -Namespace 'npm' -Name 'workspaceRoot' -Value $npmWorkspaceRoot -Overwrite Replace -LegacyProperty 'npmWorkspaceRoot'
|
||||
Set-EngineFact -Context $shared -Namespace 'npm' -Name 'packageJsonPath' -Value $packageJsonPath -Overwrite Replace -LegacyProperty 'npmPackageJsonPath'
|
||||
Write-Log -Level "OK" -Message " Release version loaded by NpmReleaseVersion plugin: $version"
|
||||
}
|
||||
|
||||
Export-ModuleMember -Function Invoke-Plugin
|
||||
Export-ModuleMember -Function Invoke-Plugin, Get-PluginMetadata
|
||||
|
||||
@ -6,8 +6,10 @@
|
||||
Loads release version from a repo-root VERSION file into shared context.
|
||||
|
||||
.DESCRIPTION
|
||||
Reads a single-line semver from the configured versionFilePath (default repo-root VERSION).
|
||||
Used by container-only repos without .csproj or package.json.
|
||||
Reads a single-line semver from the configured versionFilePath (default
|
||||
repo-root VERSION). Useful for repositories without .csproj or package.json
|
||||
version metadata. Declares providesVersion = $true so the engine can
|
||||
discover it as the single release version source.
|
||||
#>
|
||||
|
||||
if (-not (Get-Command Import-PluginDependency -ErrorAction SilentlyContinue)) {
|
||||
@ -18,6 +20,33 @@ if (-not (Get-Command Import-PluginDependency -ErrorAction SilentlyContinue)) {
|
||||
}
|
||||
}
|
||||
|
||||
function Get-VersionFileSemverInternal {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$VersionFilePath
|
||||
)
|
||||
|
||||
if (-not (Test-Path $VersionFilePath -PathType Leaf)) {
|
||||
throw "FileReleaseVersion: VERSION file not found at '$VersionFilePath'."
|
||||
}
|
||||
|
||||
$version = (Get-Content -Path $VersionFilePath -Raw -Encoding UTF8).Trim()
|
||||
if ([string]::IsNullOrWhiteSpace($version)) {
|
||||
throw "FileReleaseVersion: VERSION file is empty at '$VersionFilePath'."
|
||||
}
|
||||
|
||||
$version = $version -replace '^[vV]', ''
|
||||
if ($version -notmatch '^\d+\.\d+\.\d+') {
|
||||
throw "FileReleaseVersion: version '$version' in '$VersionFilePath' is not a valid semver."
|
||||
}
|
||||
|
||||
return $version
|
||||
}
|
||||
|
||||
function Get-PluginMetadata {
|
||||
return [pscustomobject]@{ providesVersion = $true }
|
||||
}
|
||||
|
||||
function Invoke-Plugin {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
@ -25,17 +54,28 @@ function Invoke-Plugin {
|
||||
)
|
||||
|
||||
Import-PluginDependency -ModuleName "Logging" -RequiredCommand "Write-Log"
|
||||
Import-PluginDependency -ModuleName "EngineContext" -RequiredCommand "Resolve-FileReleaseVersion"
|
||||
Import-PluginDependency -ModuleName "EngineContext" -RequiredCommand "Set-EngineState"
|
||||
|
||||
$shared = $Settings.context
|
||||
$resolved = Resolve-FileReleaseVersion -Plugins @($Settings) -ScriptDir $shared.scriptDir
|
||||
$versionFilePaths = @(Resolve-RelativePaths -Value $Settings.versionFilePath -BasePath $shared.scriptDir)
|
||||
|
||||
$shared | Add-Member -NotePropertyName version -NotePropertyValue $resolved.version -Force
|
||||
if ($versionFilePaths.Count -gt 0) {
|
||||
$shared | Add-Member -NotePropertyName versionFilePath -NotePropertyValue $versionFilePaths[0] -Force
|
||||
$versionFileSetting = if ($Settings.versionFilePath) {
|
||||
$Settings.versionFilePath
|
||||
}
|
||||
Write-Log -Level "OK" -Message " Release version loaded by FileReleaseVersion plugin: $($shared.version)"
|
||||
else {
|
||||
'..\..\..\VERSION'
|
||||
}
|
||||
|
||||
$versionFilePaths = @(Resolve-RelativePaths -Value $versionFileSetting -BasePath $shared.scriptDir)
|
||||
if ($versionFilePaths.Count -eq 0) {
|
||||
throw "FileReleaseVersion plugin requires 'versionFilePath' (repo-root VERSION file) in scriptSettings.json."
|
||||
}
|
||||
|
||||
$versionFilePath = $versionFilePaths[0]
|
||||
Write-Log -Level "INFO" -Message "Reading version from VERSION file (versionFilePath)..."
|
||||
$version = Get-VersionFileSemverInternal -VersionFilePath $versionFilePath
|
||||
|
||||
Set-EngineState -Context $shared -Name 'version' -Value $version
|
||||
Set-EngineFact -Context $shared -Namespace 'release' -Name 'versionFilePath' -Value $versionFilePath -Overwrite Replace -LegacyProperty 'versionFilePath'
|
||||
Write-Log -Level "OK" -Message " Release version loaded by FileReleaseVersion plugin: $version"
|
||||
}
|
||||
|
||||
Export-ModuleMember -Function Invoke-Plugin
|
||||
Export-ModuleMember -Function Invoke-Plugin, Get-PluginMetadata
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
Pester test plugin for the RepoUtils test engine.
|
||||
|
||||
.DESCRIPTION
|
||||
Runs the community Pester suite and publishes normalized coverage metrics on the
|
||||
Runs the RepoUtils Pester suite and publishes normalized coverage metrics on the
|
||||
shared engine context for QualityGate.
|
||||
#>
|
||||
|
||||
|
||||
@ -10,9 +10,9 @@
|
||||
shared engine context (same object passed to every plugin as .context).
|
||||
|
||||
Line coverage for threshold checks is resolved in order (first present wins):
|
||||
- qualityLineCoverage (generic; any plugin may set this)
|
||||
- coverageLineRate (conventional flat metric)
|
||||
- testResult.LineRate (object from a test plugin; property name is conventional)
|
||||
- facts test.coverageLineRate (via Get-EngineFact)
|
||||
- legacy qualityLineCoverage / coverageLineRate flat properties
|
||||
- testResult.LineRate (object from a test plugin)
|
||||
|
||||
Configure coverageThreshold > 0 to require one of those inputs. With coverageThreshold 0
|
||||
and scanVulnerabilities false, the plugin is a no-op.
|
||||
@ -64,6 +64,20 @@ function Get-LineCoveragePercentFromSharedContext {
|
||||
$Shared
|
||||
)
|
||||
|
||||
if (Get-Command Get-EngineFact -ErrorAction SilentlyContinue) {
|
||||
$fromFact = Get-EngineFact -Context $Shared -Namespace 'test' -Name 'coverageLineRate' -LegacyProperty @('qualityLineCoverage', 'coverageLineRate')
|
||||
if ($null -ne $fromFact -and -not [string]::IsNullOrWhiteSpace([string]$fromFact)) {
|
||||
return [double]$fromFact
|
||||
}
|
||||
|
||||
$testResult = Get-EngineFact -Context $Shared -Namespace 'test' -Name 'testResult' -LegacyProperty @('testResult')
|
||||
if ($null -ne $testResult -and ($testResult.PSObject.Properties.Name -contains 'LineRate')) {
|
||||
return [double]$testResult.LineRate
|
||||
}
|
||||
|
||||
return $null
|
||||
}
|
||||
|
||||
foreach ($prop in @('qualityLineCoverage', 'coverageLineRate')) {
|
||||
if ($Shared.PSObject.Properties.Name -contains $prop) {
|
||||
$raw = $Shared.$prop
|
||||
@ -92,7 +106,7 @@ function Invoke-Plugin {
|
||||
|
||||
Import-PluginDependency -ModuleName "Logging" -RequiredCommand "Write-Log"
|
||||
Import-PluginDependency -ModuleName "ScriptConfig" -RequiredCommand "Assert-Command"
|
||||
Import-PluginDependency -ModuleName "EngineContext" -RequiredCommand "Resolve-RelativePaths"
|
||||
Import-PluginDependency -ModuleName "EngineContext" -RequiredCommand "Get-EngineFact"
|
||||
|
||||
$pluginSettings = $Settings
|
||||
$sharedSettings = $Settings.context
|
||||
@ -107,11 +121,9 @@ function Invoke-Plugin {
|
||||
if ($pluginSettings.PSObject.Properties['projectFiles'] -and $null -ne $pluginSettings.projectFiles) {
|
||||
$projectFiles = @(Resolve-RelativePaths -Value $pluginSettings.projectFiles -BasePath $scriptDir)
|
||||
}
|
||||
elseif ($sharedSettings.PSObject.Properties['projectFiles'] -and $null -ne $sharedSettings.projectFiles) {
|
||||
$projectFiles = @($sharedSettings.projectFiles)
|
||||
}
|
||||
else {
|
||||
$projectFiles = @()
|
||||
$fromContext = Get-EngineFact -Context $sharedSettings -Namespace 'dotnet' -Name 'projectFiles' -LegacyProperty @('projectFiles')
|
||||
$projectFiles = if ($null -ne $fromContext) { @($fromContext) } else { @() }
|
||||
}
|
||||
|
||||
$coverageThreshold = 0
|
||||
@ -129,7 +141,7 @@ function Invoke-Plugin {
|
||||
if ($needCoverageCheck) {
|
||||
$lineRate = Get-LineCoveragePercentFromSharedContext -Shared $sharedSettings
|
||||
if ($null -eq $lineRate) {
|
||||
throw "coverageThreshold is $coverageThreshold but shared context has no line coverage. Set one of: qualityLineCoverage, coverageLineRate, or testResult.LineRate (from an earlier plugin)."
|
||||
throw "coverageThreshold is $coverageThreshold but shared context has no line coverage. Set test.coverageLineRate (or legacy qualityLineCoverage / coverageLineRate / testResult.LineRate) from an earlier plugin."
|
||||
}
|
||||
|
||||
Write-Log -Level "STEP" -Message "Checking line coverage threshold against shared context..."
|
||||
|
||||
@ -50,10 +50,15 @@ function Invoke-NotMetInternal {
|
||||
[string]$Reason
|
||||
)
|
||||
|
||||
$Shared | Add-Member -NotePropertyName skipPublishPlugins -NotePropertyValue $true -Force
|
||||
if (Get-Command Set-EngineState -ErrorAction SilentlyContinue) {
|
||||
Set-EngineState -Context $Shared -Name 'skipPublishPlugins' -Value $true
|
||||
}
|
||||
else {
|
||||
$Shared | Add-Member -NotePropertyName skipPublishPlugins -NotePropertyValue $true -Force
|
||||
}
|
||||
|
||||
if ($When -eq 'fail') {
|
||||
Write-Log -Level "ERROR" -Message "ReleasePublishGuard: $Reason"
|
||||
exit 1
|
||||
throw "ReleasePublishGuard: $Reason"
|
||||
}
|
||||
|
||||
Write-Log -Level "WARN" -Message " Publish suppressed: $Reason"
|
||||
@ -67,6 +72,7 @@ function Invoke-Plugin {
|
||||
|
||||
Import-PluginDependency -ModuleName "Logging" -RequiredCommand "Write-Log"
|
||||
Import-PluginDependency -ModuleName "PluginSupport" -RequiredCommand "Get-PluginBranches"
|
||||
Import-PluginDependency -ModuleName "EngineContext" -RequiredCommand "Set-EngineState"
|
||||
|
||||
Import-PluginDependency -ModuleName "GitTools" -RequiredCommand "Get-GitStatusShort"
|
||||
Import-PluginDependency -ModuleName "GitTools" -RequiredCommand "Test-RemoteTagExists"
|
||||
@ -82,15 +88,10 @@ function Invoke-Plugin {
|
||||
throw "ReleasePublishGuard: whenRequirementsNotMet must be 'skip' or 'fail'."
|
||||
}
|
||||
|
||||
$shared | Add-Member -NotePropertyName skipPublishPlugins -NotePropertyValue $false -Force
|
||||
Set-EngineState -Context $shared -Name 'skipPublishPlugins' -Value $false
|
||||
|
||||
Write-Log -Level "STEP" -Message "Release publish guard..."
|
||||
|
||||
if ($shared.PSObject.Properties.Name -contains 'dryRun' -and [bool]$shared.dryRun) {
|
||||
Write-Log -Level "INFO" -Message " Dry run: publish guard relaxed; publish plugins will validate only."
|
||||
return
|
||||
}
|
||||
|
||||
$allowed = @(Get-PluginBranches -Plugin $pluginSettings)
|
||||
if ($allowed.Count -gt 0 -and $allowed -notcontains '*' -and $allowed -notcontains $shared.currentBranch) {
|
||||
Invoke-NotMetInternal -Shared $shared -When $when -Reason "branch '$($shared.currentBranch)' is not in the guard branches list."
|
||||
@ -143,7 +144,7 @@ function Invoke-Plugin {
|
||||
return
|
||||
}
|
||||
|
||||
$shared | Add-Member -NotePropertyName tag -NotePropertyValue $tag -Force
|
||||
Set-EngineState -Context $shared -Name 'tag' -Value $tag
|
||||
}
|
||||
|
||||
$ensureRemote = $true
|
||||
|
||||
@ -1,363 +0,0 @@
|
||||
#requires -Version 7.0
|
||||
#requires -PSEdition Core
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Refreshes a local maksit-repoutils copy from GitHub.
|
||||
|
||||
.DESCRIPTION
|
||||
This script clones the configured repository into a temporary directory,
|
||||
refreshes the parent directory of this script, preserves existing
|
||||
scriptSettings.json files in subfolders, and copies the cloned source
|
||||
contents into that parent directory.
|
||||
|
||||
All configuration is stored in scriptSettings.json.
|
||||
|
||||
.EXAMPLE
|
||||
pwsh -File .\Update-RepoUtils.ps1
|
||||
|
||||
.NOTES
|
||||
CONFIGURATION (scriptSettings.json):
|
||||
- dryRun: If true, logs the planned update without modifying files
|
||||
- repository.url: Git repository to clone
|
||||
- repository.sourceSubdirectory: Folder copied into the target directory
|
||||
- repository.preserveFileName: Existing file name to preserve in subfolders
|
||||
- repository.cloneDepth: Depth used for git clone
|
||||
- repository.skippedRelativeDirectories: Relative directories to exclude from phase-two refresh
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[switch]$ContinueAfterSelfUpdate,
|
||||
[string]$TargetDirectoryOverride,
|
||||
[string]$ClonedSourceDirectoryOverride,
|
||||
[string]$TemporaryRootOverride
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
$srcDir = Split-Path (Split-Path $scriptDir -Parent) -Parent
|
||||
$modulesDir = Join-Path $srcDir 'modules'
|
||||
|
||||
# Refresh the src directory that contains modules, engines, plugins, and tools.
|
||||
$targetDirectory = if ([string]::IsNullOrWhiteSpace($TargetDirectoryOverride)) {
|
||||
$srcDir
|
||||
}
|
||||
else {
|
||||
[System.IO.Path]::GetFullPath($TargetDirectoryOverride)
|
||||
}
|
||||
$currentScriptPath = [System.IO.Path]::GetFullPath($MyInvocation.MyCommand.Path)
|
||||
$selfUpdateDirectory = [System.IO.Path]::Combine('tools', 'Update-RepoUtils')
|
||||
|
||||
function ConvertTo-NormalizedRelativePath {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Path
|
||||
)
|
||||
|
||||
$normalizedPath = $Path.Replace('/', [System.IO.Path]::DirectorySeparatorChar).Replace('\', [System.IO.Path]::DirectorySeparatorChar)
|
||||
return $normalizedPath.TrimStart('.', [System.IO.Path]::DirectorySeparatorChar).TrimEnd([System.IO.Path]::DirectorySeparatorChar)
|
||||
}
|
||||
|
||||
function Test-IsInRelativeDirectory {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$RelativePath,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string[]]$Directories
|
||||
)
|
||||
|
||||
$normalizedRelativePath = ConvertTo-NormalizedRelativePath -Path $RelativePath
|
||||
foreach ($directory in $Directories) {
|
||||
$normalizedDirectory = ConvertTo-NormalizedRelativePath -Path $directory
|
||||
if ([string]::IsNullOrWhiteSpace($normalizedDirectory)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (
|
||||
$normalizedRelativePath.Equals($normalizedDirectory, [System.StringComparison]::OrdinalIgnoreCase) -or
|
||||
$normalizedRelativePath.StartsWith($normalizedDirectory + [System.IO.Path]::DirectorySeparatorChar, [System.StringComparison]::OrdinalIgnoreCase)
|
||||
) {
|
||||
return $true
|
||||
}
|
||||
}
|
||||
|
||||
return $false
|
||||
}
|
||||
|
||||
#region Import Modules
|
||||
|
||||
$scriptConfigModulePath = Join-Path $modulesDir "ScriptConfig.psm1"
|
||||
if (-not (Test-Path $scriptConfigModulePath)) {
|
||||
Write-Error "ScriptConfig module not found at: $scriptConfigModulePath"
|
||||
exit 1
|
||||
}
|
||||
|
||||
$loggingModulePath = Join-Path $modulesDir "Logging.psm1"
|
||||
if (-not (Test-Path $loggingModulePath)) {
|
||||
Write-Error "Logging module not found at: $loggingModulePath"
|
||||
exit 1
|
||||
}
|
||||
|
||||
Import-Module $scriptConfigModulePath -Force
|
||||
Import-Module $loggingModulePath -Force
|
||||
|
||||
#endregion
|
||||
|
||||
#region Load Settings
|
||||
|
||||
$settings = Get-ScriptSettings -ScriptDir $scriptDir
|
||||
|
||||
#endregion
|
||||
|
||||
#region Configuration
|
||||
|
||||
$repositoryUrl = $settings.repository.url
|
||||
$dryRun = if ($null -ne $settings.dryRun) { [bool]$settings.dryRun } else { $false }
|
||||
$sourceSubdirectory = if ($settings.repository.sourceSubdirectory) { $settings.repository.sourceSubdirectory } else { 'src' }
|
||||
$preserveFileName = if ($settings.repository.preserveFileName) { $settings.repository.preserveFileName } else { 'scriptSettings.json' }
|
||||
$cloneDepth = if ($settings.repository.cloneDepth) { [int]$settings.repository.cloneDepth } else { 1 }
|
||||
[string[]]$skippedRelativeDirectories = if ($settings.repository.skippedRelativeDirectories) {
|
||||
@(
|
||||
$settings.repository.skippedRelativeDirectories |
|
||||
ForEach-Object {
|
||||
ConvertTo-NormalizedRelativePath -Path ([string]$_)
|
||||
}
|
||||
)
|
||||
}
|
||||
else {
|
||||
@(
|
||||
[System.IO.Path]::Combine('engines', 'release', 'custom'),
|
||||
[System.IO.Path]::Combine('engines', 'test', 'custom')
|
||||
)
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Validate CLI Dependencies
|
||||
|
||||
Assert-Command git
|
||||
Assert-Command pwsh
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($repositoryUrl)) {
|
||||
Write-Error "repository.url is required in scriptSettings.json."
|
||||
exit 1
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Main
|
||||
|
||||
Write-Log -Level "INFO" -Message "========================================"
|
||||
Write-Log -Level "INFO" -Message "Update RepoUtils Script"
|
||||
Write-Log -Level "INFO" -Message "========================================"
|
||||
Write-Log -Level "INFO" -Message "Target directory: $targetDirectory"
|
||||
Write-Log -Level "INFO" -Message "Dry run: $dryRun"
|
||||
|
||||
$ownsTemporaryRoot = [string]::IsNullOrWhiteSpace($TemporaryRootOverride)
|
||||
$temporaryRoot = if ($ownsTemporaryRoot) {
|
||||
Join-Path ([System.IO.Path]::GetTempPath()) ("maksit-repoutils-update-" + [System.Guid]::NewGuid().ToString('N'))
|
||||
}
|
||||
else {
|
||||
[System.IO.Path]::GetFullPath($TemporaryRootOverride)
|
||||
}
|
||||
|
||||
try {
|
||||
$clonedSourceDirectory = if ([string]::IsNullOrWhiteSpace($ClonedSourceDirectoryOverride)) {
|
||||
Write-LogStep "Cloning latest repository snapshot..."
|
||||
& git clone --depth $cloneDepth $repositoryUrl $temporaryRoot
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "git clone failed with exit code $LASTEXITCODE."
|
||||
}
|
||||
Write-Log -Level "OK" -Message "Repository cloned"
|
||||
|
||||
Join-Path $temporaryRoot $sourceSubdirectory
|
||||
}
|
||||
else {
|
||||
[System.IO.Path]::GetFullPath($ClonedSourceDirectoryOverride)
|
||||
}
|
||||
|
||||
if (-not (Test-Path -Path $clonedSourceDirectory -PathType Container)) {
|
||||
throw "The cloned repository does not contain the expected source directory: $clonedSourceDirectory"
|
||||
}
|
||||
|
||||
if (-not $ContinueAfterSelfUpdate) {
|
||||
if ($dryRun) {
|
||||
Write-LogStep "Dry run self-update summary"
|
||||
Write-Log -Level "INFO" -Message "Would refresh shared modules and $selfUpdateDirectory before relaunching the updater"
|
||||
}
|
||||
else {
|
||||
Write-LogStep "Refreshing updater files..."
|
||||
$selfUpdateFiles = Get-ChildItem -Path $clonedSourceDirectory -Recurse -Force -File |
|
||||
Where-Object {
|
||||
$relativePath = [System.IO.Path]::GetRelativePath($clonedSourceDirectory, $_.FullName)
|
||||
$isRootFile = -not $relativePath.Contains([System.IO.Path]::DirectorySeparatorChar)
|
||||
$isUpdaterFile = $relativePath.StartsWith($selfUpdateDirectory + [System.IO.Path]::DirectorySeparatorChar, [System.StringComparison]::OrdinalIgnoreCase)
|
||||
|
||||
$_.Name -ne $preserveFileName -and
|
||||
($isRootFile -or $isUpdaterFile)
|
||||
}
|
||||
|
||||
foreach ($sourceFile in $selfUpdateFiles) {
|
||||
$relativePath = [System.IO.Path]::GetRelativePath($clonedSourceDirectory, $sourceFile.FullName)
|
||||
$destinationPath = Join-Path $targetDirectory $relativePath
|
||||
$destinationDirectory = Split-Path -Parent $destinationPath
|
||||
if (-not (Test-Path -Path $destinationDirectory -PathType Container)) {
|
||||
New-Item -ItemType Directory -Path $destinationDirectory -Force | Out-Null
|
||||
}
|
||||
|
||||
Copy-Item -Path $sourceFile.FullName -Destination $destinationPath -Force
|
||||
}
|
||||
|
||||
Write-Log -Level "OK" -Message "Updater files refreshed"
|
||||
}
|
||||
|
||||
if ($dryRun) {
|
||||
Write-LogStep "Dry run bootstrap completed"
|
||||
Write-Log -Level "INFO" -Message "Continuing with phase two in the current process because no files were changed"
|
||||
}
|
||||
else {
|
||||
Write-LogStep "Relaunching the updated updater..."
|
||||
& pwsh -File $currentScriptPath `
|
||||
-ContinueAfterSelfUpdate `
|
||||
-TargetDirectoryOverride $targetDirectory `
|
||||
-ClonedSourceDirectoryOverride $clonedSourceDirectory `
|
||||
-TemporaryRootOverride $temporaryRoot
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Relaunched updater failed with exit code $LASTEXITCODE."
|
||||
}
|
||||
|
||||
Write-Log -Level "OK" -Message "Bootstrap phase completed"
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
$preservedFiles = @()
|
||||
[string[]]$updatePhaseSkippedDirectories = @($skippedRelativeDirectories) + $selfUpdateDirectory
|
||||
$existingPreservedFiles = Get-ChildItem -Path $targetDirectory -Recurse -File -Filter $preserveFileName -ErrorAction SilentlyContinue
|
||||
if ($existingPreservedFiles) {
|
||||
foreach ($file in $existingPreservedFiles) {
|
||||
$relativePath = [System.IO.Path]::GetRelativePath($targetDirectory, $file.FullName)
|
||||
$backupPath = Join-Path $temporaryRoot ("preserved-" + ($relativePath -replace '[\\/:*?""<>|]', '_'))
|
||||
$preservedFiles += [pscustomobject]@{
|
||||
RelativePath = $relativePath
|
||||
BackupPath = $backupPath
|
||||
}
|
||||
|
||||
if (-not $dryRun) {
|
||||
$backupDirectory = Split-Path -Parent $backupPath
|
||||
if (-not (Test-Path -Path $backupDirectory -PathType Container)) {
|
||||
New-Item -ItemType Directory -Path $backupDirectory -Force | Out-Null
|
||||
}
|
||||
|
||||
Copy-Item -Path $file.FullName -Destination $backupPath -Force
|
||||
}
|
||||
}
|
||||
Write-Log -Level "OK" -Message "Preserved $($preservedFiles.Count) existing $preserveFileName file(s)"
|
||||
}
|
||||
else {
|
||||
Write-Log -Level "WARN" -Message "No existing $preserveFileName files found in subfolders"
|
||||
}
|
||||
|
||||
if ($dryRun) {
|
||||
Write-LogStep "Dry run summary"
|
||||
Write-Log -Level "INFO" -Message "Would remove all files under target except preserved $preserveFileName files"
|
||||
Write-Log -Level "INFO" -Message "Would skip phase-two refresh for: $($updatePhaseSkippedDirectories -join ', ')"
|
||||
Write-Log -Level "INFO" -Message "Would copy refreshed files from: $clonedSourceDirectory"
|
||||
if ($preservedFiles.Count -gt 0) {
|
||||
$preservedList = ($preservedFiles | ForEach-Object { $_.RelativePath }) -join ", "
|
||||
Write-Log -Level "INFO" -Message "Would restore preserved files: $preservedList"
|
||||
}
|
||||
Write-Log -Level "OK" -Message "Dry run completed. No files were modified."
|
||||
return
|
||||
}
|
||||
|
||||
Write-LogStep "Cleaning target directory..."
|
||||
$filesToRemove = Get-ChildItem -Path $targetDirectory -Recurse -Force -File |
|
||||
Where-Object {
|
||||
$relativePath = [System.IO.Path]::GetRelativePath($targetDirectory, $_.FullName)
|
||||
$isInSkippedDirectory = Test-IsInRelativeDirectory -RelativePath $relativePath -Directories $updatePhaseSkippedDirectories
|
||||
|
||||
$_.Name -ne $preserveFileName -and
|
||||
-not $isInSkippedDirectory
|
||||
}
|
||||
|
||||
foreach ($file in $filesToRemove) {
|
||||
Remove-Item -Path $file.FullName -Force
|
||||
}
|
||||
|
||||
$directoriesToRemove = Get-ChildItem -Path $targetDirectory -Recurse -Force -Directory |
|
||||
Sort-Object { $_.FullName.Length } -Descending
|
||||
|
||||
foreach ($directory in $directoriesToRemove) {
|
||||
$relativePath = [System.IO.Path]::GetRelativePath($targetDirectory, $directory.FullName)
|
||||
if (Test-IsInRelativeDirectory -RelativePath $relativePath -Directories $updatePhaseSkippedDirectories) {
|
||||
continue
|
||||
}
|
||||
|
||||
$remainingItems = Get-ChildItem -Path $directory.FullName -Force -ErrorAction SilentlyContinue
|
||||
if (-not $remainingItems) {
|
||||
Remove-Item -Path $directory.FullName -Force
|
||||
}
|
||||
}
|
||||
Write-Log -Level "OK" -Message "Target directory cleaned"
|
||||
|
||||
Write-LogStep "Copying refreshed source files..."
|
||||
$sourceFilesToCopy = Get-ChildItem -Path $clonedSourceDirectory -Recurse -Force -File |
|
||||
Where-Object {
|
||||
$relativePath = [System.IO.Path]::GetRelativePath($clonedSourceDirectory, $_.FullName)
|
||||
$isInSkippedDirectory = Test-IsInRelativeDirectory -RelativePath $relativePath -Directories $updatePhaseSkippedDirectories
|
||||
|
||||
-not $isInSkippedDirectory
|
||||
}
|
||||
|
||||
foreach ($sourceFile in $sourceFilesToCopy) {
|
||||
$relativePath = [System.IO.Path]::GetRelativePath($clonedSourceDirectory, $sourceFile.FullName)
|
||||
$destinationPath = Join-Path $targetDirectory $relativePath
|
||||
$destinationDirectory = Split-Path -Parent $destinationPath
|
||||
if (-not (Test-Path -Path $destinationDirectory -PathType Container)) {
|
||||
New-Item -ItemType Directory -Path $destinationDirectory -Force | Out-Null
|
||||
}
|
||||
|
||||
Copy-Item -Path $sourceFile.FullName -Destination $destinationPath -Force
|
||||
}
|
||||
|
||||
foreach ($skippedDirectory in $updatePhaseSkippedDirectories) {
|
||||
$skippedSourcePath = Join-Path $clonedSourceDirectory $skippedDirectory
|
||||
if (Test-Path -Path $skippedSourcePath) {
|
||||
Write-Log -Level "INFO" -Message "Skipped refresh for $skippedDirectory"
|
||||
}
|
||||
}
|
||||
Write-Log -Level "OK" -Message "Source files copied"
|
||||
|
||||
if ($preservedFiles.Count -gt 0) {
|
||||
foreach ($preservedFile in $preservedFiles) {
|
||||
if (-not (Test-Path -Path $preservedFile.BackupPath -PathType Leaf)) {
|
||||
continue
|
||||
}
|
||||
|
||||
$restorePath = Join-Path $targetDirectory $preservedFile.RelativePath
|
||||
$restoreDirectory = Split-Path -Parent $restorePath
|
||||
if (-not (Test-Path -Path $restoreDirectory -PathType Container)) {
|
||||
New-Item -ItemType Directory -Path $restoreDirectory -Force | Out-Null
|
||||
}
|
||||
|
||||
Copy-Item -Path $preservedFile.BackupPath -Destination $restorePath -Force
|
||||
}
|
||||
Write-Log -Level "OK" -Message "$preserveFileName files restored"
|
||||
}
|
||||
|
||||
Write-Log -Level "OK" -Message "========================================"
|
||||
Write-Log -Level "OK" -Message "Update completed successfully!"
|
||||
Write-Log -Level "OK" -Message "========================================"
|
||||
}
|
||||
finally {
|
||||
if ($ownsTemporaryRoot -and (Test-Path -Path $temporaryRoot)) {
|
||||
Remove-Item -Path $temporaryRoot -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -1,16 +0,0 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft-07/schema",
|
||||
"title": "Update RepoUtils Script Settings",
|
||||
"description": "Configuration for the Update-RepoUtils utility.",
|
||||
"dryRun": true,
|
||||
"repository": {
|
||||
"url": "https://github.com/MAKS-IT-COM/maksit-repoutils.git",
|
||||
"sourceSubdirectory": "src",
|
||||
"preserveFileName": "scriptSettings.json",
|
||||
"cloneDepth": 1,
|
||||
"skippedRelativeDirectories": [
|
||||
"engines/release/custom",
|
||||
"engines/test/custom"
|
||||
]
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user