From f6c369b4091dd6efc1b10350a35fb5d837c9643e Mon Sep 17 00:00:00 2001 From: jdarcyryan Date: Tue, 3 Feb 2026 22:11:57 +0000 Subject: [PATCH 01/13] start to documentation bot --- .build/scripts/Write-PSModuleDocs.ps1 | 109 ++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 .build/scripts/Write-PSModuleDocs.ps1 diff --git a/.build/scripts/Write-PSModuleDocs.ps1 b/.build/scripts/Write-PSModuleDocs.ps1 new file mode 100644 index 0000000..9984bb2 --- /dev/null +++ b/.build/scripts/Write-PSModuleDocs.ps1 @@ -0,0 +1,109 @@ +function ConvertTo-HelpMarkdown { + param( + [Parameter(Mandatory, ValueFromPipeline)] + [Management.Automation.FunctionInfo] + $Command + ) + + begin { +# $gitRoot = Resolve-Path -Path "$PSScriptRoot\..\.." +# $moduleName = Split-Path -Path $gitRoot -Leaf +# $outputModulePath = "$gitRoot\.output\$moduleName" +# +# # Verify module has built +# if (-not (Test-Path -Path $outputModulePath -PathType Container)) { +# throw "Module directory not found at: '$outputModulePath', run 'make build' to build the module." +# } +# else { +# try { +# # Test module import before tests +# Import-Module $outputModulePath +# } +# catch { +# throw "Built module could not be imported at: '$outputModulePath', please run 'make build' to rebuild the module." +# } +# finally { +# Get-Module | where Path -like "$outputModulePath\*" | Remove-Module +# } +# } +# +# # Get module and alias' +# $module = Get-Module $outputModulePath + $allAlias = Get-Alias + } + + process { + $help = Get-Help $Command -Full + $parameters = $help.parameters.parameter + $examples = $help.examples.example + + $alias = @(($allAlias | where ResolvedCommandName -eq $command.Name).Name) + + @" +# $($command.Name)$(if ($alias) {" ($($alias.Name -join ', '))"}) + +## Synopsis + +$($help.Synopsis) + +## Description + +$($help.Description.Text) + +## Syntax + +$('```')powershell +$(($help.Syntax | Out-String).Trim()) +$('```') + +$(if ($parameters){ + @" +## Parameters + +$( + $parameters | foreach { + @" +### -$($_.Name) + +$($_.Description.Text) + +- **Type**: $($_.Type.Name) +- **Required**: $($_.Required) +- **Position**: $($_.Position) +- **Default value**: $( + if ($_.defaultValue) { + $_.defaultValue + } + else { + 'None' + } +) +- **Accepts pipeline input**: $($_.pipelineInput) +"@ + } +) + +"@ +}) +## Examples + +$($examples | foreach {$i = 1} { + @" +### Example $i + +$($($_.remarks.Text -join '').Trim()) + +$('```')powershell +$($_.code) +$('```') + +"@ + $i++ +}) +"@ + } + + end { + # remove module + } +} From 41ea876761cb03e5eeaea01a40c0bada90573457 Mon Sep 17 00:00:00 2001 From: jdarcyryan Date: Tue, 3 Feb 2026 22:29:10 +0000 Subject: [PATCH 02/13] updates --- .build/scripts/Write-PSModuleDocs.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.build/scripts/Write-PSModuleDocs.ps1 b/.build/scripts/Write-PSModuleDocs.ps1 index 9984bb2..cd9fc91 100644 --- a/.build/scripts/Write-PSModuleDocs.ps1 +++ b/.build/scripts/Write-PSModuleDocs.ps1 @@ -53,7 +53,7 @@ $($help.Description.Text) ## Syntax $('```')powershell -$(($help.Syntax | Out-String).Trim()) +$((Out-String -InputObject $help.Syntax).Trim()) $('```') $(if ($parameters){ From 506b363b9c85dfe02de9eb18680d6a3996cf9435 Mon Sep 17 00:00:00 2001 From: jdarcyryan Date: Sat, 21 Feb 2026 21:00:53 +0000 Subject: [PATCH 03/13] working doc writer --- .build/scripts/Write-PSModuleDocs.ps1 | 179 +++++++++++++------------- 1 file changed, 88 insertions(+), 91 deletions(-) diff --git a/.build/scripts/Write-PSModuleDocs.ps1 b/.build/scripts/Write-PSModuleDocs.ps1 index cd9fc91..6ab917a 100644 --- a/.build/scripts/Write-PSModuleDocs.ps1 +++ b/.build/scripts/Write-PSModuleDocs.ps1 @@ -6,104 +6,101 @@ function ConvertTo-HelpMarkdown { ) begin { -# $gitRoot = Resolve-Path -Path "$PSScriptRoot\..\.." -# $moduleName = Split-Path -Path $gitRoot -Leaf -# $outputModulePath = "$gitRoot\.output\$moduleName" -# -# # Verify module has built -# if (-not (Test-Path -Path $outputModulePath -PathType Container)) { -# throw "Module directory not found at: '$outputModulePath', run 'make build' to build the module." -# } -# else { -# try { -# # Test module import before tests -# Import-Module $outputModulePath -# } -# catch { -# throw "Built module could not be imported at: '$outputModulePath', please run 'make build' to rebuild the module." -# } -# finally { -# Get-Module | where Path -like "$outputModulePath\*" | Remove-Module -# } -# } -# -# # Get module and alias' -# $module = Get-Module $outputModulePath $allAlias = Get-Alias } process { $help = Get-Help $Command -Full - $parameters = $help.parameters.parameter - $examples = $help.examples.example - - $alias = @(($allAlias | where ResolvedCommandName -eq $command.Name).Name) - - @" -# $($command.Name)$(if ($alias) {" ($($alias.Name -join ', '))"}) - -## Synopsis - -$($help.Synopsis) - -## Description - -$($help.Description.Text) - -## Syntax - -$('```')powershell -$((Out-String -InputObject $help.Syntax).Trim()) -$('```') - -$(if ($parameters){ - @" -## Parameters - -$( - $parameters | foreach { - @" -### -$($_.Name) -$($_.Description.Text) + # If there's no real help documentation, return null + if (-not $help -or ( + (-not $help.Description) -and + (-not $help.examples) -and + ($help.Synopsis.Trim() -eq $command.Name -or -not $help.Synopsis) + )) { + return $null + } -- **Type**: $($_.Type.Name) -- **Required**: $($_.Required) -- **Position**: $($_.Position) -- **Default value**: $( - if ($_.defaultValue) { - $_.defaultValue - } - else { - 'None' - } -) -- **Accepts pipeline input**: $($_.pipelineInput) -"@ - } -) - -"@ -}) -## Examples - -$($examples | foreach {$i = 1} { - @" -### Example $i - -$($($_.remarks.Text -join '').Trim()) - -$('```')powershell -$($_.code) -$('```') - -"@ - $i++ -}) -"@ + $parameters = $help.parameters.parameter + $examples = $help.examples.example + $alias = @(($allAlias | Where-Object ResolvedCommandName -eq $command.Name).Name) + + $sections = [Collections.Generic.List[string]]::new() + + # Header + $header = "# $($command.Name)" + if ($alias) { $header += " ($($alias -join ', '))" } + $sections.Add($header) + + # Synopsis + if ($help.Synopsis -and $help.Synopsis.Trim() -ne $command.Name) { + $sections.Add("## Synopsis`n`n$($help.Synopsis)") + } + + # Description + if ($help.Description.Text) { + $sections.Add("## Description`n`n$($help.Description.Text)") + } + + # Syntax + $syntaxText = (Out-String -InputObject $help.Syntax).Trim() + if ($syntaxText) { + $sections.Add("## Syntax`n`n``````powershell`n$syntaxText`n``````") + } + + # Parameters + if ($parameters) { + $paramLines = [Collections.Generic.List[string]]::new() + $paramLines.Add("## Parameters") + + foreach ($param in $parameters) { + $paramSection = "### -$($param.Name)" + + if ($param.Description.Text) { + $paramSection += "`n`n$($param.Description.Text)" + } + + $bullets = [Collections.Generic.List[string]]::new() + if ($param.Type.Name) { $bullets.Add("- **Type**: $($param.Type.Name)") } + if ($param.Required) { $bullets.Add("- **Required**: $($param.Required)") } + if ($param.Position) { $bullets.Add("- **Position**: $($param.Position)") } + $bullets.Add("- **Default value**: $(if ($param.defaultValue) { $param.defaultValue } else { 'None' })") + if ($param.pipelineInput) { $bullets.Add("- **Accepts pipeline input**: $($param.pipelineInput)") } + + $paramSection += "`n`n$($bullets -join "`n")" + $paramLines.Add($paramSection) + } + + $sections.Add($paramLines -join "`n`n") + } + + # Examples + if ($examples) { + $exampleLines = [Collections.Generic.List[string]]::new() + $exampleLines.Add("## Examples") + + $i = 1 + foreach ($example in $examples) { + $exampleSection = "### Example $i" + + $remarksText = ($example.remarks.Text -join '').Trim() + if ($remarksText) { + $exampleSection += "`n`n$remarksText" + } + + if ($example.code) { + $exampleSection += "`n`n``````powershell`n$($example.code)`n``````" + } + + $exampleLines.Add($exampleSection) + $i++ + } + + $sections.Add($exampleLines -join "`n`n") + } + + $sections -join "`n`n" } - end { - # remove module - } + end {} } From 7f2c1ff165c236adede3b07d4cd2c78a89aa4f3d Mon Sep 17 00:00:00 2001 From: jdarcyryan Date: Sat, 21 Feb 2026 21:07:00 +0000 Subject: [PATCH 04/13] output fixed --- .build/scripts/Write-PSModuleDocs.ps1 | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/.build/scripts/Write-PSModuleDocs.ps1 b/.build/scripts/Write-PSModuleDocs.ps1 index 6ab917a..57ee4e6 100644 --- a/.build/scripts/Write-PSModuleDocs.ps1 +++ b/.build/scripts/Write-PSModuleDocs.ps1 @@ -43,11 +43,24 @@ function ConvertTo-HelpMarkdown { } # Syntax - $syntaxText = (Out-String -InputObject $help.Syntax).Trim() + # Syntax + $syntaxLines = foreach ($paramSet in $Command.ParameterSets) { + $paramStrings = foreach ($param in $paramSet.Parameters | Where-Object { $_.Name -notin [System.Management.Automation.Cmdlet]::CommonParameters }) { + $typeName = if ($param.ParameterType -ne [switch]) { " <$($param.ParameterType.Name)>" } else { '' } + $token = if ($param.Position -ge 0) { + "[-$($param.Name)$typeName]" | ForEach-Object { if ($param.IsMandatory) { "[-$($param.Name)$typeName]" } else { "[[-$($param.Name)$typeName]]" } } + } else { + if ($param.IsMandatory) { "-$($param.Name)$typeName" } else { "[-$($param.Name)$typeName]" } + } + $token + } + "$($Command.Name) $($paramStrings -join ' ')" + } + $syntaxText = ($syntaxLines -join "`n").Trim() if ($syntaxText) { $sections.Add("## Syntax`n`n``````powershell`n$syntaxText`n``````") } - + # Parameters if ($parameters) { $paramLines = [Collections.Generic.List[string]]::new() From 636450d91bb650454d55a1fcb7c01519489f8f06 Mon Sep 17 00:00:00 2001 From: jdarcyryan Date: Sat, 21 Feb 2026 21:13:12 +0000 Subject: [PATCH 05/13] added formatting --- .build/scripts/Write-PSModuleDocs.ps1 | 58 +++++++++++++++++++++------ 1 file changed, 45 insertions(+), 13 deletions(-) diff --git a/.build/scripts/Write-PSModuleDocs.ps1 b/.build/scripts/Write-PSModuleDocs.ps1 index 57ee4e6..baba07e 100644 --- a/.build/scripts/Write-PSModuleDocs.ps1 +++ b/.build/scripts/Write-PSModuleDocs.ps1 @@ -23,13 +23,15 @@ function ConvertTo-HelpMarkdown { $parameters = $help.parameters.parameter $examples = $help.examples.example - $alias = @(($allAlias | Where-Object ResolvedCommandName -eq $command.Name).Name) + $alias = @(($allAlias | where ResolvedCommandName -eq $command.Name).Name) $sections = [Collections.Generic.List[string]]::new() # Header $header = "# $($command.Name)" - if ($alias) { $header += " ($($alias -join ', '))" } + if ($alias) { + $header += " ($($alias -join ', '))" + } $sections.Add($header) # Synopsis @@ -42,15 +44,29 @@ function ConvertTo-HelpMarkdown { $sections.Add("## Description`n`n$($help.Description.Text)") } - # Syntax # Syntax $syntaxLines = foreach ($paramSet in $Command.ParameterSets) { - $paramStrings = foreach ($param in $paramSet.Parameters | Where-Object { $_.Name -notin [System.Management.Automation.Cmdlet]::CommonParameters }) { - $typeName = if ($param.ParameterType -ne [switch]) { " <$($param.ParameterType.Name)>" } else { '' } + $paramStrings = foreach ($param in $paramSet.Parameters | where Name -notin [Management.Automation.Cmdlet]::CommonParameters) { + $typeName = if ($param.ParameterType -ne [switch]) { + " <$($param.ParameterType.Name)>" + } + else { + '' + } $token = if ($param.Position -ge 0) { - "[-$($param.Name)$typeName]" | ForEach-Object { if ($param.IsMandatory) { "[-$($param.Name)$typeName]" } else { "[[-$($param.Name)$typeName]]" } } - } else { - if ($param.IsMandatory) { "-$($param.Name)$typeName" } else { "[-$($param.Name)$typeName]" } + "[-$($param.Name)$typeName]" | foreach { + if ($param.IsMandatory) { + "[-$($param.Name)$typeName]" + } + else { + "[[-$($param.Name)$typeName]]" + } + } + } + else { + if ($param.IsMandatory) { + "-$($param.Name)$typeName" } else { "[-$($param.Name)$typeName]" + } } $token } @@ -74,11 +90,27 @@ function ConvertTo-HelpMarkdown { } $bullets = [Collections.Generic.List[string]]::new() - if ($param.Type.Name) { $bullets.Add("- **Type**: $($param.Type.Name)") } - if ($param.Required) { $bullets.Add("- **Required**: $($param.Required)") } - if ($param.Position) { $bullets.Add("- **Position**: $($param.Position)") } - $bullets.Add("- **Default value**: $(if ($param.defaultValue) { $param.defaultValue } else { 'None' })") - if ($param.pipelineInput) { $bullets.Add("- **Accepts pipeline input**: $($param.pipelineInput)") } + + if ($param.Type.Name){ + $bullets.Add("- **Type**: $($param.Type.Name)") + } + if ($param.Required) { + $bullets.Add("- **Required**: $($param.Required)") + } + if ($param.Position) { + $bullets.Add("- **Position**: $($param.Position)") + } + + $bullets.Add("- **Default value**: $(if ($param.defaultValue){ + $param.defaultValue + } + else { + 'None' + })") + + if ($param.pipelineInput) { + $bullets.Add("- **Accepts pipeline input**: $($param.pipelineInput)") + } $paramSection += "`n`n$($bullets -join "`n")" $paramLines.Add($paramSection) From a288d7188e2aeb7af2e0024562e08b9a5f45aa8c Mon Sep 17 00:00:00 2001 From: jdarcyryan Date: Sat, 21 Feb 2026 21:21:14 +0000 Subject: [PATCH 06/13] remove synopsis if defaulted to syntax --- .build/scripts/Write-PSModuleDocs.ps1 | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/.build/scripts/Write-PSModuleDocs.ps1 b/.build/scripts/Write-PSModuleDocs.ps1 index baba07e..c3bb2ad 100644 --- a/.build/scripts/Write-PSModuleDocs.ps1 +++ b/.build/scripts/Write-PSModuleDocs.ps1 @@ -35,7 +35,7 @@ function ConvertTo-HelpMarkdown { $sections.Add($header) # Synopsis - if ($help.Synopsis -and $help.Synopsis.Trim() -ne $command.Name) { + if ($help.Synopsis -and $help.Synopsis.Trim() -notlike "$($command.Name)*") { $sections.Add("## Synopsis`n`n$($help.Synopsis)") } @@ -49,7 +49,7 @@ function ConvertTo-HelpMarkdown { $paramStrings = foreach ($param in $paramSet.Parameters | where Name -notin [Management.Automation.Cmdlet]::CommonParameters) { $typeName = if ($param.ParameterType -ne [switch]) { " <$($param.ParameterType.Name)>" - } + } else { '' } @@ -65,7 +65,10 @@ function ConvertTo-HelpMarkdown { } else { if ($param.IsMandatory) { - "-$($param.Name)$typeName" } else { "[-$($param.Name)$typeName]" + "-$($param.Name)$typeName" + } + else { + "[-$($param.Name)$typeName]" } } $token @@ -76,11 +79,11 @@ function ConvertTo-HelpMarkdown { if ($syntaxText) { $sections.Add("## Syntax`n`n``````powershell`n$syntaxText`n``````") } - + # Parameters if ($parameters) { $paramLines = [Collections.Generic.List[string]]::new() - $paramLines.Add("## Parameters") + $paramLines.Add('## Parameters') foreach ($param in $parameters) { $paramSection = "### -$($param.Name)" @@ -91,7 +94,7 @@ function ConvertTo-HelpMarkdown { $bullets = [Collections.Generic.List[string]]::new() - if ($param.Type.Name){ + if ($param.Type.Name) { $bullets.Add("- **Type**: $($param.Type.Name)") } if ($param.Required) { @@ -101,7 +104,7 @@ function ConvertTo-HelpMarkdown { $bullets.Add("- **Position**: $($param.Position)") } - $bullets.Add("- **Default value**: $(if ($param.defaultValue){ + $bullets.Add("- **Default value**: $(if ($param.defaultValue) { $param.defaultValue } else { @@ -122,7 +125,7 @@ function ConvertTo-HelpMarkdown { # Examples if ($examples) { $exampleLines = [Collections.Generic.List[string]]::new() - $exampleLines.Add("## Examples") + $exampleLines.Add('## Examples') $i = 1 foreach ($example in $examples) { From 94ff4e12b7d23e7e7436400aa46a0f88e473c1d0 Mon Sep 17 00:00:00 2001 From: jdarcyryan Date: Sat, 21 Feb 2026 21:48:56 +0000 Subject: [PATCH 07/13] overarching function + temp backup of individual help function --- .build/scripts/ConvertTo-HelpMarkdown.ps1 | 154 +++++++++++ .build/scripts/Write-PSModuleDocs.ps1 | 305 ++++++++++++++-------- 2 files changed, 346 insertions(+), 113 deletions(-) create mode 100644 .build/scripts/ConvertTo-HelpMarkdown.ps1 diff --git a/.build/scripts/ConvertTo-HelpMarkdown.ps1 b/.build/scripts/ConvertTo-HelpMarkdown.ps1 new file mode 100644 index 0000000..c3bb2ad --- /dev/null +++ b/.build/scripts/ConvertTo-HelpMarkdown.ps1 @@ -0,0 +1,154 @@ +function ConvertTo-HelpMarkdown { + param( + [Parameter(Mandatory, ValueFromPipeline)] + [Management.Automation.FunctionInfo] + $Command + ) + + begin { + $allAlias = Get-Alias + } + + process { + $help = Get-Help $Command -Full + + # If there's no real help documentation, return null + if (-not $help -or ( + (-not $help.Description) -and + (-not $help.examples) -and + ($help.Synopsis.Trim() -eq $command.Name -or -not $help.Synopsis) + )) { + return $null + } + + $parameters = $help.parameters.parameter + $examples = $help.examples.example + $alias = @(($allAlias | where ResolvedCommandName -eq $command.Name).Name) + + $sections = [Collections.Generic.List[string]]::new() + + # Header + $header = "# $($command.Name)" + if ($alias) { + $header += " ($($alias -join ', '))" + } + $sections.Add($header) + + # Synopsis + if ($help.Synopsis -and $help.Synopsis.Trim() -notlike "$($command.Name)*") { + $sections.Add("## Synopsis`n`n$($help.Synopsis)") + } + + # Description + if ($help.Description.Text) { + $sections.Add("## Description`n`n$($help.Description.Text)") + } + + # Syntax + $syntaxLines = foreach ($paramSet in $Command.ParameterSets) { + $paramStrings = foreach ($param in $paramSet.Parameters | where Name -notin [Management.Automation.Cmdlet]::CommonParameters) { + $typeName = if ($param.ParameterType -ne [switch]) { + " <$($param.ParameterType.Name)>" + } + else { + '' + } + $token = if ($param.Position -ge 0) { + "[-$($param.Name)$typeName]" | foreach { + if ($param.IsMandatory) { + "[-$($param.Name)$typeName]" + } + else { + "[[-$($param.Name)$typeName]]" + } + } + } + else { + if ($param.IsMandatory) { + "-$($param.Name)$typeName" + } + else { + "[-$($param.Name)$typeName]" + } + } + $token + } + "$($Command.Name) $($paramStrings -join ' ')" + } + $syntaxText = ($syntaxLines -join "`n").Trim() + if ($syntaxText) { + $sections.Add("## Syntax`n`n``````powershell`n$syntaxText`n``````") + } + + # Parameters + if ($parameters) { + $paramLines = [Collections.Generic.List[string]]::new() + $paramLines.Add('## Parameters') + + foreach ($param in $parameters) { + $paramSection = "### -$($param.Name)" + + if ($param.Description.Text) { + $paramSection += "`n`n$($param.Description.Text)" + } + + $bullets = [Collections.Generic.List[string]]::new() + + if ($param.Type.Name) { + $bullets.Add("- **Type**: $($param.Type.Name)") + } + if ($param.Required) { + $bullets.Add("- **Required**: $($param.Required)") + } + if ($param.Position) { + $bullets.Add("- **Position**: $($param.Position)") + } + + $bullets.Add("- **Default value**: $(if ($param.defaultValue) { + $param.defaultValue + } + else { + 'None' + })") + + if ($param.pipelineInput) { + $bullets.Add("- **Accepts pipeline input**: $($param.pipelineInput)") + } + + $paramSection += "`n`n$($bullets -join "`n")" + $paramLines.Add($paramSection) + } + + $sections.Add($paramLines -join "`n`n") + } + + # Examples + if ($examples) { + $exampleLines = [Collections.Generic.List[string]]::new() + $exampleLines.Add('## Examples') + + $i = 1 + foreach ($example in $examples) { + $exampleSection = "### Example $i" + + $remarksText = ($example.remarks.Text -join '').Trim() + if ($remarksText) { + $exampleSection += "`n`n$remarksText" + } + + if ($example.code) { + $exampleSection += "`n`n``````powershell`n$($example.code)`n``````" + } + + $exampleLines.Add($exampleSection) + $i++ + } + + $sections.Add($exampleLines -join "`n`n") + } + + $sections -join "`n`n" + } + + end {} +} diff --git a/.build/scripts/Write-PSModuleDocs.ps1 b/.build/scripts/Write-PSModuleDocs.ps1 index c3bb2ad..9ef7794 100644 --- a/.build/scripts/Write-PSModuleDocs.ps1 +++ b/.build/scripts/Write-PSModuleDocs.ps1 @@ -1,154 +1,233 @@ -function ConvertTo-HelpMarkdown { - param( - [Parameter(Mandatory, ValueFromPipeline)] - [Management.Automation.FunctionInfo] - $Command - ) - - begin { - $allAlias = Get-Alias +<# + .SYNOPSIS + Generates markdown documentation for all public functions in the PowerShell module. +#> +[CmdletBinding()] +param() + +$ErrorActionPreference = 'Stop' +$ProgressPreference = 'SilentlyContinue' +$VerbosePreference = 'SilentlyContinue' + +function Write-PSModuleDocs { + [CmdletBinding()] + <# + .SYNOPSIS + Generates markdown documentation for all public functions in the PowerShell module. + + .DESCRIPTION + Builds the module, imports it, and generates markdown documentation for each public + function. Output files are written to the docs folder in the repository root, with + one file per function named .md. + + .EXAMPLE + Write-PSModuleDocs + + Generates markdown documentation for all public functions and writes them to the docs folder. + #> + param() + + $gitRoot = Resolve-Path -Path "$PSScriptRoot\..\.." + $moduleName = Split-Path -Path $gitRoot -Leaf + $outputModulePath = "$gitRoot\.output\$moduleName" + $docsPath = "$gitRoot\docs" + + # Verify module has built + if (-not (Test-Path -Path $outputModulePath -PathType Container)) { + throw "Module directory not found at: '$outputModulePath', run 'make build' to build the module." } - process { - $help = Get-Help $Command -Full + try { + Import-Module $outputModulePath -Force -ErrorAction Stop + } + catch { + throw "Built module could not be imported at: '$outputModulePath', please run 'make build' to rebuild the module." + } - # If there's no real help documentation, return null - if (-not $help -or ( - (-not $help.Description) -and - (-not $help.examples) -and - ($help.Synopsis.Trim() -eq $command.Name -or -not $help.Synopsis) - )) { - return $null + try { + $module = Get-Module -Name $moduleName + if (-not $module) { + throw "Module '$moduleName' could not be found after import." } - $parameters = $help.parameters.parameter - $examples = $help.examples.example - $alias = @(($allAlias | where ResolvedCommandName -eq $command.Name).Name) + $allAlias = Get-Alias + $docs = [Collections.Generic.List[PSCustomObject]]::new() + + foreach ($command in $module.ExportedFunctions.Values) { + $help = Get-Help $command -Full + + # If there's no real help documentation, skip + if (-not $help -or ( + (-not $help.Description) -and + (-not $help.examples) -and + ($help.Synopsis.Trim() -eq $command.Name -or -not $help.Synopsis) + )) { + continue + } - $sections = [Collections.Generic.List[string]]::new() + $parameters = $help.parameters.parameter + $examples = $help.examples.example + $alias = @(($allAlias | where ResolvedCommandName -eq $command.Name).Name) - # Header - $header = "# $($command.Name)" - if ($alias) { - $header += " ($($alias -join ', '))" - } - $sections.Add($header) + $sections = [Collections.Generic.List[string]]::new() - # Synopsis - if ($help.Synopsis -and $help.Synopsis.Trim() -notlike "$($command.Name)*") { - $sections.Add("## Synopsis`n`n$($help.Synopsis)") - } + # Header + $header = "# $($command.Name)" + if ($alias) { + $header += " ($($alias -join ', '))" + } + $sections.Add($header) - # Description - if ($help.Description.Text) { - $sections.Add("## Description`n`n$($help.Description.Text)") - } + # Synopsis + if ($help.Synopsis -and $help.Synopsis.Trim() -notlike "$($command.Name)*") { + $sections.Add("## Synopsis`n`n$($help.Synopsis)") + } - # Syntax - $syntaxLines = foreach ($paramSet in $Command.ParameterSets) { - $paramStrings = foreach ($param in $paramSet.Parameters | where Name -notin [Management.Automation.Cmdlet]::CommonParameters) { - $typeName = if ($param.ParameterType -ne [switch]) { - " <$($param.ParameterType.Name)>" - } - else { - '' - } - $token = if ($param.Position -ge 0) { - "[-$($param.Name)$typeName]" | foreach { + # Description + if ($help.Description.Text) { + $sections.Add("## Description`n`n$($help.Description.Text)") + } + + # Syntax + $syntaxLines = foreach ($paramSet in $command.ParameterSets) { + $paramStrings = foreach ($param in $paramSet.Parameters | where Name -notin [Management.Automation.Cmdlet]::CommonParameters) { + $typeName = if ($param.ParameterType -ne [switch]) { + " <$($param.ParameterType.Name)>" + } + else { + '' + } + $token = if ($param.Position -ge 0) { + "[-$($param.Name)$typeName]" | foreach { + if ($param.IsMandatory) { + "[-$($param.Name)$typeName]" + } + else { + "[[-$($param.Name)$typeName]]" + } + } + } + else { if ($param.IsMandatory) { - "[-$($param.Name)$typeName]" + "-$($param.Name)$typeName" } else { - "[[-$($param.Name)$typeName]]" + "[-$($param.Name)$typeName]" } } + $token } - else { - if ($param.IsMandatory) { - "-$($param.Name)$typeName" - } - else { - "[-$($param.Name)$typeName]" - } - } - $token + "$($command.Name) $($paramStrings -join ' ')" + } + $syntaxText = ($syntaxLines -join "`n").Trim() + if ($syntaxText) { + $sections.Add("## Syntax`n`n``````powershell`n$syntaxText`n``````") } - "$($Command.Name) $($paramStrings -join ' ')" - } - $syntaxText = ($syntaxLines -join "`n").Trim() - if ($syntaxText) { - $sections.Add("## Syntax`n`n``````powershell`n$syntaxText`n``````") - } - # Parameters - if ($parameters) { - $paramLines = [Collections.Generic.List[string]]::new() - $paramLines.Add('## Parameters') + # Parameters + if ($parameters) { + $paramLines = [Collections.Generic.List[string]]::new() + $paramLines.Add('## Parameters') - foreach ($param in $parameters) { - $paramSection = "### -$($param.Name)" + foreach ($param in $parameters) { + $paramSection = "### -$($param.Name)" - if ($param.Description.Text) { - $paramSection += "`n`n$($param.Description.Text)" - } + if ($param.Description.Text) { + $paramSection += "`n`n$($param.Description.Text)" + } - $bullets = [Collections.Generic.List[string]]::new() + $bullets = [Collections.Generic.List[string]]::new() - if ($param.Type.Name) { - $bullets.Add("- **Type**: $($param.Type.Name)") - } - if ($param.Required) { - $bullets.Add("- **Required**: $($param.Required)") - } - if ($param.Position) { - $bullets.Add("- **Position**: $($param.Position)") - } + if ($param.Type.Name) { + $bullets.Add("- **Type**: $($param.Type.Name)") + } + if ($param.Required) { + $bullets.Add("- **Required**: $($param.Required)") + } + if ($param.Position) { + $bullets.Add("- **Position**: $($param.Position)") + } - $bullets.Add("- **Default value**: $(if ($param.defaultValue) { - $param.defaultValue - } - else { - 'None' - })") + $bullets.Add("- **Default value**: $(if ($param.defaultValue) { + $param.defaultValue + } + else { + 'None' + })") + + if ($param.pipelineInput) { + $bullets.Add("- **Accepts pipeline input**: $($param.pipelineInput)") + } - if ($param.pipelineInput) { - $bullets.Add("- **Accepts pipeline input**: $($param.pipelineInput)") + $paramSection += "`n`n$($bullets -join "`n")" + $paramLines.Add($paramSection) } - $paramSection += "`n`n$($bullets -join "`n")" - $paramLines.Add($paramSection) + $sections.Add($paramLines -join "`n`n") } - $sections.Add($paramLines -join "`n`n") - } + # Examples + if ($examples) { + $exampleLines = [Collections.Generic.List[string]]::new() + $exampleLines.Add('## Examples') - # Examples - if ($examples) { - $exampleLines = [Collections.Generic.List[string]]::new() - $exampleLines.Add('## Examples') + $i = 1 + foreach ($example in $examples) { + $exampleSection = "### Example $i" - $i = 1 - foreach ($example in $examples) { - $exampleSection = "### Example $i" + $remarksText = ($example.remarks.Text -join '').Trim() + if ($remarksText) { + $exampleSection += "`n`n$remarksText" + } - $remarksText = ($example.remarks.Text -join '').Trim() - if ($remarksText) { - $exampleSection += "`n`n$remarksText" - } + if ($example.code) { + $exampleSection += "`n`n``````powershell`n$($example.code)`n``````" + } - if ($example.code) { - $exampleSection += "`n`n``````powershell`n$($example.code)`n``````" + $exampleLines.Add($exampleSection) + $i++ } - $exampleLines.Add($exampleSection) - $i++ + $sections.Add($exampleLines -join "`n`n") } - $sections.Add($exampleLines -join "`n`n") + $docs.Add([PSCustomObject]@{ + FileName = "$($command.Name).md" + Content = $sections -join "`n`n" + }) + } + + # Clean up docs folder + if (Test-Path -Path $docsPath -PathType Container) { + Get-ChildItem -Path $docsPath -File | Remove-Item -Force + } + else { + $null = New-Item -Path $docsPath -ItemType Directory -Force + } + + if ($docs.Count -eq 0) { + $null = New-Item -Path "$docsPath\.gitkeep" -ItemType File -Force + Write-Verbose 'No documentation generated; created .gitkeep in docs folder.' + return + } + + foreach ($doc in $docs) { + $filePath = "$docsPath\$($doc.FileName)" + Set-Content -Path $filePath -Value $doc.Content -Encoding utf8 -Force + Write-Verbose "Written: $filePath" } - $sections -join "`n`n" + Write-Host "Documentation written to '$docsPath' ($($docs.Count) file(s))." -ForegroundColor Green } + finally { + Get-Module | Where-Object Path -like "$outputModulePath\*" | Remove-Module -ErrorAction SilentlyContinue + } +} - end {} +try { + Write-PSModuleDocs @PSBoundParameters +} +catch { + Write-Host $_ -ForegroundColor Red + exit 1 } From 15ea7a4541cc4fba90cb9d4517986e56417b9333 Mon Sep 17 00:00:00 2001 From: jdarcyryan Date: Sat, 21 Feb 2026 22:14:21 +0000 Subject: [PATCH 08/13] updated template psm1 --- .build/template/Template.psm1 | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/.build/template/Template.psm1 b/.build/template/Template.psm1 index 9754f3e..5de72dd 100644 --- a/.build/template/Template.psm1 +++ b/.build/template/Template.psm1 @@ -71,33 +71,9 @@ if (Test-Path -Path $privatePath) { $publicPath = "$PSScriptRoot\public" if (Test-Path -Path $publicPath) { - # Snapshots - $currentAlias = Get-Alias | select Name, ReferencedCommand - $currentCommands = (Get-Command).Name - Get-ChildItem -Path $publicPath -Filter '*.ps1' | where PSIsContainer -eq $false | foreach { . $_.FullName } - - # Find new aliases - $aliasToExport = Compare-Object $currentAlias (Get-Alias | select Name, ReferencedCommand) -Property Name, ReferencedCommand | - where SideIndicator -eq '=>' | - select -ExpandProperty InputObject - - # Find new commands - $commandsToExport = Compare-Object $currentCommands (Get-Command).Name | - where SideIndicator -eq '=>' | - where InputObject -notin $aliasToExport.ReferencedCommand | - select -ExpandProperty InputObject - - # Combine all functions and export - $allFunctions = @($commandsToExport) + @($aliasToExport.ReferencedCommand) | - where { $_ } | - select -Unique - - if ($allFunctions -or $aliasToExport) { - Export-ModuleMember -Function $allFunctions -Alias $aliasToExport.Name - } } #endregion Public From 07e88c5be9d48912f24749e6f02415b2523cbb8f Mon Sep 17 00:00:00 2001 From: jdarcyryan Date: Sat, 21 Feb 2026 22:14:38 +0000 Subject: [PATCH 09/13] resolved issues with doc writer --- .build/scripts/Write-PSModuleDocs.ps1 | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/.build/scripts/Write-PSModuleDocs.ps1 b/.build/scripts/Write-PSModuleDocs.ps1 index 9ef7794..0605c05 100644 --- a/.build/scripts/Write-PSModuleDocs.ps1 +++ b/.build/scripts/Write-PSModuleDocs.ps1 @@ -16,9 +16,9 @@ function Write-PSModuleDocs { Generates markdown documentation for all public functions in the PowerShell module. .DESCRIPTION - Builds the module, imports it, and generates markdown documentation for each public - function. Output files are written to the docs folder in the repository root, with - one file per function named .md. + Imports the built module directly from the output manifest and generates markdown + documentation for each public function. Output files are written to the docs folder + in the repository root, with one file per function named .md. .EXAMPLE Write-PSModuleDocs @@ -32,16 +32,27 @@ function Write-PSModuleDocs { $outputModulePath = "$gitRoot\.output\$moduleName" $docsPath = "$gitRoot\docs" - # Verify module has built if (-not (Test-Path -Path $outputModulePath -PathType Container)) { throw "Module directory not found at: '$outputModulePath', run 'make build' to build the module." } + $versionFolder = Get-ChildItem -Path $outputModulePath -Directory | Sort-Object Name -Descending | Select-Object -First 1 + + if (-not $versionFolder) { + throw "No version folder found in '$outputModulePath', run 'make build' to build the module." + } + + $manifestPath = "$($versionFolder.FullName)\$moduleName.psd1" + + if (-not (Test-Path -Path $manifestPath -PathType Leaf)) { + throw "Module manifest not found at: '$manifestPath', run 'make build' to build the module." + } + try { - Import-Module $outputModulePath -Force -ErrorAction Stop + Import-Module $manifestPath -Force -ErrorAction Stop } catch { - throw "Built module could not be imported at: '$outputModulePath', please run 'make build' to rebuild the module." + throw "Module could not be imported from '$manifestPath': $_" } try { @@ -220,7 +231,7 @@ function Write-PSModuleDocs { Write-Host "Documentation written to '$docsPath' ($($docs.Count) file(s))." -ForegroundColor Green } finally { - Get-Module | Where-Object Path -like "$outputModulePath\*" | Remove-Module -ErrorAction SilentlyContinue + Get-Module -Name $moduleName -ErrorAction SilentlyContinue | Remove-Module -ErrorAction SilentlyContinue } } @@ -230,4 +241,4 @@ try { catch { Write-Host $_ -ForegroundColor Red exit 1 -} +} \ No newline at end of file From b9ced6d1927d051325c70f93a90593033ad518d7 Mon Sep 17 00:00:00 2001 From: jdarcyryan Date: Sat, 21 Feb 2026 22:19:04 +0000 Subject: [PATCH 10/13] updated new-psmodule to default to no exports --- .build/scripts/New-PSModule.ps1 | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.build/scripts/New-PSModule.ps1 b/.build/scripts/New-PSModule.ps1 index a65e13a..7d81e50 100644 --- a/.build/scripts/New-PSModule.ps1 +++ b/.build/scripts/New-PSModule.ps1 @@ -116,10 +116,11 @@ function New-PSModule { $moduleFileExists = Test-Path -Path $moduleFilePath $action = if ($moduleFileExists) { 'Overwrite file' - } else { + } + else { 'Create file' } - + if ($moduleFileExists) { if ($PSCmdlet.ShouldProcess($moduleFilePath, $action)) { if ($ConfirmPreference -eq 'None' -or $PSCmdlet.ShouldContinue('Overwrite existing file?', $moduleFilePath)) { @@ -141,17 +142,17 @@ function New-PSModule { else { 'Create manifest' } - + if ($manifestExists) { if ($PSCmdlet.ShouldProcess($manifestFilePath, $action)) { if ($ConfirmPreference -eq 'None' -or $PSCmdlet.ShouldContinue('Overwrite existing manifest?', $manifestFilePath)) { - New-ModuleManifest -Path $manifestFilePath -ModuleVersion '0.1.0' -RootModule "$moduleName.psm1" -Confirm:$false -WhatIf:$false + New-ModuleManifest -Path $manifestFilePath -ModuleVersion '0.1.0' -RootModule "$moduleName.psm1" -FunctionsToExport @() -CmdletsToExport @() -VariablesToExport '' -AliasesToExport @() -Confirm:$false -WhatIf:$false } } } else { if ($PSCmdlet.ShouldProcess($manifestFilePath, $action)) { - New-ModuleManifest -Path $manifestFilePath -ModuleVersion '0.1.0' -Description $moduleName -RootModule "$moduleName.psm1" -Confirm:$false -WhatIf:$false + New-ModuleManifest -Path $manifestFilePath -ModuleVersion '0.1.0' -Description $moduleName -RootModule "$moduleName.psm1" -FunctionsToExport @() -CmdletsToExport @() -VariablesToExport '' -AliasesToExport @() -Confirm:$false -WhatIf:$false } } } From 725d31cde1d91613c3a46f4bc765fac19fcc5389 Mon Sep 17 00:00:00 2001 From: jdarcyryan Date: Sat, 21 Feb 2026 22:24:44 +0000 Subject: [PATCH 11/13] removed individual function now its working --- .build/scripts/ConvertTo-HelpMarkdown.ps1 | 154 ---------------------- 1 file changed, 154 deletions(-) delete mode 100644 .build/scripts/ConvertTo-HelpMarkdown.ps1 diff --git a/.build/scripts/ConvertTo-HelpMarkdown.ps1 b/.build/scripts/ConvertTo-HelpMarkdown.ps1 deleted file mode 100644 index c3bb2ad..0000000 --- a/.build/scripts/ConvertTo-HelpMarkdown.ps1 +++ /dev/null @@ -1,154 +0,0 @@ -function ConvertTo-HelpMarkdown { - param( - [Parameter(Mandatory, ValueFromPipeline)] - [Management.Automation.FunctionInfo] - $Command - ) - - begin { - $allAlias = Get-Alias - } - - process { - $help = Get-Help $Command -Full - - # If there's no real help documentation, return null - if (-not $help -or ( - (-not $help.Description) -and - (-not $help.examples) -and - ($help.Synopsis.Trim() -eq $command.Name -or -not $help.Synopsis) - )) { - return $null - } - - $parameters = $help.parameters.parameter - $examples = $help.examples.example - $alias = @(($allAlias | where ResolvedCommandName -eq $command.Name).Name) - - $sections = [Collections.Generic.List[string]]::new() - - # Header - $header = "# $($command.Name)" - if ($alias) { - $header += " ($($alias -join ', '))" - } - $sections.Add($header) - - # Synopsis - if ($help.Synopsis -and $help.Synopsis.Trim() -notlike "$($command.Name)*") { - $sections.Add("## Synopsis`n`n$($help.Synopsis)") - } - - # Description - if ($help.Description.Text) { - $sections.Add("## Description`n`n$($help.Description.Text)") - } - - # Syntax - $syntaxLines = foreach ($paramSet in $Command.ParameterSets) { - $paramStrings = foreach ($param in $paramSet.Parameters | where Name -notin [Management.Automation.Cmdlet]::CommonParameters) { - $typeName = if ($param.ParameterType -ne [switch]) { - " <$($param.ParameterType.Name)>" - } - else { - '' - } - $token = if ($param.Position -ge 0) { - "[-$($param.Name)$typeName]" | foreach { - if ($param.IsMandatory) { - "[-$($param.Name)$typeName]" - } - else { - "[[-$($param.Name)$typeName]]" - } - } - } - else { - if ($param.IsMandatory) { - "-$($param.Name)$typeName" - } - else { - "[-$($param.Name)$typeName]" - } - } - $token - } - "$($Command.Name) $($paramStrings -join ' ')" - } - $syntaxText = ($syntaxLines -join "`n").Trim() - if ($syntaxText) { - $sections.Add("## Syntax`n`n``````powershell`n$syntaxText`n``````") - } - - # Parameters - if ($parameters) { - $paramLines = [Collections.Generic.List[string]]::new() - $paramLines.Add('## Parameters') - - foreach ($param in $parameters) { - $paramSection = "### -$($param.Name)" - - if ($param.Description.Text) { - $paramSection += "`n`n$($param.Description.Text)" - } - - $bullets = [Collections.Generic.List[string]]::new() - - if ($param.Type.Name) { - $bullets.Add("- **Type**: $($param.Type.Name)") - } - if ($param.Required) { - $bullets.Add("- **Required**: $($param.Required)") - } - if ($param.Position) { - $bullets.Add("- **Position**: $($param.Position)") - } - - $bullets.Add("- **Default value**: $(if ($param.defaultValue) { - $param.defaultValue - } - else { - 'None' - })") - - if ($param.pipelineInput) { - $bullets.Add("- **Accepts pipeline input**: $($param.pipelineInput)") - } - - $paramSection += "`n`n$($bullets -join "`n")" - $paramLines.Add($paramSection) - } - - $sections.Add($paramLines -join "`n`n") - } - - # Examples - if ($examples) { - $exampleLines = [Collections.Generic.List[string]]::new() - $exampleLines.Add('## Examples') - - $i = 1 - foreach ($example in $examples) { - $exampleSection = "### Example $i" - - $remarksText = ($example.remarks.Text -join '').Trim() - if ($remarksText) { - $exampleSection += "`n`n$remarksText" - } - - if ($example.code) { - $exampleSection += "`n`n``````powershell`n$($example.code)`n``````" - } - - $exampleLines.Add($exampleSection) - $i++ - } - - $sections.Add($exampleLines -join "`n`n") - } - - $sections -join "`n`n" - } - - end {} -} From e20e7f28004caf948d27e32adf8e9c6a8642b673 Mon Sep 17 00:00:00 2001 From: jdarcyryan Date: Sat, 21 Feb 2026 22:54:45 +0000 Subject: [PATCH 12/13] created workflow --- .github/workflows/update_docs.yml | 53 +++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 .github/workflows/update_docs.yml diff --git a/.github/workflows/update_docs.yml b/.github/workflows/update_docs.yml new file mode 100644 index 0000000..b24a979 --- /dev/null +++ b/.github/workflows/update_docs.yml @@ -0,0 +1,53 @@ +name: Update Documentation + +on: + pull_request: + paths: + - '**.psd1' + - '!**/classes.psd1' + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +jobs: + update-docs: + runs-on: windows-latest + + env: + GITHUB_OWNER: ${{ github.repository_owner }} + GITHUB_REPOSITORY: ${{ github.event.repository.name }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + steps: + - uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + ref: ${{ github.head_ref }} + + - name: Setup Profile + shell: pwsh + run: .\.build\scripts\Setup-RunnerProfile.ps1 -Verbose + + - name: Build Module + run: .\.build\scripts\Build-PSModule.ps1 + + - name: Generate Documentation + shell: pwsh + run: .\.build\scripts\Write-PSModuleDocs.ps1 + + - name: Configure Git + run: | + git config --local user.email "github-actions[bot]@users.noreply.github.com" + git config --local user.name "DocsBot" + + - name: Commit Documentation Changes + run: | + git add docs/ + if git diff --staged --quiet; then + echo "No documentation changes to commit" + else + git commit -m "AUTO: Exported Command Documentation" + git push origin ${{ github.head_ref }} + fi From 9031b390684b1d2225add6c9c16a2194d4bb4f8e Mon Sep 17 00:00:00 2001 From: jdarcyryan Date: Sat, 21 Feb 2026 22:55:54 +0000 Subject: [PATCH 13/13] ignore docs folder --- .github/workflows/pr_tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/pr_tests.yml b/.github/workflows/pr_tests.yml index e378efd..e0284ff 100644 --- a/.github/workflows/pr_tests.yml +++ b/.github/workflows/pr_tests.yml @@ -9,6 +9,7 @@ on: - '!LICENSE' - '!Makefile' - '!.github/workflows/release.yml' + - '!docs/**' workflow_dispatch: permissions: