The PowerShell based tool psake is a great way to orchestrate a build. It can also be used for many other kinds of orchestration. One of the trickiest parts is kicking off the process in the first place.
If your user has installed psake as a global tool, then it’s very easy to launch processing by using the invoke-psake
cmdlet.
But what do you do if psake hasn’t been globally installed?
The approach I take is to have a utility script (normally called bootstrap.ps1
) that takes care of loading the psake PowerShell module in a robust way. This is called at the start of each build script - here’s an example script, integration-build.ps1
:
(I have a habit of stashing support scripts like this one in a separate \scripts\
folder so they don’t pollute the root folder of a project.)
The core of bootstrap.ps1
is a simple function that tries to load psake from a specified directory. That function is called multiple times, probing several likely locations for psake. Let’s look at how that function breaks down:
When we search for psake
, it’s possible we’ll find multiple matches - if this happens, we want to ensure that we select the most recent release (highest version number). The $toNatural
block transforms any numbers in the filename, ensuring we get a numeric sort, not an alphabetical one.
We use get-childitem
to search for the module file psake.psm1
underneath the directory we’re passed, sorting the matches into order by their version, and selecting the highest one.
If we don’t find psake, we write a logline so that consumers know where we looked - this helps with the troubleshooting scenario where someone is trying to work out why the scripts aren’t working.
With that helper function in place, we can start trying to load psake, checking a number of possible locations. For each one, we check first to see if we’ve got the module already loaded.
We begin by trying to load it from a directory local to the project - this allows us to force a particular version by including directly in the folder structure:
If that doesn’t work, we ask PowerShell to load it from the default module location:
If neither of those work, we try the local .\packages\
folder (in case NuGet has already downloaded it), as well as the global Chocolatey cache:
Newer versions of NuGet use a global machine cache, to save on disk space, so it’s worth looking there. There’s no environment variable; instead, we need to run a command to find the location. We use nuget.exe
if we can, but fall back to dotnet.exe
if needed.
Using get-command
, we search the PATH
on the machine to find the application we want. The SilentlyContinue
option means that we don’t get an error, just a $null
if it can’t be found.
If we found nuget.exe
, we use it to get a list of cache folders to scan. If we didn’t find it, we’ll use dotnet.exe
if that was found:
Each command returns a list of locations, listing both a name and a folder. Unfortunately, the two lists are slightly different, so there’s a little bit of list mangling required.
Now we have a list of possible folders - we scan through them, trying to load psake from each one.
Before calling TryLoad-Psake-ViaNuGetCache
, we need to set an environment variable so that dotnet
doesn’t display a welcome message if it’s the first run ever (credit to Pete for finding this edge case).
Once finished, we report on the location where psake was found, so that our user knows which version is being used. If we didn’t manage to load it, we abort with an error.
In use, the output of bootstrap.ps1
looks like this:
(Sample taken from the output of the script that I use to generate the static HTML for this very site.)
In most cases, this script is able to load Psake for use; if it doesn’t, the user hopefully has enough information to try and troubleshoot the issue.
Comments
blog comments powered by Disqus