Skip to content

Commit 6c85b69

Browse files
committed
feat(#140): enhance release automation and CI support
- Update `Update-NovaModuleVersion -ContinuousIntegration` to fallback to a patch bump when `HEAD` matches the latest tag - Fix PSGallery publishing on fresh CI runners by bootstrapping the PSResourceGet repository store - Add `Get-PSResourceRepositoryStoreDirectory` and `Initialize-PSGalleryRepository` functions
1 parent 6e23423 commit 6c85b69

11 files changed

Lines changed: 153 additions & 3 deletions

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8989
- CI bump now reuses the already activated built-module command when the current session is already running from
9090
`dist/`, so publish-then-bump prerelease automation can continue in the same session without losing private helper
9191
bindings.
92+
- `Update-NovaModuleVersion -ContinuousIntegration` now also falls back to a patch bump when `HEAD` already matches
93+
the latest tag, so release automation can prepare the next prerelease version without requiring an extra commit.
9294
- Keep standalone `nova bump` output stable by formatting version-update results in the CLI layer instead of relying on
9395
PowerShell's default object rendering.
9496
- `nova bump --what-if` and `% run.ps1` now surface a predictable summary for previous version, new version, label,
@@ -104,6 +106,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
104106
failing during parameter binding.
105107
- Fix configuration and validation errors so empty `project.json` files and unsupported `Manifest` keys fail fast with
106108
clear messages.
109+
- Fix semantic-release PSGallery publishing on fresh CI runners by bootstrapping the PSResourceGet repository store
110+
before
111+
`Publish-PSResource` runs.
107112

108113
### Changed
109114

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,9 @@ These switches keep the behavior explicit and opt-in:
209209

210210
- `Invoke-NovaBuild -ContinuousIntegration` re-imports the freshly built module after the build succeeds
211211
- `Update-NovaModuleVersion -ContinuousIntegration` re-imports the built module before the bump workflow starts
212+
- `Update-NovaModuleVersion -ContinuousIntegration` also falls back to a patch bump when the current `HEAD` already
213+
matches the latest tag, so release automation can seed the next prerelease line without requiring an extra commit
214+
first
212215
- `Publish-NovaModule -ContinuousIntegration` restores the built module after publish completes
213216
- `Invoke-NovaRelease -ContinuousIntegration` forwards that CI intent through the nested build/bump boundaries and then
214217
restores the built module again after publish
@@ -595,6 +598,11 @@ Responsibilities currently covered by the release pipeline include:
595598
- creating GitHub releases
596599
- publishing to PowerShell Gallery
597600

601+
The semantic-release publish script also bootstraps the local PSResourceGet repository store before calling
602+
`Publish-PSResource`, which keeps fresh GitHub Actions runners from failing when the PSGallery repository registration
603+
file
604+
does not exist yet.
605+
598606
### Where NovaModuleTools cmdlets fit
599607

600608
NovaModuleTools already provides strong release building blocks:

scripts/release/Publish-ToPSGallery.ps1

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ param(
44
)
55

66
Set-StrictMode -Version Latest
7+
$ErrorActionPreference = 'Stop'
8+
9+
$scriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
10+
. (Join-Path $scriptRoot 'SemanticReleaseSupport.ps1')
711

812
if ([string]::IsNullOrWhiteSpace($ApiKey)) {
913
throw 'PSGALLERY_API environment variable is required.'
@@ -13,4 +17,5 @@ if (-not (Test-Path -LiteralPath $ModulePath)) {
1317
throw "Module path not found: $ModulePath"
1418
}
1519

16-
Publish-PSResource -Path $ModulePath -Repository PSGallery -ApiKey $ApiKey -Verbose
20+
Initialize-PSGalleryRepository
21+
Publish-PSResource -Path $ModulePath -Repository PSGallery -ApiKey $ApiKey -Verbose -ErrorAction Stop

scripts/release/SemanticReleaseSupport.ps1

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ $script:supportRoot = Join-Path (Split-Path -Parent $MyInvocation.MyCommand.Path
44
$script:supportFileList = @(
55
'Get-ReleaseDateString.ps1'
66
'Get-ReleaseRepositoryUrl.ps1'
7+
'Get-PSResourceRepositoryStoreDirectory.ps1'
8+
'Initialize-PSGalleryRepository.ps1'
79
'ConvertTo-ReleaseTagName.ps1'
810
'Read-JsonFile.ps1'
911
'Write-JsonFile.ps1'
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
function Get-PSResourceRepositoryStoreDirectory {
2+
[CmdletBinding()]
3+
param()
4+
5+
return Join-Path $HOME '.local/share/PSResourceGet'
6+
}
7+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
function Initialize-PSGalleryRepository {
2+
[CmdletBinding()]
3+
param()
4+
5+
$storeDirectory = Get-PSResourceRepositoryStoreDirectory
6+
if (-not (Test-Path -LiteralPath $storeDirectory)) {
7+
New-Item -ItemType Directory -Path $storeDirectory -Force | Out-Null
8+
}
9+
10+
if ($null -eq (Get-PSResourceRepository -Name PSGallery -ErrorAction SilentlyContinue)) {
11+
Register-PSResourceRepository -PSGallery
12+
}
13+
}
14+

src/private/release/GetNovaVersionLabelForBump.ps1

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ function Get-NovaVersionLabelForBump {
22
[CmdletBinding()]
33
param(
44
[Parameter(Mandatory)][string]$ProjectRoot,
5-
[string[]]$CommitMessages = @()
5+
[string[]]$CommitMessages = @(),
6+
[switch]$ContinuousIntegrationRequested
67
)
78

89
if ($CommitMessages.Count -gt 0) {
@@ -18,6 +19,10 @@ function Get-NovaVersionLabelForBump {
1819
}
1920

2021
if (-not (Test-GitRepositoryHasCommitsSinceLatestTag -ProjectRoot $ProjectRoot)) {
22+
if ($ContinuousIntegrationRequested) {
23+
return 'Patch'
24+
}
25+
2126
Stop-NovaOperation -Message 'Cannot bump version because there are no commits since the latest tag.' -ErrorId 'Nova.Workflow.NoCommitsSinceLatestTag' -Category InvalidOperation -TargetObject $ProjectRoot
2227
}
2328

src/private/release/GetNovaVersionUpdateWorkflowContext.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ function Get-NovaVersionUpdateWorkflowContext {
88

99
$projectInfo = Get-NovaProjectInfo -Path $ProjectRoot
1010
$commitMessages = @(Get-GitCommitMessageForVersionBump -ProjectRoot $ProjectRoot)
11-
$label = Get-NovaVersionLabelForBump -ProjectRoot $ProjectRoot -CommitMessages $commitMessages
11+
$label = Get-NovaVersionLabelForBump -ProjectRoot $ProjectRoot -CommitMessages $commitMessages -ContinuousIntegrationRequested:$ContinuousIntegrationRequested
1212
$versionUpdatePlan = Get-NovaVersionUpdatePlan -ProjectInfo $projectInfo -Label $label -PreviewRelease:$PreviewRelease
1313

1414
return Get-NovaVersionUpdateWorkflowContextObject -ProjectRoot $ProjectRoot -ProjectInfo $projectInfo -CommitMessages $commitMessages -Label $label -VersionUpdatePlan $versionUpdatePlan -PreviewRelease:$PreviewRelease -ContinuousIntegrationRequested:$ContinuousIntegrationRequested

tests/CoverageGaps.ReleaseInternals.Tests.ps1

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,33 @@ Describe 'Coverage gaps for release and git internals' {
620620
}
621621
}
622622

623+
It 'Get-NovaVersionLabelForBump returns Patch in CI mode when the current head already matches the latest tag' {
624+
InModuleScope $script:moduleName {
625+
$projectRoot = Join-Path $TestDrive 'mocked-ci-git-state'
626+
New-Item -ItemType Directory -Path (Join-Path $projectRoot '.git') -Force | Out-Null
627+
628+
Mock Invoke-NovaGitCommand {
629+
switch ($Arguments[0]) {
630+
'rev-parse' {
631+
return [pscustomobject]@{ExitCode = 0; Output = @('.git')}
632+
}
633+
'describe' {
634+
return [pscustomobject]@{ExitCode = 0; Output = @('v1.0.0')}
635+
}
636+
'rev-list' {
637+
return [pscustomobject]@{ExitCode = 0; Output = @('0')}
638+
}
639+
default {
640+
throw "Unexpected git args: $( $Arguments -join ' ' )"
641+
}
642+
}
643+
}
644+
645+
Get-NovaVersionLabelForBump -ProjectRoot $projectRoot -ContinuousIntegrationRequested | Should -Be 'Patch'
646+
Assert-MockCalled Invoke-NovaGitCommand -Times 1 -ParameterFilter {$Arguments[0] -eq 'rev-list' -and $Arguments[2] -eq 'v1.0.0..HEAD'}
647+
}
648+
}
649+
623650
It 'Get-NovaVersionLabelForBump returns Patch when the repository has commits but no tags yet' {
624651
InModuleScope $script:moduleName {
625652
$projectRoot = Join-Path $TestDrive 'mocked-git-without-tags'

tests/NovaCommandModel.BumpAndCli.Tests.ps1

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,10 @@ Describe 'Nova command model - bump and CLI confirmation behavior' {
133133
$result = Get-NovaVersionUpdateWorkflowContext -ProjectRoot '/tmp/project' -ContinuousIntegrationRequested
134134

135135
$result.'ContinuousIntegrationRequested' | Should -BeTrue
136+
Assert-MockCalled Get-NovaVersionLabelForBump -Times 1 -ParameterFilter {
137+
$ProjectRoot -eq '/tmp/project' -and
138+
$ContinuousIntegrationRequested
139+
}
136140
}
137141
}
138142

@@ -524,6 +528,36 @@ Describe 'Nova command model - bump and CLI confirmation behavior' {
524528
}
525529
}
526530

531+
It 'Update-NovaModuleVersion -Preview -ContinuousIntegration -WhatIf still bumps from a tagged head with no new commits' -Skip:(-not [bool](Get-Command git -ErrorAction SilentlyContinue)) {
532+
InModuleScope $script:moduleName {
533+
$projectRoot = Join-Path $TestDrive 'tagged-head-ci-bump-project'
534+
New-Item -ItemType Directory -Path $projectRoot -Force | Out-Null
535+
& git -C $projectRoot init --quiet | Out-Null
536+
& git -C $projectRoot config user.name 'NovaModuleTools Tests' | Out-Null
537+
& git -C $projectRoot config user.email 'tests@example.invalid' | Out-Null
538+
Set-Content -LiteralPath (Join-Path $projectRoot 'first.txt') -Value 'first' -Encoding utf8
539+
& git -C $projectRoot add first.txt | Out-Null
540+
& git -C $projectRoot commit --quiet -m 'feat: initial release' | Out-Null
541+
& git -C $projectRoot tag v2.0.0 | Out-Null
542+
543+
Mock Get-NovaProjectInfo {
544+
[pscustomobject]@{
545+
Version = '2.0.0'
546+
ProjectJSON = (Join-Path $projectRoot 'project.json')
547+
}
548+
}
549+
Mock Set-NovaModuleVersion {throw 'WhatIf should not write project.json'}
550+
551+
$result = Update-NovaModuleVersion -Path $projectRoot -Preview -ContinuousIntegration -WhatIf
552+
553+
$result.PreviousVersion | Should -Be '2.0.0'
554+
$result.NewVersion | Should -Be '2.0.1-preview'
555+
$result.Label | Should -Be 'Patch'
556+
$result.CommitCount | Should -Be 0
557+
Assert-MockCalled Set-NovaModuleVersion -Times 0
558+
}
559+
}
560+
527561
It 'Update-NovaModuleVersion keeps native PowerShell Confirm support for direct cmdlet usage and does not route through the CLI helper' {
528562
InModuleScope $script:moduleName {
529563
$command = Get-Command -Name 'Update-NovaModuleVersion' -CommandType Function -ErrorAction Stop

0 commit comments

Comments
 (0)