Skip to content

Instantly share code, notes, and snippets.

@GeorgDangl
Last active July 27, 2022 01:21
Show Gist options
  • Save GeorgDangl/ad40b2b8b5871f6f7b432946e6907f9c to your computer and use it in GitHub Desktop.
Save GeorgDangl/ad40b2b8b5871f6f7b432946e6907f9c to your computer and use it in GitHub Desktop.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<RootNamespace></RootNamespace>
<IsPackable>False</IsPackable>
<NoWarn>CS0649;CS0169</NoWarn>
<!-- NUKE CONFIGURATION -->
<NukeVersion>0.2.26</NukeVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Nuke.Common" Version="$(NukeVersion)" />
<PackageReference Include="Nuke.CodeGeneration" Version="$(NukeVersion)" />
<PackageReference Include="GitVersion.CommandLine" Version="4.0.0-beta0012" />
<PackageReference Include="Nuke.WebDocu" Version="1.0.2" />
<PackageReference Include="Nuke.GitHub" Version="1.0.2" />
<PackageReference Include="JetBrains.dotCover.CommandLineTools" Version="2018.1.0-eap01" />
<PackageReference Include="ReportGenerator" Version="3.1.2" />
<PackageReference Include="docfx.console" Version="2.31.0">
<ExcludeAssets>build</ExcludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<NukeMetadata Include="**/*.json" Exclude="bin/**;obj/**" />
<None Remove="*.csproj.DotSettings;*.ref.*.txt" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\src\Nuke.CoberturaConverter\Nuke.CoberturaConverter.csproj" />
</ItemGroup>
</Project>
using Nuke.CoberturaConverter;
using Nuke.Common.Git;
using Nuke.Common.Tools.DocFx;
using Nuke.Common.Tools.DotCover;
using Nuke.Common.Tools.DotNet;
using Nuke.Common.Tools.GitVersion;
using Nuke.Common.Tools.ReportGenerator;
using Nuke.Core;
using Nuke.Core.Tooling;
using Nuke.Core.Utilities;
using Nuke.Core.Utilities.Collections;
using Nuke.GitHub;
using Nuke.WebDocu;
using System;
using System.Collections;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Xml.Linq;
using System.Xml.XPath;
using static Nuke.CoberturaConverter.CoberturaConverterTasks;
using static Nuke.CodeGeneration.CodeGenerator;
using static Nuke.Common.ChangeLog.ChangelogTasks;
using static Nuke.Common.Tools.DocFx.DocFxTasks;
using static Nuke.Common.Tools.DotCover.DotCoverTasks;
using static Nuke.Common.Tools.DotNet.DotNetTasks;
using static Nuke.Common.Tools.ReportGenerator.ReportGeneratorTasks;
using static Nuke.Core.EnvironmentInfo;
using static Nuke.Core.IO.FileSystemTasks;
using static Nuke.Core.IO.PathConstruction;
using static Nuke.Core.Tooling.ProcessTasks;
using static Nuke.GitHub.ChangeLogExtensions;
using static Nuke.GitHub.GitHubTasks;
using static Nuke.WebDocu.WebDocuTasks;
class Build : NukeBuild
{
// Console application entry. Also defines the default target.
public static int Main() => Execute<Build>(x => x.Compile);
// Auto-injection fields:
[GitVersion] readonly GitVersion GitVersion;
// Semantic versioning. Must have 'GitVersion.CommandLine' referenced.
[GitRepository] readonly GitRepository GitRepository;
// Parses origin, branch name and head from git config.
[Parameter] string MyGetSource;
[Parameter] string MyGetApiKey;
[Parameter] string DocuApiKey;
[Parameter] string DocuApiEndpoint;
[Parameter] string GitHubAuthenticationToken;
string DocFxFile => SolutionDirectory / "docfx.json";
// This is used to to infer which dotnet sdk version to use when generating DocFX metadata
string DocFxDotNetSdkVersion = "2.1.4";
string ChangeLogFile => RootDirectory / "CHANGELOG.md";
Target Clean => _ => _
.Executes(() =>
{
DeleteDirectories(GlobDirectories(SourceDirectory, "**/bin", "**/obj"));
DeleteDirectories(GlobDirectories(RootDirectory / "test", "**/bin", "**/obj"));
EnsureCleanDirectory(OutputDirectory);
});
Target Restore => _ => _
.DependsOn(Clean)
.Executes(() =>
{
DotNetRestore(s => DefaultDotNetRestore);
});
Target Compile => _ => _
.DependsOn(Restore)
.Executes(() =>
{
DotNetBuild(s => DefaultDotNetBuild
.SetFileVersion(GitVersion.GetNormalizedFileVersion())
.SetAssemblyVersion(GitVersion.AssemblySemVer));
});
Target Pack => _ => _
.DependsOn(Compile)
.Executes(() =>
{
var changeLog = GetCompleteChangeLog(ChangeLogFile)
.EscapeStringPropertyForMsBuild();
DotNetPack(s => DefaultDotNetPack
.SetPackageReleaseNotes(changeLog));
});
Target Test => _ => _
.DependsOn(Compile)
.Executes(() =>
{
var testProjects = GlobFiles(SolutionDirectory / "test", "*.csproj");
var testRun = 1;
foreach (var testProject in testProjects)
{
var projectDirectory = Path.GetDirectoryName(testProject);
string testFile = OutputDirectory / $"test_{testRun++}.testresults";
// This is so that the global dotnet is used instead of the one that comes with NUKE
var dotnetPath = ToolPathResolver.GetPathExecutable("dotnet");
StartProcess(dotnetPath, "xunit " +
"-nobuild " +
$"-xml {testFile.DoubleQuoteIfNeeded()}",
workingDirectory: projectDirectory)
// AssertWairForExit() instead of AssertZeroExitCode()
// because we want to continue all tests even if some fail
.AssertWaitForExit();
}
PrependFrameworkToTestresults();
});
Target Coverage => _ => _
.DependsOn(Compile)
.Executes(() =>
{
var testProjects = GlobFiles(SolutionDirectory / "test", "*.csproj").ToList();
for (var i = 0; i < testProjects.Count; i++)
{
var testProject = testProjects[i];
var projectDirectory = Path.GetDirectoryName(testProject);
// This is so that the global dotnet is used instead of the one that comes with NUKE
var dotnetPath = ToolPathResolver.GetPathExecutable("dotnet");
var snapshotIndex = i;
string xUnitOutputDirectory = OutputDirectory / $"test_{snapshotIndex:00}.testresults";
DotCoverCover(c => c
.SetTargetExecutable(dotnetPath)
.SetTargetWorkingDirectory(projectDirectory)
.SetTargetArguments($"xunit -nobuild -xml {xUnitOutputDirectory.DoubleQuoteIfNeeded()}")
.SetFilters("+:CoberturaConverter.Core")
.SetAttributeFilters("System.CodeDom.Compiler.GeneratedCodeAttribute")
.SetOutputFile(OutputDirectory / $"coverage{snapshotIndex:00}.snapshot"));
}
var snapshots = testProjects.Select((t, i) => OutputDirectory / $"coverage{i:00}.snapshot")
.Select(p => p.ToString())
.Aggregate((c, n) => c + ";" + n);
DotCoverMerge(c => c
.SetSource(snapshots)
.SetOutputFile(OutputDirectory / "coverage.snapshot"));
DotCoverReport(c => c
.SetSource(OutputDirectory / "coverage.snapshot")
.SetOutputFile(OutputDirectory / "coverage.xml")
.SetReportType(DotCoverReportType.DetailedXml));
// This is the report that's pretty and visualized in Jenkins
ReportGenerator(c => c
.SetReports(OutputDirectory / "coverage.xml")
.SetTargetDirectory(OutputDirectory / "CoverageReport"));
// This is the report in Cobertura format that integrates so nice in Jenkins
// dashboard and allows to extract more metrics and set build health based
// on coverage readings
DotCoverToCobertura(s => s
.SetInputFile(OutputDirectory / "coverage.xml")
.SetOutputFile(OutputDirectory / "cobertura_coverage.xml"))
.ConfigureAwait(false)
.GetAwaiter()
.GetResult();
});
Target Push => _ => _
.DependsOn(Pack)
.Requires(() => MyGetSource)
.Requires(() => MyGetApiKey)
.Requires(() => Configuration.EqualsOrdinalIgnoreCase("Release"))
.Executes(() =>
{
GlobFiles(OutputDirectory, "*.nupkg").NotEmpty()
.Where(x => !x.EndsWith("symbols.nupkg"))
.ForEach(x =>
{
DotNetNuGetPush(s => s
.SetTargetPath(x)
.SetSource(MyGetSource)
.SetApiKey(MyGetApiKey));
});
});
Target BuildDocFxMetadata => _ => _
.DependsOn(Restore)
.Executes(() =>
{
// So it uses a fixed, known version of MsBuild to generate the metadata. Otherwise,
// updates of dotnet or Visual Studio could introduce incompatibilities and generation failures
var dotnetPath = Path.GetDirectoryName(ToolPathResolver.GetPathExecutable("dotnet.exe"));
var msBuildPath = Path.Combine(dotnetPath, "sdk", DocFxDotNetSdkVersion, "MSBuild.dll");
SetVariable("MSBUILD_EXE_PATH", msBuildPath);
DocFxMetadata(DocFxFile, s => s.SetLogLevel(DocFxLogLevel.Verbose));
});
Target BuildDocumentation => _ => _
.DependsOn(Clean)
.DependsOn(BuildDocFxMetadata)
.Executes(() =>
{
// Using README.md as index.md
if (File.Exists(SolutionDirectory / "index.md"))
{
File.Delete(SolutionDirectory / "index.md");
}
File.Copy(SolutionDirectory / "README.md", SolutionDirectory / "index.md");
DocFxBuild(DocFxFile, s => s
.ClearXRefMaps()
.SetLogLevel(DocFxLogLevel.Verbose));
File.Delete(SolutionDirectory / "index.md");
Directory.Delete(SolutionDirectory / "core", true);
Directory.Delete(SolutionDirectory / "cli", true);
Directory.Delete(SolutionDirectory / "nuke", true);
Directory.Delete(SolutionDirectory / "obj", true);
});
Target UploadDocumentation => _ => _
.DependsOn(Push) // To have a relation between pushed package version and published docs version
.DependsOn(BuildDocumentation)
.Requires(() => DocuApiKey)
.Requires(() => DocuApiEndpoint)
.Executes(() =>
{
WebDocu(s => s
.SetDocuApiEndpoint(DocuApiEndpoint)
.SetDocuApiKey(DocuApiKey)
.SetSourceDirectory(OutputDirectory)
.SetVersion(GitVersion.NuGetVersion)
);
});
Target PublishGitHubRelease => _ => _
.DependsOn(Pack)
.Requires(() => GitHubAuthenticationToken)
.OnlyWhen(() => GitVersion.BranchName.Equals("master") || GitVersion.BranchName.Equals("origin/master"))
.Executes<Task>(async () =>
{
var releaseTag = $"v{GitVersion.MajorMinorPatch}";
var changeLogSectionEntries = ExtractChangelogSectionNotes(ChangeLogFile);
var latestChangeLog = changeLogSectionEntries
.Aggregate((c, n) => c + Environment.NewLine + n);
var completeChangeLog = $"## {releaseTag}" + Environment.NewLine + latestChangeLog;
var repositoryInfo = GetGitHubRepositoryInfo(GitRepository);
var nuGetPackages = GlobFiles(OutputDirectory, "*.nupkg").NotEmpty().ToArray();
await PublishRelease(new GitHubReleaseSettings()
.SetArtifactPaths(nuGetPackages)
.SetCommitSha(GitVersion.Sha)
.SetReleaseNotes(completeChangeLog)
.SetRepositoryName(repositoryInfo.repositoryName)
.SetRepositoryOwner(repositoryInfo.gitHubOwner)
.SetTag(releaseTag)
.SetToken(GitHubAuthenticationToken));
});
Target Generate => _ => _
.DependsOn(Restore)
.Executes(() =>
{
GenerateCode(
metadataDirectory: RootDirectory / "src" / "Nuke.CoberturaConverter",
generationBaseDirectory: RootDirectory / "src" / "Nuke.CoberturaConverter",
baseNamespace: "Nuke.CoberturaConverter"
);
});
void PrependFrameworkToTestresults()
{
var testResults = GlobFiles(OutputDirectory, "*.testresults");
foreach (var testResultFile in testResults)
{
var frameworkName = GetFrameworkNameFromFilename(testResultFile);
var xDoc = XDocument.Load(testResultFile);
foreach (var testType in ((IEnumerable) xDoc.XPathEvaluate("//test/@type")).OfType<XAttribute>())
{
testType.Value = frameworkName + "+" + testType.Value;
}
foreach (var testName in ((IEnumerable) xDoc.XPathEvaluate("//test/@name")).OfType<XAttribute>())
{
testName.Value = frameworkName + "+" + testName.Value;
}
xDoc.Save(testResultFile);
}
}
string GetFrameworkNameFromFilename(string filename)
{
var name = Path.GetFileName(filename);
name = name.Substring(0, name.Length - ".testresults".Length);
var startIndex = name.LastIndexOf('-');
name = name.Substring(startIndex + 1);
return name;
}
}

