diff --git a/GitVersionConfig.yaml b/GitVersionConfig.yaml new file mode 100644 index 0000000..c12fe19 --- /dev/null +++ b/GitVersionConfig.yaml @@ -0,0 +1 @@ +next-version: 0.1.0 \ No newline at end of file diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..8dbd671 --- /dev/null +++ b/build.bat @@ -0,0 +1,6 @@ +@ECHO OFF +PUSHD %~dp0 +PowerShell.exe -NoProfile -ExecutionPolicy Bypass -Command "& './build.ps1'" + +IF %errorlevel% neq 0 PAUSE + diff --git a/build.cake b/build.cake new file mode 100644 index 0000000..0f3f3a6 --- /dev/null +++ b/build.cake @@ -0,0 +1,328 @@ +/////////////////////////////////////////////////////////////////////////////// +// ARGUMENTS +/////////////////////////////////////////////////////////////////////////////// + +var target = Argument("target", "Default"); +var configuration = Argument("configuration", "Release"); + +////////////////////////////////////////////////////////////////////// +// EXTERNAL NUGET TOOLS +////////////////////////////////////////////////////////////////////// + +#Tool "xunit.runner.console" +#Tool "GitVersion.CommandLine" +#Tool "Brutal.Dev.StrongNameSigner" + +////////////////////////////////////////////////////////////////////// +// EXTERNAL NUGET LIBRARIES +////////////////////////////////////////////////////////////////////// + +#addin "Cake.FileHelpers" +#addin "System.Text.Json" +using System.Text.Json; + +/////////////////////////////////////////////////////////////////////////////// +// GLOBAL VARIABLES +/////////////////////////////////////////////////////////////////////////////// + +var projectName = "Polly.Extensions.Http"; +var keyName = "Polly.snk"; + +var solutions = GetFiles("./**/*.sln"); +var solutionPaths = solutions.Select(solution => solution.GetDirectory()); + +var srcDir = Directory("./src"); +var buildDir = Directory("./build"); +var artifactsDir = Directory("./artifacts"); +var testResultsDir = artifactsDir + Directory("test-results"); + +// NuGet +var nuspecExtension = ".nuspec"; +var signed = "-Signed"; +var nuspecFolder = "nuget-package"; +var nuspecSrcFile = srcDir + File(projectName + nuspecExtension); +var nuspecDestFile = buildDir + File(projectName + nuspecExtension); +var nuspecSignedDestFile = buildDir + File(projectName + signed + nuspecExtension); +var nupkgUnsignedDestDir = artifactsDir + Directory(nuspecFolder); +var nupkgSignedDestDir = artifactsDir + Directory(nuspecFolder + signed); +var snkFile = srcDir + File(keyName); + +var projectToNugetFolderMap = new Dictionary() { + { "NetStandard11", new [] {"netstandard1.1"} }, + { "NetStandard11-Signed", new [] {"netstandard1.1"} }, +}; + +// Gitversion +var gitVersionPath = ToolsExePath("GitVersion.exe"); +Dictionary gitVersionOutput; + +// StrongNameSigner +var strongNameSignerPath = ToolsExePath("StrongNameSigner.Console.exe"); + + +/////////////////////////////////////////////////////////////////////////////// +// SETUP / TEARDOWN +/////////////////////////////////////////////////////////////////////////////// + +Setup(_ => +{ + Information(""); + Information(@" ____ __ __ __ _ _ ____ _ _ ____ ____ __ _ ____ __ __ __ _ ____ _ _ ____ ____ ____ "); + Information(@"( _ \ / \ ( ) ( ) ( \/ ) ( __)( \/ )(_ _)( __)( ( \/ ___)( )/ \ ( ( \/ ___) / )( \(_ _)(_ _)( _ \"); + Information(@" ) __/( O )/ (_/\/ (_/\ ) /_ ) _) ) ( )( ) _) / /\___ \ )(( O )/ /\___ \ _ ) __ ( )( )( ) __/"); + Information(@"(__) \__/ \____/\____/(__/(_)(____)(_/\_) (__) (____)\_)__)(____/(__)\__/ \_)__)(____/(_)\_)(_/ (__) (__) (__) "); + + Information(""); +}); + +Teardown(_ => +{ + Information("Finished running tasks."); +}); + +////////////////////////////////////////////////////////////////////// +// PRIVATE TASKS +////////////////////////////////////////////////////////////////////// + +Task("__Clean") + .Does(() => +{ + DirectoryPath[] cleanDirectories = new DirectoryPath[] { + buildDir, + testResultsDir, + nupkgUnsignedDestDir, + nupkgSignedDestDir, + artifactsDir + }; + + CleanDirectories(cleanDirectories); + + foreach(var path in cleanDirectories) { EnsureDirectoryExists(path); } + + foreach(var path in solutionPaths) + { + Information("Cleaning {0}", path); + CleanDirectories(path + "/**/bin/" + configuration); + CleanDirectories(path + "/**/obj/" + configuration); + } +}); + +Task("__RestoreNugetPackages") + .Does(() => +{ + foreach(var solution in solutions) + { + Information("Restoring NuGet Packages for {0}", solution); + NuGetRestore(solution); + } +}); + +Task("__UpdateAssemblyVersionInformation") + .Does(() => +{ + var gitVersionSettings = new ProcessSettings() + .SetRedirectStandardOutput(true); + + IEnumerable outputLines; + StartProcess(gitVersionPath, gitVersionSettings, out outputLines); + + var output = string.Join("\n", outputLines); + gitVersionOutput = new JsonParser().Parse>(output); + + Information("Updated GlobalAssemblyInfo"); + Information("AssemblyVersion -> {0}", gitVersionOutput["AssemblySemVer"]); + Information("AssemblyFileVersion -> {0}", gitVersionOutput["MajorMinorPatch"]); + Information("AssemblyInformationalVersion -> {0}", gitVersionOutput["InformationalVersion"]); +}); + +Task("__UpdateDotNetStandardAssemblyVersionNumber") + .Does(() => +{ + // NOTE: TEMPORARY fix only, while GitVersionTask does not support .Net Standard assemblies. See https://github.com/App-vNext/Polly/issues/176. + // This build Task can be removed when GitVersionTask supports .Net Standard assemblies. + var assemblySemVer = gitVersionOutput["AssemblySemVer"].ToString(); + Information("Updating NetStandard AssemblyVersions to {0}", assemblySemVer); + var assemblyInfosToUpdate = GetFiles("./src/**/Properties/AssemblyInfo.cs") + .Select(f => f.FullPath) + .Where(f => !f.Contains("Specs")); + + foreach(var assemblyInfo in assemblyInfosToUpdate) { + var replacedFiles = ReplaceRegexInFiles(assemblyInfo, "AssemblyVersion[(]\".*\"[)]", "AssemblyVersion(\"" + assemblySemVer +"\")"); + if (!replacedFiles.Any()) + { + throw new Exception($"AssemblyVersion could not be updated in {assemblyInfo}."); + } + } +}); + +Task("__UpdateAppVeyorBuildNumber") + .WithCriteria(() => AppVeyor.IsRunningOnAppVeyor) + .Does(() => +{ + var fullSemVer = gitVersionOutput["FullSemVer"].ToString(); + AppVeyor.UpdateBuildVersion(fullSemVer); +}); + +Task("__BuildSolutions") + .Does(() => +{ + foreach(var solution in solutions) + { + Information("Building {0}", solution); + + MSBuild(solution, settings => + settings + .SetConfiguration(configuration) + .WithProperty("TreatWarningsAsErrors", "true") + .UseToolVersion(MSBuildToolVersion.VS2017) + .SetVerbosity(Verbosity.Minimal) + .SetNodeReuse(false)); + } +}); + +Task("__RunTests") + .Does(() => +{ + foreach(var specsProj in GetFiles("./src/**/*.Specs.csproj")) { + DotNetCoreTest(specsProj.FullPath, new DotNetCoreTestSettings { + Configuration = configuration, + NoBuild = true + }); + } +}); + +Task("__CopyNonSignedOutputToNugetFolder") + .Does(() => +{ + foreach(var project in projectToNugetFolderMap.Keys + .Where(p => !p.Contains(signed)) + ) { + var sourceDir = srcDir + Directory(projectName + "." + project) + Directory("bin") + Directory(configuration); + + foreach(var targetFolder in projectToNugetFolderMap[project]) { + var destDir = buildDir + Directory("lib"); + + Information("Copying {0} -> {1}.", sourceDir, destDir); + CopyDirectory(sourceDir, destDir); + } + } + + CopyFile(nuspecSrcFile, nuspecDestFile); +}); + +Task("__CopySignedOutputToNugetFolder") + .Does(() => +{ + foreach(var project in projectToNugetFolderMap.Keys + .Where(p => p.Contains(signed)) + ) { + var sourceDir = srcDir + Directory(projectName + "." + project) + Directory("bin") + Directory(configuration); + + foreach(var targetFolder in projectToNugetFolderMap[project]) { + var destDir = buildDir + Directory("lib"); + + Information("Copying {0} -> {1}.", sourceDir, destDir); + CopyDirectory(sourceDir, destDir); + } + } + + CopyFile(nuspecSrcFile, nuspecSignedDestFile); + + var replacedFiles = ReplaceTextInFiles(nuspecSignedDestFile, "dependency id=\"Polly\"", "dependency id=\"Polly-Signed\""); + if (!replacedFiles.Any()) + { + throw new Exception("Could not set Polly dependency to Polly-Signed, for -Signed nuget package."); + } +}); + +Task("__CreateNonSignedNugetPackage") + .Does(() => +{ + var nugetVersion = gitVersionOutput["NuGetVersion"].ToString(); + var packageName = projectName; + + Information("Building {0}.{1}.nupkg", packageName, nugetVersion); + + var nuGetPackSettings = new NuGetPackSettings { + Id = packageName, + Title = packageName, + Version = nugetVersion, + OutputDirectory = nupkgUnsignedDestDir + }; + + NuGetPack(nuspecDestFile, nuGetPackSettings); +}); + +Task("__CreateSignedNugetPackage") + .Does(() => +{ + var nugetVersion = gitVersionOutput["NuGetVersion"].ToString(); + var packageName = projectName + "-Signed"; + + Information("Building {0}.{1}.nupkg", packageName, nugetVersion); + + var nuGetPackSettings = new NuGetPackSettings { + Id = packageName, + Title = packageName, + Version = nugetVersion, + OutputDirectory = nupkgSignedDestDir + }; + + NuGetPack(nuspecSignedDestFile, nuGetPackSettings); +}); + +Task("__StronglySignAssemblies") + .Does(() => +{ + //see: https://github.com/brutaldev/StrongNameSigner + var strongNameSignerSettings = new ProcessSettings() + .WithArguments(args => args + .Append("-in") + .AppendQuoted(buildDir) + .Append("-k") + .AppendQuoted(snkFile) + .Append("-l") + .AppendQuoted("Changes")); + + StartProcess(strongNameSignerPath, strongNameSignerSettings); +}); + +////////////////////////////////////////////////////////////////////// +// BUILD TASKS +////////////////////////////////////////////////////////////////////// + +Task("Build") + .IsDependentOn("__Clean") + .IsDependentOn("__RestoreNugetPackages") + .IsDependentOn("__UpdateAssemblyVersionInformation") + .IsDependentOn("__UpdateDotNetStandardAssemblyVersionNumber") + .IsDependentOn("__UpdateAppVeyorBuildNumber") + .IsDependentOn("__BuildSolutions") + .IsDependentOn("__RunTests") + .IsDependentOn("__CopyNonSignedOutputToNugetFolder") + .IsDependentOn("__CreateNonSignedNugetPackage") + .IsDependentOn("__StronglySignAssemblies") + .IsDependentOn("__CopySignedOutputToNugetFolder") + .IsDependentOn("__CreateSignedNugetPackage"); + +/////////////////////////////////////////////////////////////////////////////// +// PRIMARY TARGETS +/////////////////////////////////////////////////////////////////////////////// + +Task("Default") + .IsDependentOn("Build"); + +/////////////////////////////////////////////////////////////////////////////// +// EXECUTION +/////////////////////////////////////////////////////////////////////////////// + +RunTarget(target); + +////////////////////////////////////////////////////////////////////// +// HELPER FUNCTIONS +////////////////////////////////////////////////////////////////////// + +string ToolsExePath(string exeFileName) { + var exePath = System.IO.Directory.GetFiles(@".\Tools", exeFileName, SearchOption.AllDirectories).FirstOrDefault(); + return exePath; +} diff --git a/build.ps1 b/build.ps1 new file mode 100644 index 0000000..8aa2a8d --- /dev/null +++ b/build.ps1 @@ -0,0 +1,132 @@ +<# + +.SYNOPSIS +This is a Powershell script to bootstrap a Cake build. + +.DESCRIPTION +This Powershell script will download NuGet if missing, restore NuGet tools (including Cake) +and execute your Cake build script with the parameters you provide. + +.PARAMETER Script +The build script to execute. +.PARAMETER Target +The build script target to run. +.PARAMETER Configuration +The build configuration to use. +.PARAMETER Verbosity +Specifies the amount of information to be displayed. +.PARAMETER Experimental +Tells Cake to use the latest Roslyn release. +.PARAMETER WhatIf +Performs a dry run of the build script. +No tasks will be executed. +.PARAMETER Mono +Tells Cake to use the Mono scripting engine. + +.LINK +http://cakebuild.net +#> + +Param( + [string]$Script = "build.cake", + [string]$Target = "Default", + [string]$Configuration = "Release", + [ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")] + [string]$Verbosity = "Verbose", + [switch]$Experimental, + [Alias("DryRun","Noop")] + [switch]$WhatIf, + [switch]$Mono, + [switch]$SkipToolPackageRestore, + [switch]$Verbose +) + +Write-Host "Preparing to run build script..." + +# Should we show verbose messages? +if($Verbose.IsPresent) +{ + $VerbosePreference = "continue" +} + +$TOOLS_DIR = Join-Path $PSScriptRoot "tools" +$NUGET_EXE = Join-Path $TOOLS_DIR "nuget.exe" +$CAKE_EXE = Join-Path $TOOLS_DIR "Cake/Cake.exe" +$PACKAGES_CONFIG = Join-Path $TOOLS_DIR "packages.config" + +# Should we use mono? +$UseMono = ""; +if($Mono.IsPresent) { + Write-Verbose -Message "Using the Mono based scripting engine." + $UseMono = "-mono" +} + +# Should we use the new Roslyn? +$UseExperimental = ""; +if($Experimental.IsPresent -and !($Mono.IsPresent)) { + Write-Verbose -Message "Using experimental version of Roslyn." + $UseExperimental = "-experimental" +} + +# Is this a dry run? +$UseDryRun = ""; +if($WhatIf.IsPresent) { + $UseDryRun = "-dryrun" +} + +# Make sure tools folder exists +if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) { + New-Item -Path $TOOLS_DIR -Type directory | out-null +} + +# Try download NuGet.exe if not exists +if (!(Test-Path $NUGET_EXE)) { + Write-Verbose -Message "Downloading NuGet.exe..." + Invoke-WebRequest -Uri https://dist.nuget.org/win-x86-commandline/latest/nuget.exe -OutFile $NUGET_EXE +} + +# Make sure NuGet exists where we expect it. +if (!(Test-Path $NUGET_EXE)) { + Throw "Could not find NuGet.exe" +} + +# Save nuget.exe path to environment to be available to child processed +$ENV:NUGET_EXE = $NUGET_EXE + +# Restore tools from NuGet? +if(-Not $SkipToolPackageRestore.IsPresent) +{ + # Restore tools from NuGet. + Push-Location + Set-Location $TOOLS_DIR + + Write-Verbose -Message "Restoring tools from NuGet..." + + # Restore packages + if (Test-Path $PACKAGES_CONFIG) + { + $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion" + Write-Verbose ($NuGetOutput | Out-String) + } + # Install just Cake if missing config + else + { + $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install Cake -Version 0.25.0 -ExcludeVersion" # Pin Cake version to 0.25.0; see https://github.com/App-vNext/Polly/issues/416 + Write-Verbose ($NuGetOutput | Out-String) + } + Pop-Location + if ($LASTEXITCODE -ne 0) + { + exit $LASTEXITCODE + } +} + +# Make sure that Cake has been installed. +if (!(Test-Path $CAKE_EXE)) { + Throw "Could not find Cake.exe" +} + +# Start Cake +Write-Host "Running build script..." +Invoke-Expression "$CAKE_EXE `"$Script`" -target=`"$Target`" -configuration=`"$Configuration`" -verbosity=`"$Verbosity`" $UseMono $UseDryRun $UseExperimental" +exit $LASTEXITCODE