|
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; |
|
} |
|
} |