Generate a GitHub README.md file from your PowerShell module help

When publishing a script or a module to a GitHub repository, it is a best practice to add a README.md file in the same folder as a script file. That way, anyone who stumbles upon the script on GitHub is presented with a brief documentation of what the script does and how to use it.

I write exclusively PowerShell modules (.psm1) nowadays, as opposed to scripts (.ps1). The main reasons for that are : I want to build tools which behaves just like native cmdlets and I want my tools to be easily reusable. Documentation is an important part of making a module reusable by anyone.

Even though I’m a big believer in documentation, sometimes, I have a hard time practicing what I preach. After working for a few hours on a module, I just want to upload it and be done with it. Exploring, tinkering and scripting in the shell and/or the ISE is the fun part, writing the documentation is the boring part. But wait, what do we do with the boring stuff ?
We AU… TO… MATE it !

If you lookup on the internet, you will find PowerShell scripts which do this, but they are using the content of the file and parsing it to extract the desired information. Text-parsing in PowerShell… Ugh. It’s so against nature, it makes me feel dirty.

Usually, I already have comment-based help in the module, so let’s use it by importing the module and leveraging the object-oriented output of Get-Help. As a bonus, this approach will work with any PowerShell module (script or compiled) and any help (comment-based or XML-based).

PS C:\> Import-Module "C:\GitHub\Powershell-Utility\Example\Example.psm1"
PS C:\> $FullModulePath = Resolve-Path -Path "C:\GitHub\Powershell-Utility\Example\Example.psm1"
PS C:\> $Module = Get-Module | Where-Object { $_.Path -eq $FullModulePath }
PS C:\> $Module

ModuleType Version    Name                                ExportedCommands
---------- -------    ----                                ----------------
Script     0.0        Example                             {Get-Nothing, Set-Nothing}

 
To illustrate how to do this, we are working with a module called Example.

The README.md is a text file so, from a PowerShell perspective, it will be an array of strings. To prepare it, it’s as simple as that :

PS C:\> $Readme = @()

 
It is empty, but we are going to populate it as we go.

PS C:\> $Commands = Get-Command -Module $Module
PS C:\> $Readme += "##Description :"
PS C:\> $Readme += "`n`r"
PS C:\> $CommandsCount = $($Commands.Count)
PS C:\> $Readme += "This module contains $CommandsCount cmdlets :  "
PS C:\> $Readme
##Description :

This module contains 2 cmdlets :

 
First, we got the command(s) in the module and store them in $Commands.
“##” is a markdown tag to mark the following text as a second level heading, like h2 in HTML.
`n is the new line character and `r is the carriage return.
By the way, `n doesn’t work well in GitHub markdown. So here is a trick : to start a new line without starting a new paragraph, you need to add 2 trailing spaces at the end of the line.

Then, $Commands.Count gives us the number of commands in the module. We store it in the variable $CommandsCount, which allows us to document the number of cmdlets in the module.

Then, we add the name of each cmdlet :

Foreach ($Command in $Commands) {
    $Readme += "**$($Command.Name)**  "
} 

 
"**" is a markdown tag. The text surrounded by "**" will be in bold.

Now, I want to document the PowerShell version required by the module. This is not in the help, but this is specified in the #Requires statement at the very beginning of the module, so we can get to it this way :

$FirstLine = $Module.Definition -split "`n" | Select-Object -First 1
If ($FirstLine -like "#Requires*") {
    $PSVersionRequired = $($FirstLine -split " " | Select-Object -Last 1)
}
If ($PSVersionRequired) {
    $Readme += "It requires PowerShell version $PSVersionRequired (or later)."
}

 
Now, let’s start the cmdlet-specific section :

PS C:\> $Readme += "`n`r"
PS C:\> $Name = $Commands[0].Name
PS C:\> $Readme += "##$Name :"
PS C:\> $Readme += "`n`r"
PS C:\> $HelpInfo = Get-Help $Name -Full
PS C:\> $Readme += $HelpInfo.description
PS C:\> $Readme
##Description :

This module contains 2 cmdlets :
It requires PowerShell version 4 (or later).

##Get-Nothing :

This cmdlet does absolutely nothing and does it remarkably well.
It takes objects as input and it outputs nothing.

 
We run Get-Help for the first cmdlet in our module, using the -Full parameter to get everything from the Help and store that in a variable $HelpInfo.
Then, the description of the cmdlet is easily retrieved from the description property of $HelpInfo.

Then, let’s get to the parameters section of our documentation.

For each parameter, we want to document its name, description, and if it has a default value, a user-friendly sentence explaining its default value. It turns out that everything we need is in the “parameters” property of our variable $HelpInfo :

PS C:\> $CommandParams = $HelpInfo.parameters.parameter
PS C:\> $CommandParams

-InputObject <PSObject[]>
    Specifies one or more object(s) to get.
    It can be string(s), integer(s), file(s), any type of object.

    Required?                    true
    Position?                    1
    Default value                (Get-Item *)
    Accept pipeline input?       true (ByValue)
    Accept wildcard characters?  false


-Filter <String>
    Specifies a filter in the provider's format or language. The value of this parameter qualifies
    the InputObject.
    The syntax of the filter, including the use of wildcards, or regular expressions, depends on
    the provider.

    Required?                    false
    Position?                    2
    Default value
    Accept pipeline input?       false
    Accept wildcard characters?  false

 

Let’s add this to the README :

PS C:\> $Readme += "###Parameters :"
PS C:\> $Readme += "`n`r"
PS C:\> Foreach ($CommandParam in $CommandParams) {
           $Readme += "**" + $($CommandParam.Name) + " :** " + $($CommandParam.description.Text) + "  "

           If ( $($CommandParam.defaultValue) ) {
               $ParamDefault = $($CommandParam.defaultValue).ToString()
               $Readme += "If not specified, it defaults to $ParamDefault ."
           }
           $Readme += "`n`r"
}
PS C:\>

 
The default value for a parameter might not be a string, so we used the method ToString() which can convert any object to a string.

I usually give 2 or 3 usage examples in the comment-based help and this is the case in our Example module. So let’s add these examples into our README :

PS C:\> $Readme += "###Examples :`n`r"
PS C:\> $Readme += $HelpInfo.examples | Out-String

 
And here is how it looks like on GitHub :

README Screenshot

You can see it for yourselves here.

With these little tools in my belt, I wrote a PowerShell Module named ReadmeFromHelp and it is available on GitHub. It contains one cmdlet New-ReadmeFromHelp and the README.md for this module has been automatically generated by… itself.

2 thoughts on “Generate a GitHub README.md file from your PowerShell module help”

Leave a Reply

Your email address will not be published. Required fields are marked *