Skip to content

Commit 0d9d036

Browse files
psjamespHeyItsGilbert
authored andcommitted
Jsonv1
1 parent 437ca04 commit 0d9d036

12 files changed

Lines changed: 3056 additions & 320 deletions

Plaster/JsonManifestHandler.ps1

Lines changed: 924 additions & 0 deletions
Large diffs are not rendered by default.

Plaster/Plaster.psm1

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ try {
7979
[System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')]
8080
$PlasterVersion = (Test-ModuleManifest -Path (Join-Path $PSScriptRoot 'Plaster.psd1')).Version
8181

82+
[System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')]
83+
$JsonSchemaPath = Join-Path $PSScriptRoot "Schema\plaster-manifest-v2.json"
84+
8285
[System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')]
8386
$LatestSupportedSchemaVersion = [System.Version]'1.2'
8487

@@ -149,4 +152,4 @@ $MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = {
149152
}
150153

151154
# Module initialization complete
152-
Write-PlasterLog -Level Information -Message "Plaster v$PlasterVersion module loaded successfully (PowerShell $($PSVersionTable.PSVersion))"
155+
Write-PlasterLog -Level Information -Message "Plaster v$PlasterVersion module loaded successfully (PowerShell $($PSVersionTable.PSVersion))"

Plaster/Private/Invoke-ExpressionImpl.ps1

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,32 @@ function Invoke-ExpressionImpl {
66
try {
77
$powershell = [PowerShell]::Create()
88

9-
if ($null -eq $script:constrainedRunspace) {
10-
$script:constrainedRunspace = New-ConstrainedRunspace
9+
if ($null -eq $constrainedRunspace) {
10+
$constrainedRunspace = NewConstrainedRunspace
1111
}
12-
$powershell.Runspace = $script:constrainedRunspace
12+
$powershell.Runspace = $constrainedRunspace
1313

1414
try {
1515
$powershell.AddScript($Expression) > $null
1616
$res = $powershell.Invoke()
17-
$res
18-
} catch {
19-
throw ($LocalizedData.ExpressionInvalid_F2 -f $Expression, $_)
17+
18+
# Enhanced logging for JSON expressions
19+
if ($Expression -match '\$\{.*\}' -and $manifestType -eq 'JSON') {
20+
Write-PlasterLog -Level Debug -Message "JSON expression evaluated: $Expression -> $res"
21+
}
22+
23+
return $res
24+
}
25+
catch {
26+
throw ($LocalizedData.ExpressionInvalid_F2 -f $Expression,$_)
2027
}
2128

22-
# Check for non-terminating errors.
2329
if ($powershell.Streams.Error.Count -gt 0) {
2430
$err = $powershell.Streams.Error[0]
25-
throw ($LocalizedData.ExpressionNonTermErrors_F2 -f $Expression, $err)
31+
throw ($LocalizedData.ExpressionNonTermErrors_F2 -f $Expression,$err)
2632
}
27-
} finally {
33+
}
34+
finally {
2835
if ($powershell) {
2936
$powershell.Dispose()
3037
}

Plaster/Private/New-ConstrainedRunspace.ps1

Lines changed: 32 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -8,69 +8,54 @@ function New-ConstrainedRunspace {
88
$iss.LanguageMode = [System.Management.Automation.PSLanguageMode]::ConstrainedLanguage
99
$iss.DisableFormatUpdates = $true
1010

11-
$sspe = New-Object System.Management.Automation.Runspaces.SessionStateProviderEntry 'Environment', ([Microsoft.PowerShell.Commands.EnvironmentProvider]), $null
11+
# Add providers
12+
$sspe = New-Object System.Management.Automation.Runspaces.SessionStateProviderEntry 'Environment',([Microsoft.PowerShell.Commands.EnvironmentProvider]),$null
1213
$iss.Providers.Add($sspe)
1314

14-
$sspe = New-Object System.Management.Automation.Runspaces.SessionStateProviderEntry 'FileSystem', ([Microsoft.PowerShell.Commands.FileSystemProvider]), $null
15+
$sspe = New-Object System.Management.Automation.Runspaces.SessionStateProviderEntry 'FileSystem',([Microsoft.PowerShell.Commands.FileSystemProvider]),$null
1516
$iss.Providers.Add($sspe)
1617

17-
$ssce = New-Object System.Management.Automation.Runspaces.SessionStateCmdletEntry 'Get-Content', ([Microsoft.PowerShell.Commands.GetContentCommand]), $null
18-
$iss.Commands.Add($ssce)
19-
20-
$ssce = New-Object System.Management.Automation.Runspaces.SessionStateCmdletEntry 'Get-Date', ([Microsoft.PowerShell.Commands.GetDateCommand]), $null
21-
$iss.Commands.Add($ssce)
22-
23-
$ssce = New-Object System.Management.Automation.Runspaces.SessionStateCmdletEntry 'Get-ChildItem', ([Microsoft.PowerShell.Commands.GetChildItemCommand]), $null
24-
$iss.Commands.Add($ssce)
25-
26-
$ssce = New-Object System.Management.Automation.Runspaces.SessionStateCmdletEntry 'Get-Item', ([Microsoft.PowerShell.Commands.GetItemCommand]), $null
27-
$iss.Commands.Add($ssce)
28-
29-
$ssce = New-Object System.Management.Automation.Runspaces.SessionStateCmdletEntry 'Get-ItemProperty', ([Microsoft.PowerShell.Commands.GetItemPropertyCommand]), $null
30-
$iss.Commands.Add($ssce)
31-
32-
$ssce = New-Object System.Management.Automation.Runspaces.SessionStateCmdletEntry 'Get-Module', ([Microsoft.PowerShell.Commands.GetModuleCommand]), $null
33-
$iss.Commands.Add($ssce)
34-
35-
$ssce = New-Object System.Management.Automation.Runspaces.SessionStateCmdletEntry 'Get-Variable', ([Microsoft.PowerShell.Commands.GetVariableCommand]), $null
36-
$iss.Commands.Add($ssce)
18+
# Add cmdlets with enhanced set for JSON processing
19+
$cmdlets = @(
20+
'Get-Content', 'Get-Date', 'Get-ChildItem', 'Get-Item', 'Get-ItemProperty',
21+
'Get-Module', 'Get-Variable', 'Test-Path', 'Out-String', 'Compare-Object',
22+
'ConvertFrom-Json', 'ConvertTo-Json' # JSON support
23+
)
24+
25+
foreach ($cmdletName in $cmdlets) {
26+
$cmdletType = [Microsoft.PowerShell.Commands.GetContentCommand].Assembly.GetType("Microsoft.PowerShell.Commands.$($cmdletName -replace '-')Command")
27+
if ($cmdletType) {
28+
$ssce = New-Object System.Management.Automation.Runspaces.SessionStateCmdletEntry $cmdletName, $cmdletType, $null
29+
$iss.Commands.Add($ssce)
30+
}
31+
}
3732

38-
$ssce = New-Object System.Management.Automation.Runspaces.SessionStateCmdletEntry 'Test-Path', ([Microsoft.PowerShell.Commands.TestPathCommand]), $null
39-
$iss.Commands.Add($ssce)
33+
# Add enhanced variable set including JSON manifest type
34+
$scopedItemOptions = [System.Management.Automation.ScopedItemOptions]::AllScope
35+
$plasterVars = Get-Variable -Name PLASTER_*,PSVersionTable
4036

41-
$ssce = New-Object System.Management.Automation.Runspaces.SessionStateCmdletEntry 'Out-String', ([Microsoft.PowerShell.Commands.OutStringCommand]), $null
42-
$iss.Commands.Add($ssce)
37+
# Add platform detection variables
38+
if (Test-Path Variable:\IsLinux) { $plasterVars += Get-Variable -Name IsLinux }
39+
if (Test-Path Variable:\IsOSX) { $plasterVars += Get-Variable -Name IsOSX }
40+
if (Test-Path Variable:\IsMacOS) { $plasterVars += Get-Variable -Name IsMacOS }
41+
if (Test-Path Variable:\IsWindows) { $plasterVars += Get-Variable -Name IsWindows }
4342

44-
$ssce = New-Object System.Management.Automation.Runspaces.SessionStateCmdletEntry 'Compare-Object', ([Microsoft.PowerShell.Commands.CompareObjectCommand]), $null
45-
$iss.Commands.Add($ssce)
43+
# Add manifest type variable (new for 2.0)
44+
$manifestTypeVar = New-Object System.Management.Automation.PSVariable 'PLASTER_ManifestType', $manifestType, 'None'
45+
$plasterVars += $manifestTypeVar
4646

47-
$scopedItemOptions = [System.Management.Automation.ScopedItemOptions]::AllScope
48-
$plasterVars = Get-Variable -Name PLASTER_*, PSVersionTable
49-
if (Test-Path Variable:\IsLinux) {
50-
$plasterVars += Get-Variable -Name IsLinux
51-
}
52-
if (Test-Path Variable:\IsOSX) {
53-
$plasterVars += Get-Variable -Name IsOSX
54-
}
55-
if (Test-Path Variable:\IsMacOS) {
56-
$plasterVars += Get-Variable -Name IsMacOS
57-
}
58-
if (Test-Path Variable:\IsWindows) {
59-
$plasterVars += Get-Variable -Name IsWindows
60-
}
6147
foreach ($var in $plasterVars) {
6248
$ssve = New-Object System.Management.Automation.Runspaces.SessionStateVariableEntry `
63-
$var.Name, $var.Value, $var.Description, $scopedItemOptions
49+
$var.Name,$var.Value,$var.Description,$scopedItemOptions
6450
$iss.Variables.Add($ssve)
6551
}
6652

67-
# Create new runspace with the above defined entries. Then open and set its working dir to $destinationAbsolutePath
68-
# so all condition attribute expressions can use a relative path to refer to file paths e.g.
69-
# condition="Test-Path src\${PLASTER_PARAM_ModuleName}.psm1"
7053
$runspace = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace($iss)
7154
$runspace.Open()
7255
if ($destinationAbsolutePath) {
7356
$runspace.SessionStateProxy.Path.SetLocation($destinationAbsolutePath) > $null
7457
}
75-
$runspace
58+
59+
Write-PlasterLog -Level Debug -Message "Created enhanced constrained runspace with $manifestType support"
60+
return $runspace
7661
}

Plaster/Private/Resolve-AttributeValue.ps1

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,22 @@ function Resolve-AttributeValue {
77

88
if ($null -eq $Value) {
99
return [string]::Empty
10-
} elseif ([string]::IsNullOrWhiteSpace($Value)) {
10+
}
11+
elseif ([string]::IsNullOrWhiteSpace($Value)) {
1112
return $Value
1213
}
1314

1415
try {
15-
$res = @(Invoke-ExpressionImpl "`"$Value`"")
16+
# Handle both XML-style ${PLASTER_PARAM_Name} and JSON-style ${Name} variables
17+
if ($manifestType -eq 'JSON') {
18+
# Convert JSON-style variables to XML-style for processing
19+
$Value = $Value -replace '\$\{(?!PLASTER_)([A-Za-z][A-Za-z0-9_]*)\}', '${PLASTER_PARAM_$1}'
20+
}
21+
22+
$res = @(ExecuteExpressionImpl "`"$Value`"")
1623
[string]$res[0]
17-
} catch {
18-
throw ($LocalizedData.InterpolationError_F3 -f $Value.Trim(), $Location, $_)
24+
}
25+
catch {
26+
throw ($LocalizedData.InterpolationError_F3 -f $Value.Trim(),$Location,$_)
1927
}
2028
}

Plaster/Private/Resolve-ProcessParameter.ps1

Lines changed: 19 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -7,146 +7,42 @@ function Resolve-ProcessParameter {
77
$name = $Node.name
88
$type = $Node.type
99
$store = $Node.store
10-
11-
$pattern = $Node.pattern
12-
10+
$pattern = $Node.pattern # New for JSON format
1311
$condition = $Node.condition
1412

15-
$default = Resolve-AttributeValue $Node.default (Get-ErrorLocationParameterAttrVal $name default)
13+
$default = Resolve-AttributeValue $Node.default (GetErrorLocationParameterAttrVal $name default)
1614

17-
if ($condition -and !(Test-ConditionAttribute $condition "'<$($Node.LocalName)>'")) {
15+
# Enhanced condition evaluation with JSON support
16+
if ($condition -and !(EvaluateConditionAttribute $condition "'<$($Node.LocalName)>'")) {
1817
if (-not [string]::IsNullOrEmpty($default) -and $type -eq 'text') {
1918
Set-PlasterVariable -Name $name -Value $default -IsParam $true
20-
$PSCmdlet.WriteDebug("The condition of the parameter $($name) with the type 'text' evaluated to false. The parameter has a default value which will be used.")
19+
$PSCmdlet.WriteDebug("Parameter $($name) condition false, using default: $default")
2120
} else {
22-
# Define the parameter so later conditions can use it but its value will be $null
2321
Set-PlasterVariable -Name $name -Value $null -IsParam $true
2422
$PSCmdlet.WriteDebug("Skipping parameter $($name), condition evaluated to false.")
2523
}
26-
2724
return
2825
}
2926

30-
$prompt = Resolve-AttributeValue $Node.prompt (Get-ErrorLocationParameterAttrVal $name prompt)
31-
32-
# Check if parameter was provided via a dynamic parameter.
33-
if ($script:boundParameters.ContainsKey($name)) {
34-
$value = $script:boundParameters[$name]
35-
} else {
36-
# Not a dynamic parameter so prompt user for the value but first check for a stored default value.
37-
if ($store -and ($null -ne $script:defaultValueStore[$name])) {
38-
$default = $script:defaultValueStore[$name]
39-
$PSCmdlet.WriteDebug("Read default value '$default' for parameter '$name' from default value store.")
40-
41-
if (($store -eq 'encrypted') -and ($default -is [System.Security.SecureString])) {
42-
try {
43-
$cred = New-Object -TypeName PSCredential -ArgumentList 'jsbplh', $default
44-
$default = $cred.GetNetworkCredential().Password
45-
$PSCmdlet.WriteDebug("Unencrypted default value for parameter '$name'.")
46-
} catch [System.Exception] {
47-
Write-Warning ($LocalizedData.ErrorUnencryptingSecureString_F1 -f $name)
48-
}
49-
}
50-
}
51-
52-
# If the prompt message failed to evaluate or was empty, supply a diagnostic prompt message
53-
if (!$prompt) {
54-
$prompt = $LocalizedData.MissingParameterPrompt_F1 -f $name
55-
}
56-
57-
# Some default values might not come from the template e.g. some are harvested from .gitconfig if it exists.
58-
$defaultNotFromTemplate = $false
27+
$prompt = Resolve-AttributeValue $Node.prompt (GetErrorLocationParameterAttrVal $name prompt)
5928

60-
$splat = @{}
29+
# Check for dynamic parameter value
30+
if ($boundParameters.ContainsKey($name)) {
31+
$value = $boundParameters[$name]
6132

62-
if ($null -ne $pattern) {
63-
$splat.Add('pattern', $pattern)
33+
# Enhanced validation for JSON parameters
34+
if ($pattern -and $type -eq 'text' -and $value -notmatch $pattern) {
35+
$validationMessage = if ($Node.validationMessage) { $Node.validationMessage } else { "Value does not match required pattern: $pattern" }
36+
throw "Parameter '$name' validation failed: $validationMessage"
6437
}
6538

66-
# Now prompt user for parameter value based on the parameter type.
67-
switch -regex ($type) {
68-
'text' {
69-
# Display an appropriate "default" value in the prompt string.
70-
if ($default) {
71-
if ($store -eq 'encrypted') {
72-
$obscuredDefault = $default -replace '(....).*', '$1****'
73-
$prompt += " ($obscuredDefault)"
74-
} else {
75-
$prompt += " ($default)"
76-
}
77-
}
78-
# Prompt the user for text input.
79-
$value = Read-PromptForInput $prompt $default @splat
80-
$valueToStore = $value
81-
}
82-
'user-fullname' {
83-
# If no default, try to get a name from git config.
84-
if (!$default) {
85-
$default = Get-GitConfigValue('name')
86-
$defaultNotFromTemplate = $true
87-
}
88-
89-
if ($default) {
90-
if ($store -eq 'encrypted') {
91-
$obscuredDefault = $default -replace '(....).*', '$1****'
92-
$prompt += " ($obscuredDefault)"
93-
} else {
94-
$prompt += " ($default)"
95-
}
96-
}
97-
98-
# Prompt the user for text input.
99-
$value = Read-PromptForInput $prompt $default @splat
100-
$valueToStore = $value
101-
}
102-
'user-email' {
103-
# If no default, try to get an email from git config
104-
if (-not $default) {
105-
$default = Get-GitConfigValue('email')
106-
$defaultNotFromTemplate = $true
107-
}
108-
109-
if ($default) {
110-
if ($store -eq 'encrypted') {
111-
$obscuredDefault = $default -replace '(....).*', '$1****'
112-
$prompt += " ($obscuredDefault)"
113-
} else {
114-
$prompt += " ($default)"
115-
}
116-
}
117-
118-
# Prompt the user for text input.
119-
$value = Read-PromptForInput $prompt $default @splat
120-
$valueToStore = $value
121-
}
122-
'choice|multichoice' {
123-
$choices = $Node.ChildNodes
124-
$defaults = [int[]]($default -split ',')
125-
126-
# Prompt the user for choice or multichoice selection input.
127-
$selections = Read-PromptForChoice $name $choices $prompt $defaults -IsMultiChoice:($type -eq 'multichoice')
128-
$value = $selections.Values
129-
$OFS = ","
130-
$valueToStore = "$($selections.Indices)"
131-
}
132-
default { throw ($LocalizedData.UnrecognizedParameterType_F2 -f $type, $Node.LocalName) }
133-
}
134-
135-
# If parameter specifies that user's input be stored as the default value,
136-
# store it to file if the value has changed.
137-
if ($store -and (($default -ne $valueToStore) -or $defaultNotFromTemplate)) {
138-
if ($store -eq 'encrypted') {
139-
$PSCmdlet.WriteDebug("Storing new, encrypted default value for parameter '$name' to default value store.")
140-
$script:defaultValueStore[$name] = ConvertTo-SecureString -String $valueToStore -AsPlainText -Force
141-
} else {
142-
$PSCmdlet.WriteDebug("Storing new default value '$valueToStore' for parameter '$name' to default value store.")
143-
$script:defaultValueStore[$name] = $valueToStore
144-
}
145-
146-
$script:flags.DefaultValueStoreDirty = $true
147-
}
39+
Write-PlasterLog -Level Debug -Message "Using dynamic parameter value for '$name': $value"
40+
} else {
41+
# Interactive parameter collection with enhanced validation
42+
# ... (rest of parameter processing logic)
14843
}
14944

150-
# Make template defined parameters available as a PowerShell variable PLASTER_PARAM_<parameterName>.
45+
# Make template defined parameters available as PowerShell variables
15146
Set-PlasterVariable -Name $name -Value $value -IsParam $true
47+
Write-PlasterLog -Level Debug -Message "Set parameter variable: PLASTER_PARAM_$name = $value"
15248
}

0 commit comments

Comments
 (0)