Skip to content

Commit

Permalink
Merge pull request #430 from erikgraa/feature/validatePatternForParam…
Browse files Browse the repository at this point in the history
…eters
  • Loading branch information
psjamesp authored Feb 27, 2023
2 parents 7b2d40b + f9e4c12 commit 8d858c1
Show file tree
Hide file tree
Showing 3 changed files with 222 additions and 5 deletions.
31 changes: 26 additions & 5 deletions Plaster/InvokePlaster.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -538,13 +538,26 @@ function Invoke-Plaster {
}
}

function PromptForInput($prompt, $default) {
function PromptForInput($prompt, $default, $pattern) {
if (!$pattern) {
$patternMatch = $true
}

do {
$value = Read-Host -Prompt $prompt
if (!$value -and $default) {
$value = $default
$patternMatch = $true
}
elseif ($value -and $pattern) {
if ($value -match $pattern) {
$patternMatch = $true
}
else {
$PSCmdlet.WriteDebug("Value '$value' did not match the pattern '$pattern'")
}
}
} while (!$value)
} while (!$value -or !$patternMatch)

$value
}
Expand Down Expand Up @@ -625,6 +638,8 @@ function Invoke-Plaster {
$type = $Node.type
$store = $Node.store

$pattern = $Node.pattern

$condition = $Node.condition

$default = InterpolateAttributeValue $Node.default (GetErrorLocationParameterAttrVal $name default)
Expand Down Expand Up @@ -675,6 +690,12 @@ function Invoke-Plaster {
# Some default values might not come from the template e.g. some are harvested from .gitconfig if it exists.
$defaultNotFromTemplate = $false

$splat = @{}

if ($null -ne $pattern) {
$splat.Add('pattern', $pattern)
}

# Now prompt user for parameter value based on the parameter type.
switch -regex ($type) {
'text' {
Expand All @@ -689,7 +710,7 @@ function Invoke-Plaster {
}
}
# Prompt the user for text input.
$value = PromptForInput $prompt $default
$value = PromptForInput $prompt $default @splat
$valueToStore = $value
}
'user-fullname' {
Expand All @@ -710,7 +731,7 @@ function Invoke-Plaster {
}

# Prompt the user for text input.
$value = PromptForInput $prompt $default
$value = PromptForInput $prompt $default @splat
$valueToStore = $value
}
'user-email' {
Expand All @@ -731,7 +752,7 @@ function Invoke-Plaster {
}

# Prompt the user for text input.
$value = PromptForInput $prompt $default
$value = PromptForInput $prompt $default @splat
$valueToStore = $value
}
'choice|multichoice' {
Expand Down
5 changes: 5 additions & 0 deletions Plaster/Schema/PlasterManifest-v1.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,11 @@
<xs:documentation>The default value for the parameter which the user will be able to accept or change.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="pattern" type="xs:string">
<xs:annotation>
<xs:documentation>Used for parameter input validation. If the value matches the pattern, the value is accepted. Otherwise it is not.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="store" type="ptd:StoreFormatType"/>
<xs:attribute name="condition" type="ptd:condition">
<xs:annotation>
Expand Down
191 changes: 191 additions & 0 deletions examples/plasterManifest-validatePattern.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
<?xml version="1.0" encoding="utf-8"?>
<plasterManifest schemaVersion="0.3" xmlns="http://www.microsoft.com/schemas/PowerShell/Plaster/v1">
<metadata>
<!-- For the default store file, we need a unique identifier to ensure one template's default value store
doesn't get used with any other template. -->
<id>ac9f688f-503d-48c9-833a-f0d767d92f47</id>
<name>NewPowerShellModule</name>
<!-- For display by UI's in a menu list -->
<title>New PowerShell Module</title>
<!-- For display by UI's in hover help -->
<description>Plaster template for creating the files for a PowerShell module.</description>
<version>0.2.0</version>
<tags>Module, ModuleManifest, Build</tags>
</metadata>
<parameters>
<parameter name='ModuleName' type='text' prompt='Enter the name of the module' pattern='^[\w\.\-_]+$'/>
<parameter name='ModuleDesc' type='text' prompt='Enter a description of the module (required for publishing to the PowerShell Gallery)' pattern='^[\w\s]+\.?$'/>
<parameter name='Version' type='text' default='1.0.0' prompt='Enter the version number for the module' pattern='^\d+\.\d+(\.\d+)?(\.\d+)?$'/>
<parameter name='FullName' type='user-fullname' store='text' prompt='Enter your full name' pattern='^[\w\s]+$'/>
<parameter name='Email' type='user-email' store='text' prompt='Enter your email address'/>
<parameter name='Options' type='multichoice' default='0,1,3' store='text' prompt='Select desired options'>
<choice label='&amp;Pester test support'
help="Adds Tests directory and a starter Pester Tests file."
value="Pester"/>
<choice label='P&amp;Sake build script'
help="Adds a PSake build script that generates the module directory for publishing to the PSGallery."
value="PSake"/>
<choice label='platyPS &amp;documentation support'
help="Adds documentation support using platyPS."
value="platyPS"/>
<choice label='&amp;Git'
help="Adds a .gitignore file."
value="Git"/>
<choice label='&amp;None'
help="No options specified."
value="None"/>
</parameter>
<parameter name='Editor' type='choice' default='2' store='text' prompt='Which editor do you use'>
<choice label='&amp;ISE'
help="Your editor is PowerShell ISE."
value="ISE"/>
<choice label='Visual Studio &amp;Code'
help="Your editor is Visual Studio Code."
value="VSCode"/>
<choice label='&amp;None'
help="No editor specified."
value="None"/>
</parameter>
<parameter name='License' type='choice' default='2' store='text' prompt='Select a license for your module'>
<choice label='&amp;Apache'
help="Adds an Apache license file."
value="Apache"/>
<choice label='&amp;MIT'
help="Adds an MIT license file."
value="MIT"/>
<choice label='&amp;None'
help="No license specified."
value="None"/>
</parameter>
</parameters>
<content>
<message>
Scaffold a PowerShell Module with the files required to run Pester tests, build with PSake and publish to the PSGallery.

</message>

<newModuleManifest destination='src\${PLASTER_PARAM_ModuleName}.psd1'
moduleVersion='$PLASTER_PARAM_Version'
rootModule='${PLASTER_PARAM_ModuleName}.psm1'
author='$PLASTER_PARAM_FullName'
description='$PLASTER_PARAM_ModuleDesc'
encoding='UTF8-NoBOM'/>
<file source='Module.psm1'
destination='src\${PLASTER_PARAM_ModuleName}.psm1'/>
<file source='_gitignore'
destination='.gitignore'
condition='$PLASTER_PARAM_Options -contains "Git"'/>
<file source='build*.ps1'
destination=''
condition='$PLASTER_PARAM_Options -contains "PSake"'/>
<file source=''
destination='docs\'
condition='$PLASTER_PARAM_Options -contains "platyPS"'/>
<!-- Recursively copy all *files* into the corresponding directory structure under dest dir RecurseFile -->
<file source='RecurseFile\**'
destination='Recurse'/>
<templateFile source='ApacheLicense.txt'
destination='LICENSE.txt'
condition="$PLASTER_PARAM_License -eq 'Apache'"
encoding="UTF8-NoBOM"/>
<templateFile source='MITLicense.txt'
destination='LICENSE.txt'
condition="$PLASTER_PARAM_License -eq 'MIT'"/>
<templateFile source='en-US\about_Module.help.txt'
destination='en-US\about_${PLASTER_PARAM_ModuleName}.help.txt'/>
<templateFile source='Tests\Module.T.ps1'
destination='test\${PLASTER_PARAM_ModuleName}.Tests.ps1'
condition="$PLASTER_PARAM_Options -contains 'Pester'"/>
<templateFile source='RecurseTemplateFile\**'
destination='Recurse'
encoding='Ascii'/>
<file source='tasks.json'
destination='.vscode\tasks.json'
condition="$PLASTER_PARAM_Editor -eq 'VSCode'"/>
<modify path='.vscode\tasks.json' encoding='UTF8'
condition="$PLASTER_PARAM_Editor -eq 'VSCode'">
<replace condition="$PLASTER_FileContent -notmatch '// Author:'">
<original>(?s)^(.*)</original>
<substitute expand='true'>// Author: $PLASTER_PARAM_FullName`r`n`$1</substitute>
</replace>
<replace condition="$PLASTER_PARAM_Options -contains 'Pester' -and $PLASTER_FileContent -notmatch 'taskName&quot;:\s*&quot;Test&quot;'">
<original><![CDATA[(?si)(?<="tasks":\s*\[)(\s*)(?=\{)]]></original>
<substitute><![CDATA[$1{
"taskName": "Test",
"suppressTaskName": true,
"isTestCommand": true,
"showOutput": "always",
"args": [
"Write-Host 'Invoking Pester...'; Invoke-Pester -PesterOption @{IncludeVSCodeMarker=$true};",
"Invoke-Command { Write-Host 'Completed Test task in task runner.' }"
],
"problemMatcher": [
{
"owner": "powershell",
"fileLocation": ["absolute"],
"severity": "error",
"pattern": [
{
"regexp": "^\\s*(\\[-\\]\\s*.*?)(\\d+)ms\\s*$",
"message": 1
},
{
"regexp": "^\\s+at\\s+[^,]+,\\s*(.*?):\\s+line\\s+(\\d+)$",
"file": 1,
"line": 2
}
]
}
]
},$1]]></substitute>
</replace>

<replace condition="$PLASTER_PARAM_Options -contains 'PSake' -and $PLASTER_FileContent -notmatch 'taskName&quot;:\s*&quot;Build&quot;'">
<original><![CDATA[(?si)(?<="tasks":\s*\[)(\s*)(?=\{)]]></original>
<substitute><![CDATA[$1{
"taskName": "Build",
"suppressTaskName": true,
"isBuildCommand": true,
"showOutput": "always",
"args": [
"Write-Host 'Invoking PSake...'; Invoke-PSake build.ps1 -taskList Build;",
"Invoke-Command { Write-Host 'Completed Build task in task runner.' }"
]
},$1]]></substitute>
</replace>
</modify>

<requireModule name="Pester" condition='$PLASTER_PARAM_Options -contains "Pester"'
minimumVersion="3.4.0"
message="Without Pester, you will not be able to run the provided Pester test to validate your module manifest file.`nWithout version 3.4.0, VS Code will not display Pester warnings and errors in the Problems panel."/>

<requireModule name="psake" condition='$PLASTER_PARAM_Options -contains "PSake"'
message="Without psake, you will not be able to run the provided build script to build and/or publish your module."/>

<requireModule name="platyPS" condition='$PLASTER_PARAM_Options -contains "platyPS"'
message="Without platyPS, you will not be able to generate PowerShell external help for your module using markdown."/>

<message nonewline='true'>`n`nYour new PowerShell module project $PLASTER_PARAM_ModuleName </message>
<message nonewline='true' condition="$PLASTER_PARAM_Options -contains 'Git'">with Git version control </message>
<message>has been created.</message>

<message condition="$PLASTER_PARAM_Options -contains 'PSake'">
You can build your project by executing the 'build' task by pressing Ctrl+P, then type 'task build'.
You can publish your project to the PSGallery by pressing Ctrl+P, then type 'task publish'.

</message>

<message condition="$PLASTER_PARAM_Options -contains 'Pester'">
A Pester test has been created to validate the module's manifest file. Add additional test to the Tests directory.
You can run the Pester tests in your project by executing the 'test' task by pressing Ctrl+P, then type 'task test'.

</message>

<message condition="$PLASTER_PARAM_Options -contains 'platyPS'">
You can generate help and additional documentation using platyPS by running the 'docs' task by pressing Ctrl+P,
then type 'task docs'. Add additional documentation written in platyPS markdown to the docs directory. You can
update the docs by running the 'docs' task again.

</message>
</content>
</plasterManifest>

0 comments on commit 8d858c1

Please sign in to comment.