Build/Program.cs (236 lines of code) (raw):

using System.IO.Compression; using System.Xml; using HostApi; using JetBrains.TeamCity.ServiceMessages.Write.Special; using NuGet.Versioning; const string solutionFile = "TeamCity.CSharpInteractive.sln"; const string packageId = "TeamCity.CSharpInteractive"; const string toolPackageId = "TeamCity.csi"; const string templatesPackageId = "TeamCity.CSharpInteractive.Templates"; var frameworks = new[] {"net6.0", "net7.0"}; var currentDir = Environment.CurrentDirectory; if (!File.Exists(solutionFile)) { Error($"Cannot find the solution \"{solutionFile}\". Current directory is \"{currentDir}\"."); return 1; } var configuration = Property.Get("configuration", "Release"); var apiKey = Property.Get("apiKey", ""); var integrationTests = bool.Parse(Property.Get("integrationTests", Tools.UnderTeamCity.ToString())); var defaultVersion = NuGetVersion.Parse(Property.Get("version", "1.0.0-dev", Tools.UnderTeamCity)); var outputDir = Path.Combine(currentDir, "TeamCity.CSharpInteractive", "bin", configuration); var templateOutputDir = Path.Combine(currentDir, "TeamCity.CSharpInteractive.Templates", "bin", configuration); var dockerLinuxTests = false; new DockerCustom("info").WithShortName("Defining a docker container type") .Run(output => { WriteLine(" " + output.Line, Color.Details); if (output.Line.Contains("OSType: linux")) { dockerLinuxTests = true; } }); if (!dockerLinuxTests) { Warning("The docker Linux container is not available. Integration tests are skipped."); } var packageVersion = new[] { Version.GetNext(new NuGetRestoreSettings(toolPackageId).WithPackageType(NuGetPackageType.Tool), defaultVersion), Version.GetNext(new NuGetRestoreSettings(packageId), defaultVersion) }.Max()!; var templatePackageVersion = Version.GetNext(new NuGetRestoreSettings(templatesPackageId), new NuGetVersion(1, 0, 0)); var packages = new[] { new PackageInfo( packageId, Path.Combine("TeamCity.CSharpInteractive", "TeamCity.CSharpInteractive.csproj"), Path.Combine(outputDir, "TeamCity.CSharpInteractive", $"{packageId}.{packageVersion.ToString()}.nupkg"), packageVersion, true), new PackageInfo( toolPackageId, Path.Combine("TeamCity.CSharpInteractive", "TeamCity.CSharpInteractive.Tool.csproj"), Path.Combine(outputDir, "TeamCity.CSharpInteractive.Tool", $"{toolPackageId}.{packageVersion.ToString()}.nupkg"), packageVersion, true), new PackageInfo( templatesPackageId, Path.Combine("TeamCity.CSharpInteractive.Templates", "TeamCity.CSharpInteractive.Templates.csproj"), Path.Combine(templateOutputDir, $"{templatesPackageId}.{templatePackageVersion.ToString()}.nupkg"), templatePackageVersion, false) }; Assertion.Succeed( new DotNetToolRestore() .Run(), "Restore tools" ); Assertion.Succeed( new DotNetClean() .WithProject(solutionFile) .WithVerbosity(DotNetVerbosity.Quiet) .WithConfiguration(configuration) .Build() ); foreach (var package in packages) { var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages", package.Id, package.Version.ToString()); if (Directory.Exists(path)) { Directory.Delete(path, true); } } var buildProps = new[] {("version", packageVersion.ToString())}; Assertion.Succeed( new DotNetClean() .WithProject(solutionFile) .WithConfiguration(configuration) .Build()); Assertion.Succeed( new DotNetBuild() .WithProject(solutionFile) .WithConfiguration(configuration) .WithProps(buildProps) .Build()); var reportDir = Path.Combine(currentDir, ".reports"); if (Directory.Exists(reportDir)) { Directory.Delete(reportDir, true); } var test = new DotNetTest() .WithProject(solutionFile) .WithConfiguration(configuration) .WithNoBuild(true) .WithProps(buildProps) .WithFilter("Integration!=true&Docker!=true"); var dotCoverSnapshot = Path.Combine(reportDir, "dotCover.dcvr"); Assertion.Succeed( test .Customize(cmd => cmd.WithArgs("dotcover") .AddArgs(cmd.Args) .AddArgs( $"--dcOutput={dotCoverSnapshot}", "--dcFilters=+:module=TeamCity.CSharpInteractive.HostApi;+:module=dotnet-csi", "--dcAttributeFilters=System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage")) .Build()); var dotCoverReportDir = Path.Combine(reportDir, "html"); var dotCoverReportHtml = Path.Combine(dotCoverReportDir, "index.html"); var dotCoverReportXml = Path.Combine(reportDir, "dotCover.xml"); Assertion.Succeed(new DotNetCustom("dotCover", "report", $"--source={dotCoverSnapshot}", $"--output={dotCoverReportHtml};{dotCoverReportXml}", "--reportType=HTML,TeamCityXml").Run(), "Generating the code coverage reports"); var dotCoverReportDoc = new XmlDocument(); dotCoverReportDoc.Load(dotCoverReportXml); var coveragePercentageValue = dotCoverReportDoc.SelectNodes("Root")?.Item(0)?.Attributes?["CoveragePercent"]?.Value; if (int.TryParse(coveragePercentageValue, out var coveragePercentage)) { switch (coveragePercentage) { case < 80: Error($"The coverage percentage {coveragePercentage} is too low. See {dotCoverReportHtml} for details."); Assertion.Exit(); break; case < 85: Warning($"The coverage percentage {coveragePercentage} is too low. See {dotCoverReportHtml} for details."); break; } } else { Warning("Coverage percentage was not found."); } var dotCoverReportZip = Path.Combine(reportDir, "dotCover.zip"); ZipFile.CreateFromDirectory(dotCoverReportDir, dotCoverReportZip); var teamCityWriter = GetService<ITeamCityWriter>(); teamCityWriter.PublishArtifact($"{dotCoverReportZip} => ."); foreach (var package in packages) { var packageOutput = Path.GetDirectoryName(package.Package); if (Directory.Exists(packageOutput)) { Directory.Delete(packageOutput, true); } Assertion.Succeed(new DotNetPack() .WithConfiguration(configuration) .WithProps(("version", package.Version.ToString())) .WithProject(package.Project) .Build()); } var uninstallTool = new DotNetCustom("tool", "uninstall", toolPackageId, "-g") .WithShortName("Uninstalling tool"); if (uninstallTool.Run(output => WriteLine(output.Line)) != 0) { Warning($"{uninstallTool} failed."); } var installTool = new DotNetCustom("tool", "install", toolPackageId, "-g", "--version", packageVersion.ToString(), "--add-source", Path.Combine(outputDir, "TeamCity.CSharpInteractive.Tool")) .WithShortName("Installing tool"); if (installTool.Run(output => WriteLine(output.Line)) != 0) { Warning($"{installTool} failed."); } Assertion.Succeed(new DotNetCustom("csi", "/?").Run(), "Checking tool"); var uninstallTemplates = new DotNetCustom("new", "-u", templatesPackageId) .WithShortName("Uninstalling template"); if (uninstallTemplates.Run(output => WriteLine(output.Line)) != 0) { Warning($"{uninstallTemplates} failed."); } var installTemplates = new DotNetCustom("new", "-i", $"{templatesPackageId}::{templatePackageVersion.ToString()}", "--nuget-source", templateOutputDir) .WithShortName("Installing template"); Assertion.Succeed(installTemplates.Run(), installTemplates.ShortName); foreach (var framework in frameworks) { var buildProjectDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()[..8]); Directory.CreateDirectory(buildProjectDir); var sampleProjectName = $"sample project for {framework}"; try { var sampleProjectDir = Path.Combine("Samples", "DemoProject", "MySampleLib", "MySampleLib.Tests"); Assertion.Succeed(new DotNetCustom("new", "build", $"--package-version={packageVersion}", "-T", framework, "--no-restore").WithWorkingDirectory(buildProjectDir).Run(), $"Creating a new {sampleProjectName}"); Assertion.Succeed(new DotNetBuild().WithProject(buildProjectDir).AddSources(Path.Combine(outputDir, "TeamCity.CSharpInteractive")).WithShortName($"Building the {sampleProjectName}").Build()); Assertion.Succeed(new DotNetRun().WithProject(buildProjectDir).WithNoBuild(true).WithWorkingDirectory(sampleProjectDir).Run(), $"Running a build for the {sampleProjectName}"); Assertion.Succeed(new CommandLine("dotnet", "csi", Path.Combine(buildProjectDir, "Program.csx")).WithWorkingDirectory(sampleProjectDir).Run(), $"Running a build as a C# script for the {sampleProjectName}"); } finally { Directory.Delete(buildProjectDir, true); } } Info("Publishing artifacts."); foreach (var package in packages) { if (!File.Exists(package.Package)) { Error($"NuGet package {package.Package} does not exist."); return 1; } teamCityWriter.PublishArtifact($"{package.Package} => ."); } if (!string.IsNullOrWhiteSpace(apiKey) && packageVersion.Release != "dev" && templatePackageVersion.Release != "dev") { var push = new DotNetNuGetPush().WithApiKey(apiKey).WithSources("https://api.nuget.org/v3/index.json"); foreach (var package in packages.Where(i => i.Publish)) { Assertion.Succeed(push.WithPackage(package.Package).Run(), $"Pushing {Path.GetFileName(package.Package)}"); } } else { Info("Pushing NuGet packages were skipped."); } if (integrationTests || dockerLinuxTests) { var logicOp = integrationTests && dockerLinuxTests ? "|" : "&"; var filter = $"Integration={integrationTests}{logicOp}Docker={dockerLinuxTests}"; Assertion.Succeed(test.WithFilter(filter).Build()); } WriteLine("To use the csi tool:", Color.Highlighted); WriteLine(" dotnet csi /?", Color.Highlighted); WriteLine("To create a build project from the template:", Color.Highlighted); WriteLine($" dotnet new build --package-version={packageVersion}", Color.Highlighted); WriteLine($"Tool and package version: {packageVersion}", Color.Highlighted); WriteLine($"Template version: {templatePackageVersion}", Color.Highlighted); WriteLine($"The coverage percentage: {coveragePercentage}", Color.Highlighted); return 0; record PackageInfo(string Id, string Project, string Package, NuGetVersion Version, bool Publish);