Skip to content

Commit 491fa54

Browse files
committed
#215 feat: enhance Invoke-NovaRelease with progress reporting and next-step guidance
refactor(Invoke-NovaRelease): align with terminal-ux-design principles Fixes #223
1 parent 5320789 commit 491fa54

6 files changed

Lines changed: 287 additions & 19 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
2525
- `Invoke-NovaAgenticCopilotScaffold` now shows apply progress, ends with the project root plus suggested review and validation steps, and fails with clearer cancellation guidance when the overwrite warning is declined.
2626
- `Invoke-NovaBuild` now shows progress for the main build phases, ends with the output module directory plus the next suggested validation step, and explains the refreshed session when `-ContinuousIntegration` reloads the built module.
2727
- `Invoke-NovaCli` now treats blank command input as root help, reports unknown commands with clearer recovery guidance, and ships a dedicated PlatyPS help topic for the PowerShell wrapper surface.
28+
- `Invoke-NovaRelease` now shows progress for the main release phases, ends with the publish target plus a next-step hint, and uses a plan summary instead of a success summary when `-WhatIf` previews the release.
2829

2930
### Deprecated
3031

RELEASE_NOTE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ This file summarizes the release notes for NovaModuleTools. **UNRELEASED** chang
2121
- `Invoke-NovaAgenticCopilotScaffold` now shows apply progress, ends with the project root plus suggested review and validation steps, and gives clearer cancellation guidance when the overwrite warning is declined.
2222
- `Invoke-NovaBuild` now shows progress for the main build phases, ends with the output module directory plus the next suggested validation step, and explains the refreshed session when `-ContinuousIntegration` reloads the built module.
2323
- `Invoke-NovaCli` now treats blank command input as root help, reports unknown commands with clearer recovery guidance, and ships a dedicated help topic for the PowerShell wrapper surface.
24+
- `Invoke-NovaRelease` now shows progress for the main release phases, ends with the publish target plus a next-step hint, and uses a plan summary instead of a success summary when `-WhatIf` previews the release.
2425

2526
### Deprecated
2627

docs/NovaModuleTools/en-US/Invoke-NovaRelease.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ When local release mode is selected, the resolved local publish target is previe
5757

5858
This command supports `-WhatIf` and `-Confirm` through PowerShell `SupportsShouldProcess`. Use `-WhatIf` to preview the entire release workflow and resolved publish target without building, testing, versioning, or publishing.
5959

60+
During the workflow, Nova shows progress for the main release phases. When the workflow completes, Nova prints the publish target and a short next-step hint. In `-WhatIf` mode, Nova ends with a release-plan summary instead of a release-complete message.
61+
6062
## EXAMPLES
6163

