In this post I show how you can simplify your Cake global tool bootstrapper scripts by taking advantage of local tools, introduced in .NET Core 3.0.
I described the new local tools feature (introduced in .NET Core 3.0) in a recent post. I'm not going to recap the details of local tools here, so if you haven't already, I strongly recommend reading that post. In this post, I'm going to take the scripts I've been using to bootstrap and run Cake on Windows using PowerShell and Linux using bash or sh, and simplify them by taking advantage of the .NET Core local tools.
Prerequisites
For the scripts shown in this post, I make a few assumptions:
- Your build environment already has the .NET Core 3.0 SDK installed.
- You have created a dotnet-tools.json manifest in the default location for your repository (as described in my previous post).
- You have specified version
0.35.0
(or higher) of theCake.Tool
global tool in the tool manifest. This version is compatible with .NET Core 3.0.
If you need to install the .NET Core SDK as well, then I suggest either using one of the more comprehensive Cake bootstrapper scripts, or the official dotnet-install scripts.
You can create a dotnet-tools.json manifest in your project and add the latest version of the Cake tool by running the following from your project's root folder:
dotnet new tool-manifest
dotnet tool install Cake.Tool
The dotnet-tools.json manifest should be checked-in to your source code repository.
A Cake bootstrapper script for PowerShell using .NET Core 3.0 local tools
I always like to have a build.ps1 or build.sh script in a project's root directory, to make it easy to run a full build, whether on a CI machine or locally. With the introduction of local tools to .NET Core 3.0 these scripts have become even simpler:
[CmdletBinding()]
Param(
[string]$Script = "build.cake",
[string]$Target,
[Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)]
[string[]]$ScriptArgs
)
# Restore Cake tool
& dotnet tool restore
# Build Cake arguments
$cakeArguments = @("$Script");
if ($Target) { $cakeArguments += "--target=$Target" }
$cakeArguments += $ScriptArgs
& dotnet tool run dotnet-cake -- $cakeArguments
exit $LASTEXITCODE
This script essentially only does four things:
- Define the arguments for the script, to make it easy to specify a Cake script file or a custom target.
- Restore the local tools using the dotnet-tools.json manifest, by running
dotnet tool restore
. - Parse the arguments provided to the script into the format required by the Cake global tool
- Execute the Cake global tool by running
dotnet tool run dotnet-cake
and passing in the arguments parsed in the previous step
In the previous version of the script (for pre-.NET Core 3.0), I installed global tools "locally", which means you have to faff around making sure you have the correct version installed (as you can only "globally" install a single version of a tool). With local tools you can have multiple versions of a tool available, so that all goes away!
To run the script, run .\build.ps1
, or pass in a target to run:
> .\build.ps1 -Target=Clean
Tool 'cake.tool' (version '0.35.0') was restored. Available commands: dotnet-cake
Restore was successful.
Running Target: Clean
...
Obviously the bootstrapper script isn't something you're going to have to look at very often, so some complexity in there isn't a big issue. But then again, the less code you have to look after the better!
The script is arguably so simple now, that it's pretty much unnecessary. I would still include it though, as it makes it obvious to consumers of your project how to run the build.
A Cake bootstrapper script for bash using .NET Core 3.0 local tools
Next in line for trimming is the bash bootstrapper script. Previously we had to make sure we installed the correct version of the Cake global tool, and had to work out the required directories etc. With local tools, that all goes away:
#!/usr/bin/env bash
# Define default arguments.
SCRIPT="build.cake"
CAKE_ARGUMENTS=()
# Parse arguments.
for i in "$@"; do
case $1 in
-s|--script) SCRIPT="$2"; shift ;;
--) shift; CAKE_ARGUMENTS+=("$@"); break ;;
*) CAKE_ARGUMENTS+=("$1") ;;
esac
shift
done
# Restore Cake tool
dotnet tool restore
if [ $? -ne 0 ]; then
echo "An error occured while installing Cake."
exit 1
fi
# Start Cake
dotnet tool run dotnet-cake "$SCRIPT" "${CAKE_ARGUMENTS[@]}"
This bash script is only doing three steps:
- Parse arguments
- Restore the .NET Core local tools
- Run the restored Cake global tool
This script will work for environments where you have bash available, such as on Debian or Ubuntu distributions. If you're building on the tiny Alpine distribution, you'll need to use the script from the next section instead.
A Cake bootstrapper shell script for Alpine using .NET Core 3.0 local tools
Alpine uses the Almquist (ash) shell instead of bash, so you can't use "bash-isms" like arrays. The script below is a slight modification of the previous script which uses string concatenation in place of arrays:
#!/bin/sh
# Define default arguments.
SCRIPT="build.cake"
CAKE_ARGUMENTS=""
# Parse arguments.
for i in "$@"; do
case $1 in
-s|--script) SCRIPT="$2"; shift ;;
--) shift; CAKE_ARGUMENTS="${CAKE_ARGUMENTS} $@"; break ;;
*) CAKE_ARGUMENTS="${CAKE_ARGUMENTS} $1" ;;
esac
shift
done
set -- ${CAKE_ARGUMENTS}
# Restore Cake tool
dotnet tool restore
if [ $? -ne 0 ]; then
echo "An error occured while installing Cake."
exit 1
fi
# Start Cake
dotnet tool run dotnet-cake "--" "$SCRIPT" "$@"
This version also addresses the use of eval
from my previous version of the script. That's not related to local tools, I just finally came across this StackExchange answer that showed how to use set --
to replace the "$@"
pseudo-array. That makes me much happier!
Summary
This post showed how you can use the .NET Core 3 global tools feature to simplify your Cake bootstrapper scripts. With a simple JSON file and two .NET Core CLI commands, you can restore and run any .NET Core global tools you need. If you need to install additional tools for your build, you could include them in your manifest file too, or let Cake manage that for you as described here.