(chore): update deps and sync RepoUtils

This commit is contained in:
Maksym Sadovnychyy 2026-08-14 21:02:41 +02:00
parent 9f6284ba6f
commit 8f42706221
31 changed files with 843 additions and 983 deletions

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -1,3 +0,0 @@
@echo off
pwsh -NoProfile -ExecutionPolicy Bypass -File "%~dp0tools\Update-RepoUtils\Update-RepoUtils.ps1" %*
pause

View File

@ -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'
}

View File

@ -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
}

View File

@ -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

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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 {

View File

@ -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

View File

@ -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) {

View File

@ -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)"

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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!"

View File

@ -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))."

View File

@ -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) {

View File

@ -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

View File

@ -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

View File

@ -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.
#>

View File

@ -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..."

View File

@ -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

View File

@ -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

View File

@ -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"
]
}
}