Changelog

All notable changes to CoberturaConverter are documented here.

v1.0.1

  • Support nested types when converting from dotCover
  • Exclude types without lines when converting from dotCover to Cobertura

v1.0.0

  • Initial Release
assembly-versioning-scheme: MajorMinorPatch
assembly-file-versioning-scheme: MajorMinorPatch
branches:
master:
regex: (origin/)?master
develop:
increment: Patch
tag: beta
regex: (origin/)?dev(elop)?(ment)?$
./build.ps1 UploadDocumentation+PublishGitHubRelease `
-MyGetSource $env:MyGetSource `
-MyGetApiKey $env:MyGetApiKey `
-DocuApiKey $env:DocuApiKey `
-DocuApiEndpoint $env:DocuApiEndpoint `
-GitHubAuthenticationToken $env:GitHubAuthenticationToken
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="NukeMyGet" value="https://www.myget.org/F/nukebuild/api/v3/index.json" protocolVersion="3" />
</packageSources>
</configuration>
# PowerShell
powershell -Command iwr https://nuke.build/powershell -OutFile setup.ps1
powershell -ExecutionPolicy ByPass -File ./setup.ps1
/// <summary>
/// Configuration to build. Default is <em>Debug</em> (local) or <em>Release</em> (server).
/// </summary>
[Parameter("Configuration to build. Default is 'Debug' (local) or 'Release' (server).")]
public string Configuration { get; } = EnvironmentInfo.IsLocalBuild ? "Debug" : "Release";
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment