Scott's Blog

Powershell but Devops Part 3

In part 2 we learned how to lint our powershell, and automate unit testing using Github actions. In this third and final part, we’ll talk about automating your module release using GitHub Actions as well as generating a static documentation site.

Pre-requesites

The tools we’ll need for part 3 are:

Getting started with Documentation

Alright, let’s start with why the documentation is important. While I feel like this goes without saying, documentation is how end users or other software developers will know how to interact with your code. Without it, it creates a feeling that your software shouldn’t be used, since you’re the only one who knows how to use and maintain it.

So, how do we make our lives easier and generate the documentation. Well, that’s where it gets difficult. Let’s start with installing the platyPS module.

Install-Module -Name platyPS

Now, we import the module and generate a new markdown help website.

Import-Module platyPS
Import-Module .\MathFunctions.psd1

New-MarkdownHelp -Module MathFunctions -OutputFolder .\docs

Sheesh, that was hard. Right?! Only kidding.

When we run New-MarkdownHelp, we provide it with the name of our module MathFunctions and give it the name of our desired folder to output the docs to. In this case, we’re going to output them to .\docs.

It’s important to note that platyPS is able to generate our markdown based on the comment blocks we defined in our module.

<#
   .SYNOPSIS
    Returns the sum of two numbers

   .DESCRIPTION
    Will add two numbers together

   .PARAMETER Number1
    The first number

   .PARAMETER Number2
    The second number

   .EXAMPLE
    PS> Add-Numbers 1 1
    File.doc
#>

This block is what is responsible for the markdown generated for our code. As you make changes to your functions, ensure you update the inline documentation to make it easy to generate your documentation when you make changes.

Automate!

Now, let’s add this as a part of our workflow in GitHub Actions.

  generate-docs:
    name: Generate docs
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository code
        uses: actions/checkout@v2
      - name: Generate documentation for module
        shell: pwsh
        run: |
          Install-Module platyPS -Force
          Import-Module platyPS
          Import-Module .\MathFunctions.psd1

          New-MarkdownHelp -Module MathFunctions -OutputFolder .\docs -Force          
      - name: Commit files
        run: |
          git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
          git config --local user.name "github-actions[bot]"
          git commit -m "Add changes" -a          
      - name: Should push
        shell: pwsh
        run: |
          $FilesChanged = (git diff HEAD HEAD~1 --name-only -- .\docs).count
          if($FilesChanged -gt 0) { echo "ShouldPush=true" >> $GITHUB_ENV }          
      - name: Push changes
        if: env.ShouldPush == 'true'
        uses: ad-m/github-push-action@master
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          branch: ${{ github.ref }}

In the above code, we run New-MarkdownHelp -Force from platyPS to generate fresh documentation each time our build runs. platyPS does support Update-MarkdownHelp, which I tried to use for this project but wasn’t able to get it to work properly. In fact, I prefer to use New-MarkdownHelp -Force anyway to generate a new clean copy. So, it worked out in my case.

Next, we commit the files and push the changes to our branch. This will ensure the docs are updated with each build. In between these two steps, we have a step called Should push. This step uses git diff to determine if any changes were made to the .\docs folder. We do this to ensure we don’t run git push without changes, as git will return a non-zero exit code if we attempt to push with no changes, resulting in a failed build.

Great! Now we have docs that are being generated each time we update our module.

Creating Releases in GitHub Actions

This is probably the easiest part of the whole series, although I hope I laid it out in a simple way thus far.

We’re going to require an API key from PowerShell Gallery, once you’ve obtained an API key add that as a secret in your repositories settings, named NUGET_API_KEY.

After we have our API key, we’ll add the below job to our pipeline:

  publish-to-gallery:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Build and publish
        env:
          NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
        shell: pwsh
        run: |
          mkdir MathFunctions
          mv ./MathFunctions.psd1 ./MathFunctions
          mv ./MathFunctions.psm1 ./MathFunctions
          Publish-Module -Path ./MathFunctions -NuGetApiKey $env:NUGET_API_KEY -Verbose          

We have to run a few steps to “create” our release. We’ll create a new directory, named the same as our module. In my case, that module name is MathFunctions. I’ll copy over the .psd1 and .psm1 associated with my project. From there, we can use Publish-Module to upload the module to PowerShell Gallery.

Now we can head over to PowerShell Gallery and look at our published function, here’s mine MathFunctions.

NOTE: Remember to update your module version in the module manifest each time you intend to release your project. If you don’t update the version, your workflow will fail as the module version is already published.

Conclusion

To recap this post, we learned how to add static documentation generation to our project, automate it’s generation using GitHub Actions. Then, we published our module to the PowerShell Gallery using GitHub actions.

I think that wraps up this series, but that doesn’t mean there won’t be more content related to this repository or series in the future.

If you have any questions, comments or wanna talk about how you build, test, and deploy PowerShell modules, please reach out to me on twitter @scwheele or open an issue on the project’s repository on Github PowerShellDevOps

Until next time, go learn something new! :)