Published in Articles on by Michiel van Oosterhout ~ 3 min read

The previous article described the process of creating, publishing, and installing a PowerShell module. Since we'll likely need our PowerShell modules to evolve over time, adding features and fixing bugs, we will have to publish new versions somewhat regularly. In this article we'll look at a script that we can use to implement Git-based versioning and publishing to multiple release channels.

Railway junction
Railway junction Max Pixel

Git-based versioning and automation

Automation is key to make sure our process is

  • safe: only publish if all tests pass;
  • correct: ensure we publish prereleases and releases to separate channels;
  • and easy: automatically publish when changes are committed and/or approved.

In the past we've already looked at two different recipes for Git-based versioning and publishing to release channels, and we implemented one of them for PowerShell script packages, which we then used in a GitHub workflow, as well as Azure DevOps, Bitbucket, and GitLab pipelines. We even made it work for repositories with multiple scripts.

All of these techniques can be applied PowerShell modules as well. That just leaves us with some small modifications to Publish-PowerShellScript.ps1, so it can be used to publish a module instead of a script package.

Publish-PowerShellModule

The name of the cmdlet will change from Publish-PowerShellScript to Publish-PowerShellModule, but the Param block does not change.

The first significant change is the $manifestPath which changes to the .psd1 file path. Note that, although a module manifest is optional, to publish a module it is required. After determining the path to the manifest, we change the call to validate the manifest from Test-ScriptFileInfo to Test-ModuleManifest.

The code analysis for a script package is invoked on all .ps1 files, excluding .Tests.ps1 files. For a module it should also be invoked on .psm1 and .psd1 files.

When publishing a prerelease we need to update the manifest. For a module we need to use Update-ModuleManifest instead of Update-ScriptFileInfo. And instead of setting the version to include a prerelease label (e.g. 1.2.0-beta0001), we can simply use the -Prerelease parameter.

Finally, we use Publish-Module instead of Publish-Script, and we provide the path to the manifest's parent directory.

With those relatively small changes we get Publish-PowerShellModule.ps1, a script to publish a PowerShell module, using a Git-based workflow to choose the release channel.

Caveats and improvements

For simple scenarios the script will work, but there are a few caveats, as well as a few possible improvements:

  • Any .Tests.ps1 files will be included in the NuGet package.
  • Dependencies (RequiredModules) should be installed and imported into the session before running the script.
    (The script could perform these actions automatically, except it may not be clear what the source repository for each dependency should be.)
  • Dependencies from the PowerShell Gallery must be available in the target repository.
    (This is only an issue when publishing to a private repository. One solution is to add the name of each dependency to the PrivateData.PSData.ExternalModuleDependencies array.)
  • The script has not been tested with nested modules.
  • The script could auto-generate the informational lists of files (FileList) and modules (ModuleList).

Conclusion

The workflow for publishing a PowerShell module is very similar to that for publishing a PowerShell script package. The original script requires only minor adjustments for the simple scenario of publishing a single module, with no nested modules or dependencies.