Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.