Last active
July 27, 2022 01:21
-
-
Save GeorgDangl/ad40b2b8b5871f6f7b432946e6907f9c to your computer and use it in GitHub Desktop.
NUKE Build Example Setup, see https://blog.dangl.me/archive/escalating-automation-the-nuclear-option/
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using Nuke.CoberturaConverter; | |
| using Nuke.Common.Git; | |
| using Nuke.Common.Tools.DocFx; | |
| using Nuke.Common.Tools.DotNet; | |
| using Nuke.Common.Tools.GitVersion; | |
| 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.DotNet.DotNetTasks; | |
| using static Nuke.Core.EnvironmentInfo; | |
| using static Nuke.Core.IO.FileSystemTasks; | |
| using static Nuke.Core.IO.PathConstruction; | |
| 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. | |
| // Parameters, passed via script invokation in the form of | |
| // -MyGetSource "https://myget.org/f/my-feed" | |
| [Parameter] string MyGetSource; | |
| [Parameter] string MyGetApiKey; | |
| [Parameter] string DocuApiKey; | |
| [Parameter] string DocuApiEndpoint; | |
| [Parameter] string GitHubAuthenticationToken; | |
| string DocFxFile => SolutionDirectory / "docfx.json"; | |
| string ChangeLogFile => RootDirectory / "CHANGELOG.md"; | |
| Target Clean => _ => _ | |
| .Executes(() => | |
| { | |
| DeleteDirectories(GlobDirectories(SourceDirectory, "**/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); | |
| var dotnetXunitSettings = new DotNetSettings() | |
| // Need to set it here, otherwise it takes the one from NUKEs .tmp directory | |
| .SetToolPath(ToolPathResolver.GetPathExecutable("dotnet")) | |
| .SetWorkingDirectory(projectDirectory) | |
| .SetArgumentConfigurator(c => c.Add("xunit") | |
| .Add("-nobuild") | |
| .Add("-xml {value}", "\"" + OutputDirectory / $"test_{testRun++}.testresults" + "\"")); | |
| ProcessTasks.StartProcess(dotnetXunitSettings) | |
| .AssertWaitForExit(); | |
| } | |
| PrependFrameworkToTestresults(); | |
| }); | |
| Target Coverage => _ => _ | |
| .DependsOn(Compile) | |
| .Executes(() => | |
| { | |
| var testProjects = GlobFiles(SolutionDirectory / "test", "*.csproj").ToList(); | |
| var dotnetPath = ToolPathResolver.GetPathExecutable("dotnet"); | |
| for (var i = 0; i < testProjects.Count; i++) | |
| { | |
| var testProject = testProjects[i]; | |
| var projectDirectory = Path.GetDirectoryName(testProject); | |
| var snapshotIndex = i; | |
| var toolSettings = new ToolSettings() | |
| .SetToolPath(ToolPathResolver.GetPackageExecutable("JetBrains.dotCover.CommandLineTools", "tools/dotCover.exe")) | |
| .SetArgumentConfigurator(a => a | |
| .Add("cover") | |
| .Add($"/TargetExecutable=\"{dotnetPath}\"") | |
| .Add($"/TargetWorkingDir=\"{projectDirectory}\"") | |
| .Add($"/TargetArguments=\"xunit -nobuild -xml \"\"{OutputDirectory / $"test_{snapshotIndex:00}.testresults"}\"\"\"") | |
| .Add("/Filters=\"+:CoberturaConverter.Core\"") | |
| .Add("/AttributeFilters=\"System.CodeDom.Compiler.GeneratedCodeAttribute\"") | |
| .Add($"/Output=\"{OutputDirectory / $"coverage{snapshotIndex:00}.snapshot"}\"")); | |
| ProcessTasks.StartProcess(toolSettings) | |
| .AssertZeroExitCode(); | |
| } | |
| var snapshots = testProjects.Select((t, i) => OutputDirectory / $"coverage{i:00}.snapshot") | |
| .Select(p => p.ToString()) | |
| .Aggregate((c, n) => c + ";" + n); | |
| var mergeSettings = new ToolSettings() | |
| .SetToolPath(ToolPathResolver.GetPackageExecutable("JetBrains.dotCover.CommandLineTools", "tools/dotCover.exe")) | |
| .SetArgumentConfigurator(a => a | |
| .Add("merge") | |
| .Add($"/Source=\"{snapshots}\"") | |
| .Add($"/Output=\"{OutputDirectory / "coverage.snapshot"}\"")); | |
| ProcessTasks.StartProcess(mergeSettings) | |
| .AssertZeroExitCode(); | |
| var reportSettings = new ToolSettings() | |
| .SetToolPath(ToolPathResolver.GetPackageExecutable("JetBrains.dotCover.CommandLineTools", "tools/dotCover.exe")) | |
| .SetArgumentConfigurator(a => a | |
| .Add("report") | |
| .Add($"/Source=\"{OutputDirectory / "coverage.snapshot"}\"") | |
| .Add($"/Output=\"{OutputDirectory / "coverage.xml"}\"") | |
| .Add("/ReportType=\"DetailedXML\"")); | |
| ProcessTasks.StartProcess(reportSettings) | |
| .AssertZeroExitCode(); | |
| // This is the report that's pretty and visualized in Jenkins | |
| var reportGeneratorSettings = new ToolSettings() | |
| .SetToolPath(ToolPathResolver.GetPackageExecutable("ReportGenerator", "tools/ReportGenerator.exe")) | |
| .SetArgumentConfigurator(a => a | |
| .Add($"-reports:\"{OutputDirectory / "coverage.xml"}\"") | |
| .Add($"-targetdir:\"{OutputDirectory / "CoverageReport"}\"")); | |
| ProcessTasks.StartProcess(reportGeneratorSettings) | |
| .AssertZeroExitCode(); | |
| // 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(() => | |
| { | |
| if (IsLocalBuild) | |
| { | |
| SetVariable("VSINSTALLDIR", @"C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional"); | |
| SetVariable("VisualStudioVersion", "15.0"); | |
| } | |
| 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; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # PowerShell | |
| powershell -Command iwr https://nuke.build/powershell -OutFile setup.ps1 | |
| powershell -ExecutionPolicy ByPass -File ./setup.ps1 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment