Skip to content

Commit

Permalink
Fix issue with parameters getting empty string (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
johlju authored Feb 3, 2024
1 parent f99cedf commit 9943005
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 21 deletions.
137 changes: 121 additions & 16 deletions .build/tasks/Update_Bootstrap_Script.build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -39,33 +39,108 @@ param

# Synopsis: Updates the bootstrap script before deploy
task Update_Bootstrap_Script {
function Get-FunctionDefinition
function Get-FunctionDefinitionAst
{
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true)]
[System.String]
$CommandName
$CommandName,

[Parameter()]
[System.String]
$ModuleContent
)

# Get the script content
$moduleContent = (Get-Command $CommandName).Module.Definition
if (-not $PSBoundParameters.ContainsKey('ModuleContent'))
{
# Get the command script definition.
$moduleContent = (Get-Command $CommandName).Module.Definition
}

# Parse the script into an AST
$ast = [System.Management.Automation.Language.Parser]::ParseInput($moduleContent, [ref]$null, [ref]$null)
# Parse the script into an AST.
$ast = [System.Management.Automation.Language.Parser]::ParseInput($ModuleContent, [ref] $null, [ref] $null)

# Find the Start-PSResourceGetBootstrap function definition
$functionDefinition = $ast.Find({
param($node)
$astFilter = {
$args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] -and
$args[0].Name -eq $CommandName
}

return $node -is [System.Management.Automation.Language.FunctionDefinitionAst] -and
$node.Name -in $CommandName
}, $true)
$functionDefinition = $ast.Find($astFilter, $true)

return $functionDefinition
}

function Get-ParameterAst
{
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true)]
[System.Management.Automation.Language.ParamBlockAst]
$Ast,

[Parameter(Mandatory = $true)]
[System.String]
$ParameterName
)

$astFilter = {
$args[0] -is [System.Management.Automation.Language.ParameterAst] `
-and $args[0].Name.Extent.Text -eq $ParameterName
}

$parameterAst = $Ast.Find($astFilter, $false)

return $parameterAst
}

function Get-ParameterValidationAst
{
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true)]
[System.Management.Automation.Language.ParameterAst]
$Ast
)

$astFilter = {
$args[0] -is [System.Management.Automation.Language.AttributeAst] `
-and $args[0].TypeName.Name -match 'Validate'
}

$validateAttributeAst = $Ast.Find($astFilter, $false)

return $validateAttributeAst
}

function Remove-AstExtentContent
{
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true)]
[System.Management.Automation.Language.Ast]
$Ast,

[Parameter(Mandatory = $true)]
[System.String]
$Script
)

$startOffset = $Ast.Extent.StartOffset
$endOffset = $Ast.Extent.EndOffset

Write-Debug -Message "Start offset: $startOffset, End offset: $endOffset"

$beforeAst = $Script.Substring(0, $startOffset)
$afterAst = $Script.Substring($endOffset)

return $beforeAst + $afterAst
}

# Get the vales for task variables, see https://github.com/gaelcolas/Sampler#task-variables.
. Set-SamplerTaskVariable

Expand All @@ -83,10 +158,37 @@ task Update_Bootstrap_Script {
$builtBootstrapScript = $builtBootstrapScript.Replace('v#.#.#', $ModuleVersion)
$builtBootstrapScript = $builtBootstrapScript.Replace('yyyy-MM-dd', (Get-Date -Format 'yyyy-MM-dd'))

Write-Build -Color 'DarkGray' -Text "`tGet the function definition for the Start-PSResourceGetBootstrap function."
$functionDefinition = Get-FunctionDefinition -CommandName 'Start-PSResourceGetBootstrap'
Write-Build -Color 'DarkGray' -Text "`tParse the parameter block of the Start-PSResourceGetBootstrap command in the built module."

# Get the function definition of the command Start-PSResourceGetBootstrap command in the built module.
$functionDefinition = Get-FunctionDefinitionAst -CommandName 'Start-PSResourceGetBootstrap'

# Need to parse the entire module content to get the correct parameter block.
$moduleContent = $functionDefinition.Parent.Parent.Extent.Text

# Get the parameters for the Start-PSResourceGetBootstrap command.
$parameters = $functionDefinition.Body.ParamBlock.Parameters.Name

foreach ($parameter in $parameters)
{
Write-Build -Color 'DarkGray' -Text "`t`tGet the validation attributes for parameter $parameter."
$parameterAst = Get-ParameterAst -Ast $functionDefinition.Body.ParamBlock -ParameterName $parameter
$validationAttributeAst = Get-ParameterValidationAst -Ast $parameterAst

if ($validationAttributeAst -and $validationAttributeAst.TypeName.Name -eq 'ValidateScript')
{
Write-Build -Color 'DarkGray' -Text "`t`t`tRemove the validation script for parameter $parameter."
$moduleContent = Remove-AstExtentContent -Ast $validationAttributeAst -Script $moduleContent

Write-Build -Color 'DarkGray' -Text "`t`tGet the parameter block for the Start-PSResourceGetBootstrap function."
# Parse the $moduleContent result to AST to get the correct parameter block, to continue parsing parameters.
$functionDefinition = Get-FunctionDefinitionAst -CommandName 'Start-PSResourceGetBootstrap' -ModuleContent $moduleContent
}
}

# Parse the $moduleContent result to AST to get the correct parameter block.
$functionDefinition = Get-FunctionDefinitionAst -CommandName 'Start-PSResourceGetBootstrap' -ModuleContent $moduleContent

Write-Build -Color 'DarkGray' -Text "`tWrite the parameter block of the bootstrap script."
$parameterBlockString = "[CmdletBinding(DefaultParameterSetName = 'Scope')]`n" + $functionDefinition.Body.ParamBlock.Extent.Text

Write-Build -Color 'DarkGray' -Text "`tSet parameters in the bootstrap script"
Expand All @@ -102,6 +204,9 @@ task Update_Bootstrap_Script {
Write-Build -Color 'DarkGray' -Text "`tSet localization in the bootstrap script."
$builtBootstrapScript = $builtBootstrapScript.Replace('#placeholder localization', "`$script:localizedData = `n$localizationContent")

Write-Build -Color 'DarkGray' -Text "`tGet the function definition of the Start-PSResourceGetBootstrap command in the built module."
$functionDefinition = Get-FunctionDefinitionAst -CommandName 'Start-PSResourceGetBootstrap'

Write-Build -Color 'DarkGray' -Text "`tGet the comment-based help for the Start-PSResourceGetBootstrap function."
$commentBasedHelp = ($functionDefinition.GetHelpContent()).GetCommentBlock()
$functionDefinitionString = $functionDefinition.Extent.Text
Expand All @@ -124,7 +229,7 @@ task Update_Bootstrap_Script {
{
Write-Build -Color 'DarkGray' -Text "`t`tGet definition for command $Command."

$functionDefinition = Get-FunctionDefinition -CommandName $command
$functionDefinition = Get-FunctionDefinitionAst -CommandName $command

$functionDefinitionString += $functionDefinition.Extent.Text
$functionDefinitionString += "`n"
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Status badges updated.
- Update integration tests for the bootstrap script.
- Fix missing commands in bootstrap script.
- Fix bootstrap script parameter block.

### Changed

Expand Down
1 change: 1 addition & 0 deletions source/Public/Start-PSResourceGetBootstrap.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ function Start-PSResourceGetBootstrap
(
[Parameter(Mandatory = $true, ParameterSetName = 'Destination')]
[ValidateScript({
Write-Verbose -Message "Destination folder is set to '$($_)'."
Test-Path -Path $_ -PathType 'Container'
})]
[System.String]
Expand Down
32 changes: 32 additions & 0 deletions tests/Integration/Bootstrap.Integration.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,38 @@ Describe 'Bootstrap Script' -Tag 'BootstrapScript' {
}
}

Context 'When using Invoke-Expression to initiate the bootstrap script (with defaults)' {
BeforeAll {
Remove-Item -Path "$currentUserPath/$moduleName" -Recurse -Force -ErrorAction 'SilentlyContinue'
}

It 'Should have removed the downloaded module in previous tests' {
Get-Module $moduleName -ListAvailable | Where-Object -FilterScript {
$_.Path -match [System.Text.RegularExpressions.Regex]::Escape($currentUserPath)
} | Should -BeNullOrEmpty
}

It 'Should bootstrap the module to the default scope CurrentUser' {
# Must create the path first, otherwise the test will fail if it does not exist.
New-Item -Path $currentUserPath -ItemType 'Directory' -Force | Out-Null

$script = Get-Content -Path './output/bootstrap.ps1' -Raw

# Set default parameters for the command that the script runs.
$PSDefaultParameterValues['Start-PSResourceGetBootstrap:Force'] = $true
$PSDefaultParameterValues['Start-PSResourceGetBootstrap:Verbose'] = $true

{ $script | Invoke-Expression } | Should -Not -Throw

$PSDefaultParameterValues.Remove('Start-PSResourceGetBootstrap:Force')
$PSDefaultParameterValues.Remove('Start-PSResourceGetBootstrap:Verbose')

Get-Module $moduleName -ListAvailable | Where-Object -FilterScript {
$_.Path -match [System.Text.RegularExpressions.Regex]::Escape($currentUserPath)
} | Should -Not -BeNullOrEmpty
}
}

Context 'When using Destination parameter set' {
BeforeAll {
$testFolder1 = "$TestDrive/Test1"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,8 @@ Describe 'Start-PSResourceGetBootstrap' {
---> System.IO.IOException: Permission denied
#>
It 'Should bootstrap the module to the specified scope AllUsers' -Skip:$IsLinux {
$ErrorView = 'DetailedView'

{ Start-PSResourceGetBootstrap -Scope 'AllUsers' -Force -Verbose } | Should -Not -Throw

Write-Verbose -Message ('Error count: {0}' -f $Error.Count) -Verbose
Write-Verbose -Message ($Error | Out-String) -Verbose

$allUsersPath = Get-PSModulePath -Scope 'AllUsers'

Get-Module $moduleName -ListAvailable | Where-Object -FilterScript {
Expand Down

0 comments on commit 9943005

Please sign in to comment.