Recently our team released the Reporting Services PowerShell Tools as a project to collaborate with the community, we had a lot more feedback and participation that we ever though and we decided to push it a little further and publish it to the PowerShell Gallery.
We aren’t by any means PowerShell experts and when we published the module we found that we should have run the PSSScriptAnalyzer , so we run it and fixed but this will not scale as a manual task and also I would like to automate the publishing to the gallery so is in sync with the code in the repo.
Investigating around fin this great starting point in the http://ramblingcookiemonster.github.io/GitHub-Pester-AppVeyor/ blog post, it describes how to use AppVeyor for running unit tests with Pester, however I wanted something slightly different, my main goals are:
- Provide static analysis using PSSScriptAnalyzer and show the results to the users submiting the pull request
- After the PR is merged into main publish automatically to the PowerShell gallery
So started by onboarding the Github repo to AppVeyor and created a New Project based on our repo, the procedure is pretty straightforward and AppVeyor has plenty of documentation so I’m not including any detailed instructions.
Onboarding the ScriptAnalyzer
First thing is to add the appveyor.yml file in our github repo, that one was pretty interesting as my initials attempts failed , basically I wanted to install the PSScriptAnalyzer module but it failed with
Exception calling “ShouldContinue” with “2” argument(s): “The method or operation is not implemented.”
At C:Program FilesWindowsPowerShellModulesPowerShellGet1.0.0.1PSModule.psm1:5908 char:8
+ if($Force -or $PSCmdlet.ShouldContinue($shouldContinueQueryMessag …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : NotImplementedException
Install-Module : NuGet provider is required to interact with NuGet-based repositories. Please ensure that ‘2.8.5.201’ or newer version of NuGet provider is installed.
At line:1 char:1
+ Install-Module -Name PSScriptAnalyzer
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [Install-Module], InvalidOperationException
+ FullyQualifiedErrorId : CouldNotInstallNuGetProvider,Install-Module
Command executed with exception: NuGet provider is required to interact with NuGet-based repositories. Please ensure that ‘2.8.5.201’ or newer version of NuGet provider is installed.
The solution is to install first the nuget package provider, the install section end up being
install: | |
– ps:Install-PackageProvider -Name NuGet -Force | |
– ps:Install-Module -Name PSScriptAnalyzer -Force |
The next step was to produce a XML results file, I spend some time figuring out what would be the simplest schema and selected junit and then used some magic string manipulation to get the results of the script analyzer into the testresults file
# Execute the script analyzer
$results = Invoke-ScriptAnalyzer –Path .functions –Recurse | where severity -eq “Error”
# Format the results
$header = “<testsuite tests=`”$($results.Count)`”>”
$body = $results | ForEach-Object {“<testcase classname=`”analyzer`” name=`”$($_.RuleName)`”><failure type=`”$($_.ScriptName)`”>$($_.Message)</failure></testcase>”}
$footer = “</testsuite>”
$header + $body +$footer | out-file .TestsResults.xml
# Upload results
$wc = New-Object ‘System.Net.WebClient’
$wc.UploadFile(“https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)“, (Resolve-Path .TestsResults.xml))
# Fail if there are issues
if($results.Count -gt 0){throw “ScriptAnalyzer found $($results.Count) issues”}
That give me this nice UI when the script analyzer find any issue
You can check the entire script in our repo appveyor.yml
Publishing to the PowerShell Gallery
I want to control the publishing process, I don’t want every single commit to master to publish a new version of the module to the PowerShell Gallery, so I decided to use git tags in order to request a publish, basically when a new tag is added the continuous integration process will run the tests and if they pass use the tag label as the version to publish in the gallery, the general workflow is
- Update the module manifest (.psd1) with the tag version
- Publish to the PowerShell Gallery
- If the publish is successful commit back to the repo the new module manifest (.psd1)
Interacting with the PowerShell gallery is based on a secret key they provide to use it in the Publish-Module cmdlet, in order to be used in your AppVeyor script you should encrypt it, the detailed instructions are available here https://www.appveyor.com/docs/build-configuration/#secure-variables
Also here are the instructions on how to interact with git from the AppVeyor script https://www.appveyor.com/docs/how-to/git-push/
One issue that I hit was that my git commands from powershell where treating the git console messages as errors, searching around found this workaround and implemented in the AppVeyor script, the deploy section looks like this.
# Deploy to Powershell Gallery only where there is a tag which will have the version number
$deploy = ($env:APPVEYOR_REPO_TAG -eq $true)
if ($deploy)
{
git checkout jtarquino/autopublish
Write-Host “Starting Deployment tag $env:APPVEYOR_REPO_TAG_NAME”
$moduleName = “ReportingServicesTools”
$currentVersion = (Import-PowerShellDataFile .$moduleName.psd1).ModuleVersion
((Get-Content .ReportingServicesTools.psd1).replace(“ModuleVersion = ‘$($currentVersion)‘”, “ModuleVersion = ‘$($env:APPVEYOR_REPO_TAG_NAME)‘”)) | Set-Content .$moduleName.psd1
Publish-Module –Path . –NuGetApiKey $env:galleryPublishingKey
git config —global core.safecrlf false
git config —global credential.helper store
Add-Content “$env:USERPROFILE.git-credentials” “https://$($env:access_token):x-oauth-basic@github.com`n“
git config —global user.email “yourEmail@outlook.com”
git config —global user.name “Your Name”
git add ReportingServicesTools.psd1
git commit –m “Automatic Version Update from CI”
git push
}
For some unexplainable reason I wasn’t able to use the Update-ModuleManifest cmdlet so I did the manual replacing of the module version.
If you are interested in the entire deployment file it is available here in our repo appveyor.yml