Create a gist now

Instantly share code, notes, and snippets.

An approach to building .NET Core applications with Cake, XUnit and synchronous `project.json` versionining for both local and on Bamboo
#addin "Cake.Powershell"
#r "tools/AddIns/Cake.DotNetCoreVersion/Cake.DotNetCoreVersion.dll"
//////////////////////////////////////////////////////////////////////
// ARGUMENTS
//////////////////////////////////////////////////////////////////////
var target = Argument("target", "Default");
var configuration = Argument("configuration", "Release");
string version = null;
string semanticVersion = null;
string prerelease = null;
//////////////////////////////////////////////////////////////////////
// TASKS
//////////////////////////////////////////////////////////////////////
Task("Clean")
.Does(() =>
{
CleanDirectory("./artifacts/");
CleanDirectory("./test-results/");
});
Task("Restore")
.Does(() =>
{
DotNetCoreRestore();
});
Task("Version")
.Does(() =>
{
if (Bamboo.IsRunningOnBamboo)
{
// MA - We are running a CI build - so need make sure we execute the script with -local = $false
StartPowershellFile("./version.ps1", args =>
{
args.Append("local", "$false");
args.Append("branch", EnvironmentVariable("bamboo_planRepository_branchName"));
});
}
else
{
StartPowershellFile("./version.ps1", args => args.Append("local", "$true"));
}
string[] lines = System.IO.File.ReadAllLines("./version.props");
foreach (string line in lines)
{
if (line.StartsWith("version"))
{
version = line.Substring("version=".Length).Trim();
}
else if (line.StartsWith("semanticVersion"))
{
semanticVersion = line.Substring("semanticVersion=".Length).Trim();
}
else if (line.StartsWith("prerelease"))
{
prerelease = line.Substring("prerelease=".Length).Trim();
}
}
Console.WriteLine("Version: {0}", version);
Console.WriteLine("SemanticVersion: {0}", semanticVersion);
Console.WriteLine("PreRelease: {0}", prerelease);
DotNetCoreVersion(new DotNetCoreVersionSettings
{
Files = GetFiles("**/project.json"),
Version = semanticVersion
});
});
Task("Build")
.Does(() =>
{
// MA - Build the libraries
DotNetCoreBuild("./src/**/project.json", new DotNetCoreBuildSettings
{
Configuration = configuration
});
// MA - Build the test libraries
DotNetCoreBuild("./tests/**/project.json", new DotNetCoreBuildSettings
{
Configuration = configuration
});
});
Task("Test")
.WithCriteria(() => HasArgument("test"))
.Does(() =>
{
var tests = GetFiles("./tests/**/project.json");
foreach (var test in tests)
{
string projectFolder = System.IO.Path.GetDirectoryName(test.FullPath);
string projectName = projectFolder.Substring(projectFolder.LastIndexOf('\\') + 1);
string resultsFile = "./test-results/" + projectName + ".xml";
DotNetCoreTest(test.FullPath, new DotNetCoreTestSettings
{
ArgumentCustomization = args => args.Append("-xml " + resultsFile)
});
// MA - Transform the result XML into NUnit-compatible XML for the build server.
XmlTransform("./tools/NUnitXml.xslt", "./test-results/" + projectName + ".xml", "./test-results/NUnit." + projectName + ".xml");
}
});
Task("Pack")
.WithCriteria(() => HasArgument("pack"))
.Does(() =>
{
var projects = GetFiles("./src/**/project.json");
foreach (var project in projects)
{
// MA - Pack the libraries
DotNetCorePack(project.FullPath, new DotNetCorePackSettings
{
Configuration = configuration,
OutputDirectory = "./artifacts/"
});
}
});
//////////////////////////////////////////////////////////////////////
// TASK TARGETS
//////////////////////////////////////////////////////////////////////
Task("Default")
.IsDependentOn("Version")
.IsDependentOn("Clean")
.IsDependentOn("Restore")
.IsDependentOn("Build")
.IsDependentOn("Test")
.IsDependentOn("Pack");
//////////////////////////////////////////////////////////////////////
// EXECUTION
//////////////////////////////////////////////////////////////////////
RunTarget(target);
versionMajor=1
versionMinor=0
versionRev=0
namespace Cake.DotNetVersion
{
using System;
using System.Collections.Generic;
using Cake.Core;
using Cake.Core.Annotations;
using Cake.Core.IO;
/// <summary>
/// Contains functionality required to updating project file versions.
/// </summary>
[CakeAliasCategory("DotNetCoreVersion")]
public static class DotNetCoreVersionExtensions
{
[CakeMethodAlias]
public static void DotNetCoreVersion(this ICakeContext context, string version)
{
var settings = new DotNetCoreVersionSettings
{
Files = context.Globber.GetFiles("/**/project.json"),
Version = version
};
DotNetCoreVersion(context, settings);
}
[CakeMethodAlias]
public static void DotNetCoreVersion(this ICakeContext context, FilePath file, string version)
{
var settings = new DotNetCoreVersionSettings
{
File = file,
Version = version
};
DotNetCoreVersion(context, settings);
}
[CakeMethodAlias]
public static void DotNetCoreVersion(this ICakeContext context, IEnumerable<FilePath> files, string version)
{
var settings = new DotNetCoreVersionSettings
{
Files = files,
Version = version
};
DotNetCoreVersion(context, settings);
}
[CakeMethodAlias]
public static void DotNetCoreVersion(this ICakeContext context, DotNetCoreVersionSettings settings)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
if (settings == null)
{
throw new ArgumentNullException(nameof(settings));
}
if (settings.File == null && settings.Files == null)
{
throw new ArgumentException("You must provide a file or set of file paths to process.");
}
if (string.IsNullOrEmpty(settings.Version))
{
throw new ArgumentException("You must provide a version to set.");
}
var runner = new DotNetCoreVersionRunner(context.FileSystem, context.Environment, context.ProcessRunner, context.Tools);
runner.Run(settings);
}
}
}
namespace Cake.DotNetVersion
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Cake.Core;
using Cake.Core.IO;
using Cake.Core.Tooling;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
/// <summary>
/// Applies a version to a set of project.json files.
/// </summary>
public class DotNetCoreVersionRunner : Tool<DotNetCoreVersionSettings>
{
/// <summary>
/// Initialises a new instance of <see cref="DotNetCoreVersionRunner"/>
/// </summary>
/// <param name="fileSystem">The Cake file system.</param>
public DotNetCoreVersionRunner(IFileSystem fileSystem, ICakeEnvironment environment, IProcessRunner processRunner, IToolLocator toolLocator)
: base(fileSystem, environment, processRunner, toolLocator)
{
}
public void Run(DotNetCoreVersionSettings settings)
{
if (settings == null)
{
throw new ArgumentNullException(nameof(settings));
}
if (settings.Files != null)
{
foreach (var file in settings.Files)
{
RunCore(file, settings.Version);
}
}
else if (settings.File != null)
{
RunCore(settings.File, settings.Version);
}
}
protected override IEnumerable<string> GetToolExecutableNames()
{
return new string[0];
}
protected override string GetToolName()
{
return "DotNetCoreVersion";
}
private void RunCore(FilePath filePath, string version)
{
JObject project;
using (var file = new FileStream(filePath.FullPath, FileMode.Open))
using (var stream = new StreamReader(file))
using (var json = new JsonTextReader(stream))
{
project = JObject.Load(json);
}
var versionAttr = project.Property("version");
if (versionAttr == null)
{
project.Add("version", new JValue(version));
}
else
{
versionAttr.Value = version;
}
File.WriteAllText(filePath.FullPath, project.ToString(), Encoding.UTF8);
}
}
}
namespace Cake.DotNetVersion
{
using System.Collections.Generic;
using Cake.Core.IO;
using Cake.Core.Tooling;
/// <summary>
/// Contains settings used by the version tool.
/// </summary>
public class DotNetCoreVersionSettings : ToolSettings
{
/// <summary>
/// Gets or sets the collection of project files to update.
/// </summary>
public IEnumerable<FilePath> Files { get; set; }
/// <summary>
/// Gets or sets the single file path to update.
/// </summary>
public FilePath File { get; set; }
/// <summary>
/// Gets or sets the version to set.
/// </summary>
public string Version { get; set; }
}
}
[CmdletBinding()]
Param(
[bool] $local = $true,
[string] $branch = $null
)
function ReadBuildProps
{
$buildProps = ConvertFrom-StringData (Get-Content ./build.props -raw)
$buildObject = New-Object PSObject -Property $buildProps | Select-Object versionMajor, versionMinor, versionRev
return $buildObject
}
function ReadBranchName
{
$branchName = (& git rev-parse --abbrev-ref HEAD).Trim()
return $branchName
}
function ReadCommitCount
{
$count = (& git rev-list --all --count).Trim()
return $count
}
function CreateVersion
(
[string] $major,
[string] $minor,
[string] $rev,
[string] $branch,
[int] $commits,
[bool] $local = $true
)
{
$version = [string]::Concat($major, ".", $minor, ".", $rev)
$prerelease = $null
$branch = $branch.Replace('/', '-')
if ($branch -ne "release")
{
if ($local)
{
$prerelease = $branch
}
else
{
$prerelease = "$($branch)-$($commits)"
}
}
if ($prerelease -ne $null)
{
$semVer = [string]::Concat($version, "-", $prerelease)
}
else
{
$semVer = $version
}
return @{
Version = $version
SemanticVersion = $semVer
PreRelease = $prerelease
}
}
function WriteVersion
(
[string] $version,
[string] $semanticVersion,
[string] $prerelease
)
{
Set-Content ./version.props "version=$($version)`nsemanticVersion=$($semanticVersion)`nprerelease=$($prerelease)"
}
function ResolveVersion
(
[bool] $local = $true
)
{
$parts = ReadBuildProps
if (!$branch)
{
$branch = ReadBranchName
}
$count = ReadCommitCount
$version = CreateVersion -major $parts.versionMajor -minor $parts.versionMinor -rev $parts.versionRev -branch $branch -commits $count -local $local
WriteVersion -version $version.Version -semanticVersion $version.SemanticVersion -prerelease $version.PreRelease
}
ResolveVersion -local $local
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment