We are noodling around with how to best provide support for publishing modules to the PowerShell Gallery from a PowerShell workspace within Visual Studio Code. In today’s release of the PowerShell extension for VSCode (version 0.5.0), we have included an example of how this might work in a future release which I’ll discuss at length in this blog post.
If you haven’t already got Visual Studio Code and the PowerShell extension, see my blog post on Getting Started with Visual Studio Code. If you already have Visual Studio Code and the PowerShell extension, make sure you have updated both to their latest versions – currently 0.10.10 for VS Code and 0.5.0 for the PowerShell extension.
The “examples” directory that comes with the PowerShell extension has been updated to add a sample module implementation consisting of the following files:
The module files consist primarily of SampleModule.psd1, SampleModule.psm1, ReleaseNotes.md and the about topic file en-US\about_SampleModule.help.txt.
There are two files that do the work of building a Release directory and copying over the module files and directories to be published, then running Pester tests and finally publishing the module. Note: the publish step in the examples directory runs Publish-Module with –WhatIf so no publishing actually takes place.
The Build.ps1 file is a PSake script that contains the tasks: Init, Clean, Build, Test, PrePublish, Publish and PostPublish. There are also a few utility tasks: ShowKey and StoreKey. I’ll cover keys later. The tasks have been written in a generic way so that you typically only need to customize a few properties e.g.:
Properties { # The name of your module should match the basename of the PSD1 file. $ModuleName = (Get-Item $PSScriptRoot\*.psd1)[0].BaseName # Path to the release notes file. Set to $null if the release notes # reside in the manifest file. $ReleaseNotesPath = "$PSScriptRoot\ReleaseNotes.md" # The directory used to publish the module from. If you are using Git, the # $PublishDir should be ignored if it is under the workspace directory. $PublishDir = "$PSScriptRoot\Release\$ModuleName" # The following items will not be copied to the $PublishDir. # Add items that should not be published with the module. $Exclude = @( 'Release', 'Tests', '.git*', '.vscode', # The next three files are unique to this examples dir. 'DebugTest.ps1', 'Stop*.ps1', 'Readme.md', (Split-Path $PSCommandPath -Leaf) ) # Name of the repository you wish to publish to. Default repo is the PSGallery. $PublishRepository = $null # Your NuGet API key for the PSGallery. Leave it as $null and the first time # you publish you will be prompted to enter your API key. The build will # store the key encrypted in a file, so that on subsequent publishes you # will no longer be prompted for the API key. $NuGetApiKey = $null $EncryptedApiKeyPath = "$env:LOCALAPPDATA\vscode-powershell\NuGetApiKey.clixml" }
You can get by with leaving most of these properties at their default values. There are also two empty tasks – PrePublish and PostPublish – in which you can place PowerShell script to, for example, sign your module files before publishing or copy artifacts to other locations after publishing.
The second file that makes this work is the .vscode\tasks.json file which contains definitions for the Clean, Build, Publish and Test tasks. Here’s a sample of that file e.g.:
{ "taskName": "Build", "suppressTaskName": true, "isBuildCommand": true, "showOutput": "always", "args": [ "Write-Host 'Invoking PSake...'; Invoke-PSake build.ps1 -taskList Build;", "Invoke-Command { Write-Host 'Completed Build task in task runner.' }" ] }, { "taskName": "Publish", "suppressTaskName": true, "showOutput": "always", "args": [ "Write-Host 'Invoking PSake...'; Invoke-PSake build.ps1 -taskList Publish;", "Invoke-Command { Write-Host 'Completed Publish task in task runner.' }" ] },
Notice the “isBuildCommand” setting for the Build task above is set to true. This task is declaring to VSCode that it is the “Build” command for this VSCode workspace which means you can press Ctrl+Shift+B to “build” the Release directory.
NOTE: Building and publishing require the installation of PSake. The good folks working on PSake should be publishing it to the PowerShell Gallery soon thanks to PowerShell MVP Dave Wyatt’s help in setting up the module for publishing. You should be able to install the module with the command:
PS C:\> Install-Module PSake -Scope CurrentUser
If the module can’t be found, download it from here and install it manually.
There is also a task for running Pester tests. Its “isTestCommand” setting is set to true so you can press Ctrl+Shift+T to run your Pester tests at any time.
So let’s look at a run through of opening the Examples folder and running the Build task in this animated gif.
The build task created the “Release” folder and copied just the files we want in our module to that folder. That’s the folder we will tell the Publish-Module command to use. This is more reliable than trying to publish with a module name and relying on Publish-Module to find the one you intended by searching the $env:PSModulePath. You can customize which files or directories are excluded from the Release directory by modifying the $Exclude hashtable property in the Build.ps1 file.
Every time you run the Build task, the first step is to execute the Clean task which “cleans” out the Release\SampleModule directory so you have just the files in there you expect. This also means you should not author any new files in this directory since the contents will be deleted on the next Build (or Publish).
If you are using Git, the examples folder comes with a .gitignore that is configured to not check-in the Release directory as well as a temporary file that gets created in the Test directory.
Now lets see how the publish process works. As I mentioned above, you can play with this all you want since the Publish-Module command is specified with the “-WhatIf” parameter. However, to demonstrate how this “would” work we do prompt for a NuGet API Key. For this example, you can type in any garbage string you want. What you will note though, is that the second time you run the Publish task, you will NOT be prompted for that key. That is because the Build.ps1 script will encrypt your NuGet API key and store it in your user profile directory – at this location: $env:LOCALAPPDATA\vscode-powershell\NuGetApiKey.clixml.
Let’s run the publish task and see what this looks like:
Notice that after the Build step, all of your Pester tests are executed just to make sure that everything is working correctly before the publish step is attempted. One of the Pester tests will likely be a canned test – it submits your PSD1 manifest file to the Test-ModuleManifest command.
Hopefully this looks pretty cool to you. I encourage you to look at the Build.ps1 script and if you spot any issues or have any feedback in general, let us know on GitHub.
There is a lot left to figure out. Right now, we’ve crafted the tasks.json file, put in place the Build.ps1 file, the .gitignore file, provisioned a PSD1 module manifest file, started an about topic, etc. What we would like to do is provide a mechanism by which you can tell the PowerShell extension to generate a “New PowerShell Gallery Module Project” or “New PowerShell Module Project” or “New Module Manifest File”. This scaffolding capability will be a major addition to the PowerShell extension IMO. But we also want to make sure that we don’t make too much of the build & publish process “black-box” i.e. built-into the extension where it can’t easily be modified. We are hoping that by using the VSCode task runner and PSake for build scripts, we keep the part you are mostly likely going to want to customize open to such customization.
“This scaffolding capability will be a major addition to the PowerShell extension IMO” Couldn’t agree with you more. If all of these components remain visible in a project folder it would still encourage people to open the box and peek into it to see how they would change things. This reminds me of the creating a new solution in Visual Studio, but extremely transparent. Thanks for your work on this.
Thanks!
The psake module has now been pushed to the Powershell Gallery 🙂
https://www.powershellgallery.com/packages/psake/4.6.0
Thanks!!
It sounds like there was good work being done on this, and then it stopped? I thought it would be awesome to use VSCode to publish to both Git and a Private PSGallery. Is there still any desire to work on something like that?
Yeah, it was pointed out (rightly) that the build work did not belong in Plaster (a templating engine for PowerShell). So we created PlasterBuild but I have not had time to work on it. Been too busy with the VSCode extension for PowerShell and posh-git. I would like to start up PlasterBuild at some point but my free time is limited. 🙂