Sharing variables and state across your builds can currently be achieved in a handful of different ways, depending on what state you want to share and across what scope.
Typed Context
Available since Cake 0.28.0.
You can use a typed context to easily share complex state across tasks without using global variables or static members. Using typed context in your script is done in 3 parts: the context itself, the Setup
method, and individual Task
methods.
For more background on typed context, check out the release blog post
Creating your typed context
First, you will need a class to act as the typed context. This can be any standard C#, and doesn't need to include any Cake-specific code. For this example, we'll use the following simple class:
public class BuildData
{
public string Configuration { get; }
public bool BuildPackages { get; }
public List<string> Frameworks {get; set;}
// you can use read-only or mutable properties
public BuildData(
string configuration,
bool buildPackages)
{
Configuration = configuration;
BuildPackages = buildPackages;
}
}
Returning your context from Setup
To have your script use a typed context, you need to return an instance of your setup class from your (correctly-typed) Setup
method. You can use this method to change how your tasks will run later. Using the example above:
Setup<BuildData>(setupContext => {
return new BuildData(
configuration: Argument("configuration", "Release"),
buildPackages: !BuildSystem.IsLocalBuild
) {
Frameworks = IsRunningOnUnix()
? new List<string> { "netcoreapp2.1" }
: new List<string> { "net472", "netcoreapp2.1" }
};
});
Using your context in a Task
Finally, you can access your typed context from any task, by supplying a type parameter to the Does
, DoesForEach
or WithCriteria
method of your task declaration:
Task("Build-Packages")
.WithCriteria<BuildData>((context, data) => data.BuildPackages) //Your typed context is the second argument
.Does<BuildData>(data => //make sure you use the right type parameter here
{
Information("Packages were {0}", data.BuildPackages ? "built" : "not built");
});
You can also use your typed context in the Teardown
method:
Teardown<BuildData>((context, data) => // make sure you use the type parameter here
{
Information($"Completed build for {(string.Join(", ", data.Frameworks))}");
});
Global Variables
The simplest approach to sharing variables or state across your build is using global variables in your build script.
To do this, simply declare any variables outside the scope of your Task
methods. The convention for these variables is to place them at the start of the script.
///////////////////////////////////////////////////////////////////////////////
// VARIABLES
///////////////////////////////////////////////////////////////////////////////
var target = Argument("target", "Default");
var configuration = Argument("configuration", "Release");
var artifacts = "./dist/";
var testResultsPath = MakeAbsolute(Directory(artifacts + "./test-results"));
You can then access these variables in any of your Task
methods.
Setup(setupContext =>
{
Information($"Using {configuration} build configuration");
});
Task("Clean")
.Does(() =>
{
// Clean artifacts directory
CleanDirectory(artifacts);
});