6264
### EXAMPLE 1
@@ -323,6 +325,8 @@ When `-ContinuousIntegration` is used, the release workflow restores the built `
323325
`Invoke-NovaRelease` uses `SupportsShouldProcess`, so `Get-Help Invoke-NovaRelease -Full` surfaces native `-WhatIf`
324326
and `-Confirm` support.
325327

328+
Use `Ctrl+C` if you need to stop a running release before all phases complete.
329+
326330
## RELATED LINKS
327331

328332
- [Invoke-NovaBuild](./Invoke-NovaBuild.md)

src/private/release/InvokeNovaReleaseWorkflow.ps1

Lines changed: 184 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,20 +52,195 @@ function Invoke-NovaReleaseWorkflow {
5252
$testWorkflowParams = Get-NovaReleaseBuildWorkflowParameterMap -WorkflowParams $workflowParams -OverrideWarningRequested:$overrideWarningRequested
5353
$shouldRestoreBuiltModule = Test-NovaReleaseWorkflowShouldRestoreBuiltModule -WorkflowParams $workflowParams -ContinuousIntegrationRequested:$continuousIntegrationRequested
5454
$publishParams = $WorkflowContext.PublishParams
55+
$whatIfEnabled = Test-NovaReleaseWorkflowWhatIfEnabled -WorkflowParams $workflowParams
56+
$progressActivity = 'Running Nova release workflow'
57+
$versionResult = $null
5558

56-
Invoke-NovaBuild @buildWorkflowParams
57-
if (-not $skipTestsRequested) {
58-
Test-NovaBuild @testWorkflowParams
59-
}
59+
try {
60+
Invoke-NovaReleaseWorkflowStep -Activity $progressActivity -Status 'Building the current project state' -PercentComplete 15 -Action {
61+
Invoke-NovaBuild @buildWorkflowParams
62+
}
63+
64+
if (-not $skipTestsRequested) {
65+
Invoke-NovaReleaseWorkflowStep -Activity $progressActivity -Status 'Running pre-release tests' -PercentComplete 35 -Action {
66+
Test-NovaBuild @testWorkflowParams
67+
}
68+
}
69+
70+
$versionResult = Invoke-NovaReleaseWorkflowStep -Activity $progressActivity -Status (Get-NovaReleaseVersionStepStatus -WhatIfEnabled:$whatIfEnabled) -PercentComplete 55 -Action {
71+
Update-NovaModuleVersion @ciWorkflowParams
72+
}
6073

61-
$versionResult = Update-NovaModuleVersion @ciWorkflowParams
62-
Invoke-NovaBuild @buildWorkflowParams
74+
Invoke-NovaReleaseWorkflowStep -Activity $progressActivity -Status 'Rebuilding release output' -PercentComplete 75 -Action {
75+
Invoke-NovaBuild @buildWorkflowParams
76+
}
6377

64-
& $WorkflowContext.PublishInvocation.Action @publishParams
78+
Invoke-NovaReleaseWorkflowStep -Activity $progressActivity -Status (Get-NovaReleasePublishStepStatus -WorkflowContext $WorkflowContext -WhatIfEnabled:$whatIfEnabled) -PercentComplete 90 -Action {
79+
& $WorkflowContext.PublishInvocation.Action @publishParams
80+
}
6581

66-
if ($shouldRestoreBuiltModule) {
67-
$null = Import-NovaBuiltModuleForCi -ProjectInfo $WorkflowContext.ProjectInfo
82+
if ($shouldRestoreBuiltModule) {
83+
Invoke-NovaReleaseWorkflowStep -Activity $progressActivity -Status 'Refreshing the current session with the built module' -PercentComplete 98 -Action {
84+
$null = Import-NovaBuiltModuleForCi -ProjectInfo $WorkflowContext.ProjectInfo
85+
}
86+
}
87+
} finally {
88+
Write-Progress -Activity $progressActivity -Completed
6889
}
6990

91+
Write-NovaReleaseWorkflowResult -WorkflowContext $WorkflowContext -VersionResult $versionResult -WhatIfEnabled:$whatIfEnabled -ShouldRestoreBuiltModule:$shouldRestoreBuiltModule
7092
return $versionResult
7193
}
94+
95+
function Invoke-NovaReleaseWorkflowStep {
96+
[CmdletBinding()]
97+
param(
98+
[Parameter(Mandatory)][string]$Activity,
99+
[Parameter(Mandatory)][string]$Status,
100+
[Parameter(Mandatory)][int]$PercentComplete,
101+
[Parameter(Mandatory)][scriptblock]$Action
102+
)
103+
104+
Write-Progress -Activity $Activity -Status $Status -PercentComplete $PercentComplete
105+
& $Action
106+
}
107+
108+
function Test-NovaReleaseWorkflowWhatIfEnabled {
109+
[CmdletBinding()]
110+
param(
111+
[hashtable]$WorkflowParams = @{}
112+
)
113+
114+
return $WorkflowParams.ContainsKey('WhatIf') -and $WorkflowParams.WhatIf
115+
}
116+
117+
function Get-NovaReleaseVersionStepStatus {
118+
[CmdletBinding()]
119+
param(
120+
[switch]$WhatIfEnabled
121+
)
122+
123+
if ($WhatIfEnabled) {
124+
return 'Planning the next release version'
125+
}
126+
127+
return 'Updating the project version'
128+
}
129+
130+
function Get-NovaReleasePublishStepStatus {
131+
[CmdletBinding()]
132+
param(
133+
[Parameter(Mandatory)][pscustomobject]$WorkflowContext,
134+
[switch]$WhatIfEnabled
135+
)
136+
137+
$targetDescription = if ($WorkflowContext.PublishInvocation.IsLocal) {
138+
'the local module path'
139+
} else {
140+
"repository $( $WorkflowContext.PublishInvocation.Target )"
141+
}
142+
143+
if ($WhatIfEnabled) {
144+
return "Previewing publish to $targetDescription"
145+
}
146+
147+
return "Publishing release to $targetDescription"
148+
}
149+
150+
function Write-NovaReleaseWorkflowResult {
151+
[CmdletBinding()]
152+
param(
153+
[Parameter(Mandatory)][pscustomobject]$WorkflowContext,
154+
[AllowNull()][object]$VersionResult,
155+
[switch]$WhatIfEnabled,
156+
[switch]$ShouldRestoreBuiltModule
157+
)
158+
159+
$resolvedVersion = Get-NovaReleaseWorkflowResultVersion -VersionResult $VersionResult
160+
$statusMessage = Get-NovaReleaseWorkflowStatusMessage -ProjectInfo $WorkflowContext.ProjectInfo -Version $resolvedVersion -WhatIfEnabled:$WhatIfEnabled
161+
Write-Message $statusMessage -color Green
162+
Write-Message "Publish target: $( $WorkflowContext.PublishInvocation.Target )"
163+
164+
if ($WorkflowContext.SkipTestsRequested) {
165+
Write-Message 'Pre-release tests were skipped for this run.'
166+
}
167+
168+
if ($ShouldRestoreBuiltModule) {
169+
Write-Message 'The freshly built dist module is loaded again for later commands in this session.'
170+
}
171+
172+
foreach ($line in (Get-NovaReleaseWorkflowNextStepLine -WorkflowContext $WorkflowContext -WhatIfEnabled:$WhatIfEnabled)) {
173+
Write-Message $line
174+
}
175+
}
176+
177+
function Get-NovaReleaseWorkflowResultVersion {
178+
[CmdletBinding()]
179+
param(
180+
[AllowNull()][object]$VersionResult
181+
)
182+
183+
if ($null -eq $VersionResult) {
184+
return $null
185+
}
186+
187+
if ($VersionResult.PSObject.Properties.Name -contains 'NewVersion') {
188+
return $VersionResult.NewVersion
189+
}
190+
191+
if ($VersionResult.PSObject.Properties.Name -contains 'Version') {
192+
return $VersionResult.Version
193+
}
194+
195+
return $null
196+
}
197+
198+
function Get-NovaReleaseWorkflowStatusMessage {
199+
[CmdletBinding()]
200+
param(
201+
[Parameter(Mandatory)][pscustomobject]$ProjectInfo,
202+
[AllowNull()][string]$Version,
203+
[switch]$WhatIfEnabled
204+
)
205+
206+
if ($WhatIfEnabled) {
207+
if ([string]::IsNullOrWhiteSpace($Version)) {
208+
return "Release plan ready for $( $ProjectInfo.ProjectName )"
209+
}
210+
211+
return "Release plan ready for $( $ProjectInfo.ProjectName ) -> $Version"
212+
}
213+
214+
if ([string]::IsNullOrWhiteSpace($Version)) {
215+
return "Released Nova module: $( $ProjectInfo.ProjectName )"
216+
}
217+
218+
return "Released Nova module: $( $ProjectInfo.ProjectName ) $Version"
219+
}
220+
221+
function Get-NovaReleaseWorkflowNextStepLine {
222+
[CmdletBinding()]
223+
param(
224+
[Parameter(Mandatory)][pscustomobject]$WorkflowContext,
225+
[switch]$WhatIfEnabled
226+
)
227+
228+
if ($WhatIfEnabled) {
229+
return @(
230+
'Next step:'
231+
'Run Invoke-NovaRelease without -WhatIf when you are ready to apply the release.'
232+
)
233+
}
234+
235+
if ($WorkflowContext.PublishInvocation.IsLocal) {
236+
return @(
237+
'Next step:'
238+
'Get-NovaProjectInfo -Installed'
239+
)
240+
}
241+
242+
return @(
243+
'Next step:'
244+
'Get-NovaProjectInfo -Version'
245+
)
246+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
function Get-NovaBuildCommandParameterMap {
2+
param(
3+
[hashtable]$WorkflowParams,
4+
[switch]$OverrideWarningRequested
5+
)
6+
7+
$map = @{} + $WorkflowParams
8+
if ($OverrideWarningRequested) {
9+
$map.OverrideWarning = $true
10+
}
11+
12+
return $map
13+
}
14+
15+
function Invoke-NovaBuild {
16+
param()
17+
18+
$script:buildCalls += 1
19+
}
20+
21+
function Test-NovaBuild {
22+
param()
23+
24+
$script:testCalls += 1
25+
}
26+
27+
function Update-NovaModuleVersion {
28+
param()
29+
30+
$script:versionCalls += 1
31+
return [pscustomobject]@{Version = '1.0.0'}
32+
}
33+
34+
function Import-NovaBuiltModuleForCi {
35+
param($ProjectInfo)
36+
37+
$script:restoreCalls += 1
38+
}
39+
40+
function Write-Message {
41+
param(
42+
[string]$Text,
43+
[string]$color
44+
)
45+
}
46+
47+
function Write-Progress {
48+
param(
49+
$Activity,
50+
$Status,
51+
$PercentComplete,
52+
[switch]$Completed
53+
)
54+
}

tests/private/release/InvokeNovaReleaseWorkflow.Tests.ps1

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
BeforeAll {
22
$projectRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $PSScriptRoot))
33
. (Join-Path $projectRoot 'src/private/release/InvokeNovaReleaseWorkflow.ps1')
4-
function Get-NovaBuildCommandParameterMap {param([hashtable]$WorkflowParams,[switch]$OverrideWarningRequested) $map=@{}+$WorkflowParams; if($OverrideWarningRequested){$map.OverrideWarning=$true}; return $map}
5-
function Invoke-NovaBuild {param() $script:buildCalls += 1}
6-
function Test-NovaBuild {param() $script:testCalls += 1}
7-
function Update-NovaModuleVersion {param() $script:versionCalls += 1; return [pscustomobject]@{Version='1.0.0'}}
8-
function Import-NovaBuiltModuleForCi {param($ProjectInfo) $script:restoreCalls += 1}
4+
. (Join-Path $PSScriptRoot 'InvokeNovaReleaseWorkflow.TestSupport.ps1')
95
}
106

117
Describe 'Get-NovaReleaseNestedWorkflowParameterMap' {
@@ -46,14 +42,16 @@ Describe 'Invoke-NovaReleaseWorkflow' {
4642
$script:versionCalls = 0
4743
$script:restoreCalls = 0
4844
$script:publishCalls = 0
45+
Mock Write-Message {}
46+
Mock Write-Progress {}
4947
}
5048

51-
It 'builds, tests, updates version, builds again, publishes, and restores in CI' {
49+
It 'builds, tests, updates version, builds again, publishes, restores in CI, and reports the result' {
5250
$ctx = [pscustomobject]@{
5351
WorkflowParams = @{}
5452
PublishParams = @{}
55-
PublishInvocation = [pscustomobject]@{Action = {param() $script:publishCalls += 1}}
56-
ProjectInfo = [pscustomobject]@{}
53+
PublishInvocation = [pscustomobject]@{Action = {param() $script:publishCalls += 1}; Target = 'PSGallery'; IsLocal = $false}
54+
ProjectInfo = [pscustomobject]@{ProjectName = 'NovaModuleTools'}
5755
ContinuousIntegrationRequested = $true
5856
SkipTestsRequested = $false
5957
OverrideWarningRequested = $false
@@ -65,18 +63,53 @@ Describe 'Invoke-NovaReleaseWorkflow' {
6563
$script:versionCalls | Should -Be 1
6664
$script:publishCalls | Should -Be 1
6765
$script:restoreCalls | Should -Be 1
66+
Assert-MockCalled Write-Progress -Times 6
67+
Assert-MockCalled Write-Message -Times 5
68+
Assert-MockCalled Write-Message -Times 1 -ParameterFilter {
69+
$Text -eq 'Released Nova module: NovaModuleTools 1.0.0' -and $color -eq 'Green'
70+
}
71+
Assert-MockCalled Write-Message -Times 1 -ParameterFilter {
72+
$Text -eq 'Get-NovaProjectInfo -Version'
73+
}
6874
}
6975

7076
It 'skips tests when SkipTestsRequested and skips restore when not CI' {
7177
$ctx = [pscustomobject]@{
7278
WorkflowParams = @{}
7379
PublishParams = @{}
74-
PublishInvocation = [pscustomobject]@{Action = {param() $script:publishCalls += 1}}
75-
ProjectInfo = [pscustomobject]@{}
80+
PublishInvocation = [pscustomobject]@{Action = {param() $script:publishCalls += 1}; Target = '/modules'; IsLocal = $true}
81+
ProjectInfo = [pscustomobject]@{ProjectName = 'NovaModuleTools'}
7682
SkipTestsRequested = $true
7783
}
7884
$null = Invoke-NovaReleaseWorkflow -WorkflowContext $ctx
7985
$script:testCalls | Should -Be 0
8086
$script:restoreCalls | Should -Be 0
87+
Assert-MockCalled Write-Message -Times 1 -ParameterFilter {
88+
$Text -eq 'Pre-release tests were skipped for this run.'
89+
}
90+
Assert-MockCalled Write-Message -Times 1 -ParameterFilter {
91+
$Text -eq 'Get-NovaProjectInfo -Installed'
92+
}
93+
}
94+
95+
It 'writes a release plan summary in WhatIf mode without restoring the built module' {
96+
$ctx = [pscustomobject]@{
97+
WorkflowParams = @{WhatIf = $true}
98+
PublishParams = @{}
99+
PublishInvocation = [pscustomobject]@{Action = {param() $script:publishCalls += 1}; Target = 'PSGallery'; IsLocal = $false}
100+
ProjectInfo = [pscustomobject]@{ProjectName = 'NovaModuleTools'}
101+
ContinuousIntegrationRequested = $true
102+
SkipTestsRequested = $false
103+
}
104+
105+
$null = Invoke-NovaReleaseWorkflow -WorkflowContext $ctx
106+
107+
$script:restoreCalls | Should -Be 0
108+
Assert-MockCalled Write-Message -Times 1 -ParameterFilter {
109+
$Text -eq 'Release plan ready for NovaModuleTools -> 1.0.0' -and $color -eq 'Green'
110+
}
111+
Assert-MockCalled Write-Message -Times 1 -ParameterFilter {
112+
$Text -eq 'Run Invoke-NovaRelease without -WhatIf when you are ready to apply the release.'
113+
}
81114
}
82115
}

0 commit comments

Comments
 (0)