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
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,
(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.
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.
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.
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.