Skip to content

Commit b6b65e5

Browse files
committed
fix(#148): improve interactive validation for nova init prompts
- Implement immediate inline validation for invalid module names during interactive flow. - Update prompt behavior to retry on validation failure before proceeding. - Enhance documentation to reflect new validation behavior.
1 parent 0ac16e6 commit b6b65e5

16 files changed

Lines changed: 263 additions & 71 deletions

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ Keep stable `Update-NovaModuleVersion` / `% nova bump` releases on the SemVer ma
4646
- Fix the repository `run.ps1` quality loop after the CI installer refactor introduced new ScriptAnalyzer warnings.
4747
- The internal CI installer helper now follows ScriptAnalyzer naming and `ShouldProcess` expectations.
4848
- `run.ps1` no longer stops in the analyzer step because of those helper warnings.
49+
- Fix interactive `nova init` / `nova init -e` scaffold validation so invalid answers retry immediately at the prompt.
50+
- Invalid module names now show the validation message inline instead of failing after the full questionnaire.
51+
- Standard and example scaffold flows now share the same retry-first validation behavior through the common prompt
52+
path.
4953

5054
### Security
5155

docs/NovaModuleTools/en-US/Initialize-NovaModule.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ PS> Initialize-NovaModule [-Path <string>] [-Example] [-WhatIf] [-Confirm] [<Com
3030
The command collects project details interactively, including the module name, description, version, author, minimum
3131
PowerShell version, Git initialization, and, for the standard scaffold, optional basic Pester support.
3232

33+
If you enter an invalid answer during the interactive flow, `Initialize-NovaModule` reports the validation problem
34+
immediately and retries that prompt before it continues to the next question.
35+
3336
Use this command when you want to start a new module in the NovaModuleTools structure without hand-creating the project
3437
layout.
3538

@@ -38,7 +41,8 @@ supported.
3841

3942
Use `-Example` when you want the scaffold to start from the packaged example project instead of the minimal default
4043
layout. The example flow keeps the example source, resource, and test files, skips the Pester enable/disable question,
41-
and applies the interactive metadata values to the copied `project.json`.
44+
and applies the interactive metadata values to the copied `project.json`. The standard and example flows share the same
45+
inline validation and retry behavior for interactive answers.
4246

4347
This command supports `-WhatIf` and `-Confirm` through PowerShell `SupportsShouldProcess`. Use `-WhatIf` to preview the
4448
scaffold target after the interactive answers have been collected, without creating folders, writing `project.json`, or
@@ -53,6 +57,7 @@ PS> Initialize-NovaModule -Path ~/Work
5357
```
5458

5559
Starts the interactive scaffold flow and creates the new module under `~/Work`.
60+
Invalid interactive answers are retried immediately before the command continues.
5661

5762
### EXAMPLE 2
5863

@@ -70,6 +75,7 @@ PS> Initialize-NovaModule -Example -Path ~/Work
7075

7176
Creates a new project under `~/Work` from the packaged example template and applies the answers from the interactive
7277
prompt flow to the copied `project.json`.
78+
Invalid interactive answers are retried immediately here as well.
7379

7480
### EXAMPLE 4
7581

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
function Get-AwesomePromptFieldDescription {
2+
param([Parameter(Mandatory)][object]$Ask)
3+
4+
$fieldDescription = [System.Management.Automation.Host.FieldDescription]::new(
5+
(Get-AwesomePromptValue -Ask $Ask -Name 'Prompt')
6+
)
7+
$defaultValue = Get-AwesomePromptValue -Ask $Ask -Name 'Default'
8+
if ($defaultValue -ne 'MANDATORY') {
9+
$fieldDescription.DefaultValue = $defaultValue
10+
}
11+
12+
return $fieldDescription
13+
}
14+
15+

src/private/cli/GetAwesomePromptResult.ps1

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
function Get-AwesomePromptResult {
22
param(
3-
[Parameter(Mandatory)][pscustomobject]$Ask,
3+
[Parameter(Mandatory)][object]$Ask,
44
[Parameter(Mandatory)][object]$Response
55
)
66

77
if ( [string]::IsNullOrEmpty($Response.Values)) {
8-
return $Ask.Default
8+
return Get-AwesomePromptValue -Ask $Ask -Name 'Default'
99
}
1010

1111
return $Response.Values
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
function Get-AwesomePromptValidationFailure {
2+
param(
3+
[Parameter(Mandatory)][object]$Ask,
4+
[AllowNull()]$Value
5+
)
6+
7+
$validation = Get-AwesomePromptValue -Ask $Ask -Name 'Validation'
8+
if ($null -eq $validation) {
9+
return $null
10+
}
11+
12+
$validator = Get-AwesomePromptValue -Ask $validation -Name 'Test'
13+
if ($null -eq $validator) {
14+
return $null
15+
}
16+
17+
if ([bool](& $validator $Value)) {
18+
return $null
19+
}
20+
21+
return [pscustomobject]@{
22+
Message = Get-AwesomePromptValue -Ask $validation -Name 'Message'
23+
ErrorId = Get-AwesomePromptValue -Ask $validation -Name 'ErrorId'
24+
Category = Get-AwesomePromptValue -Ask $validation -Name 'Category'
25+
TargetObject = $Value
26+
}
27+
}
28+
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
function Get-AwesomePromptValue {
2+
param(
3+
[Parameter(Mandatory)][object]$Ask,
4+
[Parameter(Mandatory)][string]$Name
5+
)
6+
7+
if ($Ask -is [System.Collections.IDictionary]) {
8+
if ( $Ask.Contains($Name)) {
9+
return $Ask[$Name]
10+
}
11+
12+
return $null
13+
}
14+
15+
$property = $Ask.PSObject.Properties[$Name]
16+
if ($null -eq $property) {
17+
return $null
18+
}
19+
20+
return $property.Value
21+
}
22+
Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
function Read-AwesomeChoicePrompt {
22
param(
3-
[Parameter(Mandatory)][pscustomobject]$Ask,
3+
[Parameter(Mandatory)][object]$Ask,
44
[Parameter(Mandatory)][object]$HostUi
55
)
66

7-
$options = Get-AwesomeChoiceOptionList -Choice $Ask.Choice
8-
$defaultIndex = $options.Label.IndexOf('&' + $Ask.Default)
9-
$response = $HostUi.PromptForChoice($Ask.Caption, $Ask.Message, $options, $defaultIndex)
7+
$options = Get-AwesomeChoiceOptionList -Choice (Get-AwesomePromptValue -Ask $Ask -Name 'Choice')
8+
$defaultIndex = $options.Label.IndexOf('&' + (Get-AwesomePromptValue -Ask $Ask -Name 'Default'))
9+
$response = $HostUi.PromptForChoice(
10+
(Get-AwesomePromptValue -Ask $Ask -Name 'Caption'),
11+
(Get-AwesomePromptValue -Ask $Ask -Name 'Message'),
12+
$options,
13+
$defaultIndex
14+
)
1015

1116
return $options.Label[$response] -replace '&'
1217
}

src/private/cli/ReadAwesomeStandardPrompt.ps1

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
11
function Read-AwesomeStandardPrompt {
22
param(
3-
[Parameter(Mandatory)][pscustomobject]$Ask,
3+
[Parameter(Mandatory)][object]$Ask,
44
[Parameter(Mandatory)][object]$HostUi
55
)
66

7-
$fieldDescription = [System.Management.Automation.Host.FieldDescription]::new($Ask.Prompt)
8-
if ($Ask.Default -ne 'MANDATORY') {
9-
$fieldDescription.DefaultValue = $Ask.Default
10-
}
7+
$fieldDescription = Get-AwesomePromptFieldDescription -Ask $Ask
118

129
do {
13-
$response = $HostUi.Prompt($Ask.Caption, $Ask.Message, @($fieldDescription))
10+
$response = $HostUi.Prompt(
11+
(Get-AwesomePromptValue -Ask $Ask -Name 'Caption'),
12+
(Get-AwesomePromptValue -Ask $Ask -Name 'Message'),
13+
@($fieldDescription)
14+
)
15+
16+
if (Test-AwesomePromptRequiresRetry -Ask $Ask -Response $response) {
17+
Write-AwesomePromptRetryMessage -Ask $Ask -Response $response
18+
}
1419
} while (Test-AwesomePromptRequiresRetry -Ask $Ask -Response $response)
1520

1621
return Get-AwesomePromptResult -Ask $Ask -Response $response
Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
function Test-AwesomePromptRequiresRetry {
22
param(
3-
[Parameter(Mandatory)][pscustomobject]$Ask,
3+
[Parameter(Mandatory)][object]$Ask,
44
[Parameter(Mandatory)][object]$Response
55
)
66

7-
return $Ask.Default -eq 'MANDATORY' -and [string]::IsNullOrEmpty($Response.Values)
7+
if ((Get-AwesomePromptValue -Ask $Ask -Name 'Default') -eq 'MANDATORY' -and [string]::IsNullOrEmpty($Response.Values)) {
8+
return $true
9+
}
10+
11+
$value = Get-AwesomePromptResult -Ask $Ask -Response $Response
12+
return $null -ne (Get-AwesomePromptValidationFailure -Ask $Ask -Value $value)
813
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
function Write-AwesomePromptRetryMessage {
2+
param(
3+
[Parameter(Mandatory)][object]$Ask,
4+
[Parameter(Mandatory)][object]$Response
5+
)
6+
7+
$value = Get-AwesomePromptResult -Ask $Ask -Response $Response
8+
$failure = Get-AwesomePromptValidationFailure -Ask $Ask -Value $value
9+
if ($null -eq $failure) {
10+
return
11+
}
12+
13+
Write-Message -Text $failure.Message -color Yello
14+
}
15+

0 commit comments

Comments
 (0)