#tool "nuget:?package=GitVersion.CommandLine&version=5.0.1" #tool "nuget:?package=GitReleaseNotes" #addin nuget:?package=Cake.Json #addin nuget:?package=Newtonsoft.Json #tool "nuget:?package=ReportGenerator" #tool "nuget:?package=coveralls.net&version=0.7.0" #addin Cake.Coveralls&version=0.10.1 // compile var compileConfig = Argument("configuration", "Release"); var slnFile = "./Ocelot.sln"; // build artifacts var artifactsDir = Directory("artifacts"); // unit testing var artifactsForUnitTestsDir = artifactsDir + Directory("UnitTests"); var unitTestAssemblies = @"./test/Ocelot.UnitTests/Ocelot.UnitTests.csproj"; var minCodeCoverage = 80d; var coverallsRepoToken = "coveralls-repo-token-ocelot"; var coverallsRepo = "https://coveralls.io/github/TomPallister/Ocelot"; // acceptance testing var artifactsForAcceptanceTestsDir = artifactsDir + Directory("AcceptanceTests"); var acceptanceTestAssemblies = @"./test/Ocelot.AcceptanceTests/Ocelot.AcceptanceTests.csproj"; // integration testing var artifactsForIntegrationTestsDir = artifactsDir + Directory("IntegrationTests"); var integrationTestAssemblies = @"./test/Ocelot.IntegrationTests/Ocelot.IntegrationTests.csproj"; // benchmark testing var artifactsForBenchmarkTestsDir = artifactsDir + Directory("BenchmarkTests"); var benchmarkTestAssemblies = @"./test/Ocelot.Benchmarks"; // packaging var packagesDir = artifactsDir + Directory("Packages"); var releaseNotesFile = packagesDir + File("releasenotes.md"); var artifactsFile = packagesDir + File("artifacts.txt"); // stable releases var tagsUrl = "https://api.github.com/repos/tompallister/ocelot/releases/tags/"; var nugetFeedStableKey = EnvironmentVariable("nuget-apikey-stable"); var nugetFeedStableUploadUrl = "https://www.nuget.org/api/v2/package"; var nugetFeedStableSymbolsUploadUrl = "https://www.nuget.org/api/v2/package"; // internal build variables - don't change these. var releaseTag = ""; string committedVersion = "0.0.0-dev"; var buildVersion = committedVersion; GitVersion versioning = null; var target = Argument("target", "Default"); Information("target is " + target); Information("Build configuration is " + compileConfig); Task("Default") .IsDependentOn("Build"); Task("Build") .IsDependentOn("RunTests") .IsDependentOn("CreatePackages"); Task("Clean") .Does(() => { if (DirectoryExists(artifactsDir)) { DeleteDirectory(artifactsDir, recursive:true); } CreateDirectory(artifactsDir); }); Task("Version") .Does(() => { versioning = GetNuGetVersionForCommit(); var nugetVersion = versioning.NuGetVersion; Information("SemVer version number: " + nugetVersion); if (IsRunningOnCircleCI()) { Information("Persisting version number..."); PersistVersion(committedVersion, nugetVersion); buildVersion = nugetVersion; } else { Information("We are not running on build server, so we won't persist the version number."); } }); Task("Compile") .IsDependentOn("Clean") .IsDependentOn("Version") .Does(() => { var settings = new DotNetCoreBuildSettings { Configuration = compileConfig, }; DotNetCoreBuild(slnFile, settings); }); Task("RunUnitTests") .IsDependentOn("Compile") .Does(() => { var testSettings = new DotNetCoreTestSettings { Configuration = compileConfig, ResultsDirectory = artifactsForUnitTestsDir, ArgumentCustomization = args => args .Append("--settings test/Ocelot.UnitTests/UnitTests.runsettings") }; EnsureDirectoryExists(artifactsForUnitTestsDir); DotNetCoreTest(unitTestAssemblies, testSettings); if (IsRunningOnWindows()) { var coverageSummaryFile = GetSubDirectories(artifactsForUnitTestsDir).First().CombineWithFilePath(File("coverage.opencover.xml")); ReportGenerator(coverageSummaryFile, artifactsForUnitTestsDir); if (IsRunningOnCircleCI()) { var repoToken = EnvironmentVariable(coverallsRepoToken); if (string.IsNullOrEmpty(repoToken)) { throw new Exception(string.Format("Coveralls repo token not found. Set environment variable '{0}'", coverallsRepoToken)); } Information(string.Format("Uploading test coverage to {0}", coverallsRepo)); CoverallsNet(coverageSummaryFile, CoverallsNetReportType.OpenCover, new CoverallsNetSettings() { RepoToken = repoToken }); } else { Information("We are not running on the build server so we won't publish the coverage report to coveralls.io"); } var sequenceCoverage = XmlPeek(coverageSummaryFile, "//CoverageSession/Summary/@sequenceCoverage"); var branchCoverage = XmlPeek(coverageSummaryFile, "//CoverageSession/Summary/@branchCoverage"); Information("Sequence Coverage: " + sequenceCoverage); if(double.Parse(sequenceCoverage) < minCodeCoverage) { var whereToCheck = !IsRunningOnCircleCI() ? coverallsRepo : artifactsForUnitTestsDir; throw new Exception(string.Format("Code coverage fell below the threshold of {0}%. You can find the code coverage report at {1}", minCodeCoverage, whereToCheck)); }; } }); Task("RunAcceptanceTests") .IsDependentOn("Compile") .Does(() => { var settings = new DotNetCoreTestSettings { Configuration = compileConfig, ArgumentCustomization = args => args .Append("--no-restore") .Append("--no-build") }; EnsureDirectoryExists(artifactsForAcceptanceTestsDir); DotNetCoreTest(acceptanceTestAssemblies, settings); }); Task("RunIntegrationTests") .IsDependentOn("Compile") .Does(() => { var settings = new DotNetCoreTestSettings { Configuration = compileConfig, ArgumentCustomization = args => args .Append("--no-restore") .Append("--no-build") }; EnsureDirectoryExists(artifactsForIntegrationTestsDir); DotNetCoreTest(integrationTestAssemblies, settings); }); Task("RunTests") .IsDependentOn("RunUnitTests") .IsDependentOn("RunAcceptanceTests") .IsDependentOn("RunIntegrationTests"); Task("CreatePackages") .IsDependentOn("Compile") .Does(() => { EnsureDirectoryExists(packagesDir); CopyFiles("./src/**/Release/Ocelot.*.nupkg", packagesDir); // todo fix this for docker build //GenerateReleaseNotes(releaseNotesFile); var projectFiles = GetFiles("./src/**/Release/Ocelot.*.nupkg"); foreach(var projectFile in projectFiles) { System.IO.File.AppendAllLines(artifactsFile, new[]{ projectFile.GetFilename().FullPath, // todo fix this for docker build //"releaseNotes:releasenotes.md" }); } var artifacts = System.IO.File .ReadAllLines(artifactsFile) .Distinct(); foreach(var artifact in artifacts) { var codePackage = packagesDir + File(artifact); Information("Created package " + codePackage); } if (IsRunningOnCircleCI()) { var path = packagesDir.ToString() + @"/**/*"; foreach (var file in GetFiles(path)) { // todo - upload to github releases? // AppVeyor.UploadArtifact(file.FullPath); } } }); Task("EnsureStableReleaseRequirements") .Does(() => { Information("Check if stable release..."); if (!IsRunningOnCircleCI()) { throw new Exception("Stable release should happen via circleci"); } // todo how this on circle? // var isTag = // AppVeyor.Environment.Repository.Tag.IsTag && // !string.IsNullOrWhiteSpace(AppVeyor.Environment.Repository.Tag.Name); // if (!isTag) // { // throw new Exception("Stable release should happen from a published GitHub release"); // } Information("Release is stable..."); }); Task("UpdateVersionInfo") .IsDependentOn("EnsureStableReleaseRequirements") .Does(() => { // todo need a way to get the tag releaseTag = AppVeyor.Environment.Repository.Tag.Name; }); Task("DownloadGitHubReleaseArtifacts") .IsDependentOn("UpdateVersionInfo") .Does(() => { try { Information("DownloadGitHubReleaseArtifacts"); EnsureDirectoryExists(packagesDir); Information("Directory exists..."); var releaseUrl = tagsUrl + releaseTag; Information("Release url " + releaseUrl); var assets_url = Newtonsoft.Json.Linq.JObject.Parse(GetResource(releaseUrl)) .Value("assets_url"); Information("Assets url " + assets_url); var assets = GetResource(assets_url); Information("Assets " + assets_url); foreach(var asset in Newtonsoft.Json.JsonConvert.DeserializeObject(assets)) { Information("In the loop.."); var file = packagesDir + File(asset.Value("name")); Information("Downloading " + file); DownloadFile(asset.Value("browser_download_url"), file); } Information("Out of the loop..."); } catch(Exception exception) { Information("There was an exception " + exception); throw; } }); Task("ReleasePackagesToStableFeed") .IsDependentOn("DownloadGitHubReleaseArtifacts") .Does(() => { PublishPackages(packagesDir, artifactsFile, nugetFeedStableKey, nugetFeedStableUploadUrl, nugetFeedStableSymbolsUploadUrl); }); Task("Release") .IsDependentOn("ReleasePackagesToStableFeed"); RunTarget(target); /// Gets nuique nuget version for this commit private GitVersion GetNuGetVersionForCommit() { GitVersion(new GitVersionSettings{ UpdateAssemblyInfo = false, OutputType = GitVersionOutput.BuildServer }); return GitVersion(new GitVersionSettings{ OutputType = GitVersionOutput.Json }); } /// Updates project version in all of our projects private void PersistVersion(string committedVersion, string newVersion) { Information(string.Format("We'll search all csproj files for {0} and replace with {1}...", committedVersion, newVersion)); var projectFiles = GetFiles("./**/*.csproj"); foreach(var projectFile in projectFiles) { var file = projectFile.ToString(); Information(string.Format("Updating {0}...", file)); var updatedProjectFile = System.IO.File.ReadAllText(file) .Replace(committedVersion, newVersion); System.IO.File.WriteAllText(file, updatedProjectFile); } } /// generates release notes based on issues closed in GitHub since the last release private void GenerateReleaseNotes(ConvertableFilePath file) { if(!IsRunningOnWindows()) { Warning("We are not running on Windows so we cannot generate release notes."); return; } Information("Generating release notes at " + file); var releaseNotesExitCode = StartProcess( @"tools/GitReleaseNotes/tools/gitreleasenotes.exe", new ProcessSettings { Arguments = ". /o " + file }); if (string.IsNullOrEmpty(System.IO.File.ReadAllText(file))) { System.IO.File.WriteAllText(file, "No issues closed since last release"); } if (releaseNotesExitCode != 0) { throw new Exception("Failed to generate release notes"); } } /// Publishes code and symbols packages to nuget feed, based on contents of artifacts file private void PublishPackages(ConvertableDirectoryPath packagesDir, ConvertableFilePath artifactsFile, string feedApiKey, string codeFeedUrl, string symbolFeedUrl) { var artifacts = System.IO.File .ReadAllLines(artifactsFile) .Distinct(); foreach(var artifact in artifacts) { var codePackage = packagesDir + File(artifact); Information("Pushing package " + codePackage); Information("Calling NuGetPush"); NuGetPush( codePackage, new NuGetPushSettings { ApiKey = feedApiKey, Source = codeFeedUrl }); } } /// gets the resource from the specified url private string GetResource(string url) { try { Information("Getting resource from " + url); var assetsRequest = System.Net.WebRequest.CreateHttp(url); assetsRequest.Method = "GET"; assetsRequest.Accept = "application/vnd.github.v3+json"; assetsRequest.UserAgent = "BuildScript"; using (var assetsResponse = assetsRequest.GetResponse()) { var assetsStream = assetsResponse.GetResponseStream(); var assetsReader = new StreamReader(assetsStream); var response = assetsReader.ReadToEnd(); Information("Response is " + response); return response; } } catch(Exception exception) { Information("There was an exception " + exception); throw; } } private bool IsRunningOnCircleCI() { return !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("CIRCLECI")); }