From 5a7caa3323a1f8a3788dff0abee8f42cd32e68a4 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 18 Jun 2016 22:43:22 -0700 Subject: [PATCH 1/6] Make Get-Version.ps1 and GetBuildVersion task equivalent This was a significant refactoring to move all version computations and string assembling into a reusable class that both MSBuild and Powershell can call into. Fixes #70 --- .../BuildIntegrationTests.cs | 145 +++++---- .../RepoTestBase.cs | 3 +- src/NerdBank.GitVersioning/CloudBuild.cs | 25 ++ .../CloudBuildServices/AppVeyor.cs | 64 ++++ .../CloudBuildServices/TeamCity.cs | 26 ++ .../VisualStudioTeamServices.cs | 37 +++ src/NerdBank.GitVersioning/ICloudBuild.cs | 52 ++++ .../NerdBank.GitVersioning.csproj | 6 + src/NerdBank.GitVersioning/VersionOracle.cs | 277 ++++++++++++++++++ .../Nerdbank.GitVersioning.NuGet.nuproj | 2 - .../build/AppVeyor.targets | 34 --- .../build/NerdBank.GitVersioning.targets | 60 ++-- .../build/VisualStudioTeamServices.targets | 40 --- .../tools/Get-Version.ps1 | 56 +--- .../GetBuildVersion.cs | 150 ++++------ .../Nerdbank.GitVersioning.Tasks.csproj | 1 + .../SetCloudBuildVariables.cs | 80 +++++ 17 files changed, 750 insertions(+), 308 deletions(-) create mode 100644 src/NerdBank.GitVersioning/CloudBuild.cs create mode 100644 src/NerdBank.GitVersioning/CloudBuildServices/AppVeyor.cs create mode 100644 src/NerdBank.GitVersioning/CloudBuildServices/TeamCity.cs create mode 100644 src/NerdBank.GitVersioning/CloudBuildServices/VisualStudioTeamServices.cs create mode 100644 src/NerdBank.GitVersioning/ICloudBuild.cs create mode 100644 src/NerdBank.GitVersioning/VersionOracle.cs delete mode 100644 src/Nerdbank.GitVersioning.NuGet/build/AppVeyor.targets delete mode 100644 src/Nerdbank.GitVersioning.NuGet/build/VisualStudioTeamServices.targets create mode 100644 src/Nerdbank.GitVersioning.Tasks/SetCloudBuildVariables.cs diff --git a/src/NerdBank.GitVersioning.Tests/BuildIntegrationTests.cs b/src/NerdBank.GitVersioning.Tests/BuildIntegrationTests.cs index f3bbd676b..87ea57f7e 100644 --- a/src/NerdBank.GitVersioning.Tests/BuildIntegrationTests.cs +++ b/src/NerdBank.GitVersioning.Tests/BuildIntegrationTests.cs @@ -27,6 +27,7 @@ public class BuildIntegrationTests : RepoTestBase { private const string GitVersioningTargetsFileName = "NerdBank.GitVersioning.targets"; + private const string UnitTestCloudBuildPrefix = "UnitTest: "; private static readonly string[] ToxicEnvironmentVariablePrefixes = new string[] { "APPVEYOR", @@ -42,7 +43,6 @@ public class BuildIntegrationTests : RepoTestBase // Set global properties to neutralize environment variables // that might actually be defined by a CI that is building and running these tests. { "PublicRelease", string.Empty }, - { "_NBGV_UnitTest", "true" } }; private Random random; @@ -59,6 +59,7 @@ public BuildIntegrationTests(ITestOutputHelper logger) this.LoadTargetsIntoProjectCollection(); this.testProject = this.CreateProjectRootElement(this.projectDirectory, "test.proj"); this.globalProperties.Add("NerdbankGitVersioningTasksPath", Environment.CurrentDirectory + "\\"); + Environment.SetEnvironmentVariable("_NBGV_UnitTest", "true"); // Sterilize the test of any environment variables. foreach (System.Collections.DictionaryEntry variable in Environment.GetEnvironmentVariables()) @@ -71,6 +72,12 @@ public BuildIntegrationTests(ITestOutputHelper logger) } } + protected override void Dispose(bool disposing) + { + Environment.SetEnvironmentVariable("_NBGV_UnitTest", string.Empty); + base.Dispose(disposing); + } + [Fact] public async Task GetBuildVersion_Returns_BuildVersion_Property() { @@ -104,7 +111,7 @@ public async Task GetBuildVersion_OutsideGit_PointingToGit() // Write the same version file to the 'real' repo this.WriteVersionFile(version); - + // Point the project to the 'real' repo this.testProject.AddProperty("GitRepoRoot", this.RepoPath); @@ -382,14 +389,12 @@ public async Task PublicRelease_RegEx_SatisfiedByCI(IReadOnlyDictionary properties, string expectedMessage) { - foreach (var property in properties) - { - this.globalProperties[property.Key] = property.Value; - } - - string keyName = "n1"; - string value = "v1"; - this.testProject.AddItem("CloudBuildVersionVars", keyName, new Dictionary { { "Value", value } }); - - string alwaysExpectedMessage = expectedMessage - .Replace("{NAME}", keyName) - .Replace("{VALUE}", value); - - var versionOptions = new VersionOptions + using (ApplyEnvironmentVariables(properties)) { - Version = SemanticVersion.Parse("1.0"), - CloudBuild = new VersionOptions.CloudBuildOptions { SetVersionVariables = true }, - }; - this.WriteVersionFile(versionOptions); - this.InitializeSourceControl(); + string keyName = "n1"; + string value = "v1"; + this.testProject.AddItem("CloudBuildVersionVars", keyName, new Dictionary { { "Value", value } }); - var buildResult = await this.BuildAsync(); - AssertStandardProperties(versionOptions, buildResult); - string conditionallyExpectedMessage = expectedMessage - .Replace("{NAME}", "GitBuildVersion") - .Replace("{VALUE}", buildResult.BuildVersion); - Assert.Contains(alwaysExpectedMessage, buildResult.LoggedEvents.Select(e => e.Message.TrimEnd())); - Assert.Contains(conditionallyExpectedMessage, buildResult.LoggedEvents.Select(e => e.Message.TrimEnd())); + string alwaysExpectedMessage = UnitTestCloudBuildPrefix + expectedMessage + .Replace("{NAME}", keyName) + .Replace("{VALUE}", value); - versionOptions.CloudBuild.SetVersionVariables = false; - this.WriteVersionFile(versionOptions); - buildResult = await this.BuildAsync(); - AssertStandardProperties(versionOptions, buildResult); - conditionallyExpectedMessage = expectedMessage - .Replace("{NAME}", "GitBuildVersion") - .Replace("{VALUE}", buildResult.BuildVersion); - Assert.Contains(alwaysExpectedMessage, buildResult.LoggedEvents.Select(e => e.Message.TrimEnd())); - Assert.DoesNotContain(conditionallyExpectedMessage, buildResult.LoggedEvents.Select(e => e.Message.TrimEnd())); + var versionOptions = new VersionOptions + { + Version = SemanticVersion.Parse("1.0"), + CloudBuild = new VersionOptions.CloudBuildOptions { SetVersionVariables = true }, + }; + this.WriteVersionFile(versionOptions); + this.InitializeSourceControl(); + + var buildResult = await this.BuildAsync(); + AssertStandardProperties(versionOptions, buildResult); + string conditionallyExpectedMessage = UnitTestCloudBuildPrefix + expectedMessage + .Replace("{NAME}", "GitBuildVersion") + .Replace("{VALUE}", buildResult.BuildVersion); + Assert.Contains(alwaysExpectedMessage, buildResult.LoggedEvents.Select(e => e.Message.TrimEnd())); + Assert.Contains(conditionallyExpectedMessage, buildResult.LoggedEvents.Select(e => e.Message.TrimEnd())); + + versionOptions.CloudBuild.SetVersionVariables = false; + this.WriteVersionFile(versionOptions); + buildResult = await this.BuildAsync(); + AssertStandardProperties(versionOptions, buildResult); + conditionallyExpectedMessage = UnitTestCloudBuildPrefix + expectedMessage + .Replace("{NAME}", "GitBuildVersion") + .Replace("{VALUE}", buildResult.BuildVersion); + Assert.Contains(alwaysExpectedMessage, buildResult.LoggedEvents.Select(e => e.Message.TrimEnd())); + Assert.DoesNotContain(conditionallyExpectedMessage, buildResult.LoggedEvents.Select(e => e.Message.TrimEnd())); + } } private static VersionOptions BuildNumberVersionOptionsBasis @@ -483,16 +486,23 @@ public async Task BuildNumber_SetInCI(VersionOptions versionOptions, IReadOnlyDi { this.WriteVersionFile(versionOptions); this.InitializeSourceControl(); - - foreach (var property in properties) + using (ApplyEnvironmentVariables(properties)) { - this.globalProperties[property.Key] = property.Value; + var buildResult = await this.BuildAsync(); + AssertStandardProperties(versionOptions, buildResult); + expectedBuildNumberMessage = expectedBuildNumberMessage.Replace("{CLOUDBUILDNUMBER}", buildResult.CloudBuildNumber); + Assert.Contains(UnitTestCloudBuildPrefix + expectedBuildNumberMessage, buildResult.LoggedEvents.Select(e => e.Message.TrimEnd())); } - var buildResult = await this.BuildAsync(); - AssertStandardProperties(versionOptions, buildResult); - expectedBuildNumberMessage = expectedBuildNumberMessage.Replace("{CLOUDBUILDNUMBER}", buildResult.CloudBuildNumber); - Assert.Contains(expectedBuildNumberMessage, buildResult.LoggedEvents.Select(e => e.Message.TrimEnd())); + versionOptions.CloudBuild.BuildNumber.Enabled = false; + this.WriteVersionFile(versionOptions); + using (ApplyEnvironmentVariables(properties)) + { + var buildResult = await this.BuildAsync(); + AssertStandardProperties(versionOptions, buildResult); + expectedBuildNumberMessage = expectedBuildNumberMessage.Replace("{CLOUDBUILDNUMBER}", buildResult.CloudBuildNumber); + Assert.DoesNotContain(UnitTestCloudBuildPrefix + expectedBuildNumberMessage, buildResult.LoggedEvents.Select(e => e.Message.TrimEnd())); + } } [Theory] @@ -705,6 +715,20 @@ private static Version GetExpectedAssemblyVersion(VersionOptions versionOptions, return assemblyVersion; } + private static RestoreEnvironmentVariables ApplyEnvironmentVariables(IReadOnlyDictionary variables) + { + Requires.NotNull(variables, nameof(variables)); + + var oldValues = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (var variable in variables) + { + oldValues[variable.Key] = Environment.GetEnvironmentVariable(variable.Key); + Environment.SetEnvironmentVariable(variable.Key, variable.Value); + } + + return new RestoreEnvironmentVariables(oldValues); + } + private void AssertStandardProperties(VersionOptions versionOptions, BuildResults buildResult, string relativeProjectDirectory = null) { int versionHeight = this.Repo.GetVersionHeight(relativeProjectDirectory); @@ -728,9 +752,6 @@ private void AssertStandardProperties(VersionOptions versionOptions, BuildResult Assert.Equal($"{assemblyVersion.Major}.{assemblyVersion.Minor}.{assemblyVersion.Build}.{assemblyVersion.Revision}", buildResult.AssemblyVersion); Assert.Equal(idAsVersion.Build.ToString(), buildResult.BuildNumber); - Assert.Equal(idAsVersion.Build.ToString(), buildResult.BuildNumberFirstAndSecondComponentsIfApplicable); - Assert.Equal(idAsVersion.Build.ToString(), buildResult.BuildNumberFirstComponent); - Assert.Equal(string.Empty, buildResult.BuildNumberSecondComponent); Assert.Equal($"{version}", buildResult.BuildVersion); Assert.Equal($"{idAsVersion.Major}.{idAsVersion.Minor}.{idAsVersion.Build}", buildResult.BuildVersion3Components); Assert.Equal(idAsVersion.Build.ToString(), buildResult.BuildVersionNumberComponent); @@ -846,6 +867,21 @@ private void MakeItAVBProject() csharpImport.Project = @"$(MSBuildToolsPath)\Microsoft.VisualBasic.targets"; } + private struct RestoreEnvironmentVariables : IDisposable + { + private readonly IReadOnlyDictionary applyVariables; + + internal RestoreEnvironmentVariables(IReadOnlyDictionary applyVariables) + { + this.applyVariables = applyVariables; + } + + public void Dispose() + { + ApplyEnvironmentVariables(this.applyVariables); + } + } + private static class CloudBuild { public static readonly ImmutableDictionary VSTS = ImmutableDictionary.Empty @@ -882,9 +918,6 @@ internal BuildResults(BuildResult buildResult, IReadOnlyList log public string PrereleaseVersion => this.BuildResult.ProjectStateAfterBuild.GetPropertyValue("PrereleaseVersion"); public string MajorMinorVersion => this.BuildResult.ProjectStateAfterBuild.GetPropertyValue("MajorMinorVersion"); public string BuildVersionNumberComponent => this.BuildResult.ProjectStateAfterBuild.GetPropertyValue("BuildVersionNumberComponent"); - public string BuildNumberFirstComponent => this.BuildResult.ProjectStateAfterBuild.GetPropertyValue("BuildNumberFirstComponent"); - public string BuildNumberSecondComponent => this.BuildResult.ProjectStateAfterBuild.GetPropertyValue("BuildNumberSecondComponent"); - public string BuildNumberFirstAndSecondComponentsIfApplicable => this.BuildResult.ProjectStateAfterBuild.GetPropertyValue("BuildNumberFirstAndSecondComponentsIfApplicable"); public string GitCommitIdShort => this.BuildResult.ProjectStateAfterBuild.GetPropertyValue("GitCommitIdShort"); public string GitVersionHeight => this.BuildResult.ProjectStateAfterBuild.GetPropertyValue("GitVersionHeight"); public string SemVerBuildSuffix => this.BuildResult.ProjectStateAfterBuild.GetPropertyValue("SemVerBuildSuffix"); diff --git a/src/NerdBank.GitVersioning.Tests/RepoTestBase.cs b/src/NerdBank.GitVersioning.Tests/RepoTestBase.cs index ce5f780d3..936a370f3 100644 --- a/src/NerdBank.GitVersioning.Tests/RepoTestBase.cs +++ b/src/NerdBank.GitVersioning.Tests/RepoTestBase.cs @@ -2,6 +2,7 @@ { using System; using System.Collections.Generic; + using System.Diagnostics; using System.IO; using System.Linq; using System.Text; @@ -9,7 +10,7 @@ using LibGit2Sharp; using Validation; using Xunit.Abstractions; - using System.Diagnostics; + public abstract class RepoTestBase : IDisposable { public RepoTestBase(ITestOutputHelper logger) diff --git a/src/NerdBank.GitVersioning/CloudBuild.cs b/src/NerdBank.GitVersioning/CloudBuild.cs new file mode 100644 index 000000000..7fc51d0b9 --- /dev/null +++ b/src/NerdBank.GitVersioning/CloudBuild.cs @@ -0,0 +1,25 @@ +namespace Nerdbank.GitVersioning +{ + using System.Linq; + using CloudBuildServices; + + /// + /// Provides access to cloud build providers. + /// + public static class CloudBuild + { + /// + /// An array of cloud build systems we support. + /// + private static readonly ICloudBuild[] SupportedCloudBuilds = new ICloudBuild[] { + new AppVeyor(), + new VisualStudioTeamServices(), + new TeamCity(), + }; + + /// + /// Gets the cloud build provider that applies to this build, if any. + /// + public static ICloudBuild Active => SupportedCloudBuilds.FirstOrDefault(cb => cb.IsApplicable); + } +} diff --git a/src/NerdBank.GitVersioning/CloudBuildServices/AppVeyor.cs b/src/NerdBank.GitVersioning/CloudBuildServices/AppVeyor.cs new file mode 100644 index 000000000..febc2c036 --- /dev/null +++ b/src/NerdBank.GitVersioning/CloudBuildServices/AppVeyor.cs @@ -0,0 +1,64 @@ +namespace Nerdbank.GitVersioning.CloudBuildServices +{ + using System; + using System.ComponentModel; + using System.Diagnostics; + using System.IO; + + /// + /// + /// + /// + /// The AppVeyor-specific properties referenced here are documented here: + /// http://www.appveyor.com/docs/environment-variables + /// + internal class AppVeyor : ICloudBuild + { + /// + /// + /// + /// + /// AppVeyor's branch variable is the target branch of a PR, which is *NOT* to be misinterpreted + /// as building the target branch itself. So only set the branch built property if it's not a PR. + /// + public string BuildingBranch => !this.IsPullRequest && !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("APPVEYOR_REPO_BRANCH")) + ? $"refs/heads/{Environment.GetEnvironmentVariable("APPVEYOR_REPO_BRANCH")}" + : null; + + public string BuildingRef => null; + + public string BuildingTag => string.Equals("true", Environment.GetEnvironmentVariable("APPVEYOR_REPO_TAG"), StringComparison.OrdinalIgnoreCase) + ? $"refs/tags/{Environment.GetEnvironmentVariable("APPVEYOR_REPO_TAG_NAME")}" + : null; + + public string GitCommitId => null; + + public bool IsApplicable => !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("APPVEYOR")); + + public bool IsPullRequest => !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("APPVEYOR_PULL_REQUEST_NUMBER")); + + public void SetCloudBuildNumber(string buildNumber, TextWriter stdout, TextWriter stderr) + { + // We ignore exit code so as to not fail the build when the cloud build number is not unique. + RunAppveyor($"UpdateBuild -Version \"{buildNumber}\"", stdout, stderr); + } + + public void SetCloudBuildVariable(string name, string value, TextWriter stdout, TextWriter stderr) + { + RunAppveyor($"SetVariable -Name {name} -Value \"{value}\"", stdout, stderr); + } + + private static void RunAppveyor(string args, TextWriter stdout, TextWriter stderr) + { + try + { + Process.Start("appveyor", args) + .WaitForExit(); + } + catch (Win32Exception ex) when ((uint)ex.HResult == 0x80004005) + { + (stderr ?? Console.Error).WriteLine("Could not find appveyor tool to set cloud build variable."); + } + } + } +} diff --git a/src/NerdBank.GitVersioning/CloudBuildServices/TeamCity.cs b/src/NerdBank.GitVersioning/CloudBuildServices/TeamCity.cs new file mode 100644 index 000000000..e0fe63cae --- /dev/null +++ b/src/NerdBank.GitVersioning/CloudBuildServices/TeamCity.cs @@ -0,0 +1,26 @@ +namespace Nerdbank.GitVersioning.CloudBuildServices +{ + using System; + using System.IO; + + internal class TeamCity : ICloudBuild + { + public string BuildingBranch => null; + + public string BuildingTag => null; + + public string GitCommitId => Environment.GetEnvironmentVariable("BUILD_VCS_NUMBER"); + + public bool IsApplicable => this.GitCommitId != null; + + public bool IsPullRequest => false; + + public void SetCloudBuildNumber(string buildNumber, TextWriter stdout, TextWriter stderr) + { + } + + public void SetCloudBuildVariable(string name, string value, TextWriter stdout, TextWriter stderr) + { + } + } +} diff --git a/src/NerdBank.GitVersioning/CloudBuildServices/VisualStudioTeamServices.cs b/src/NerdBank.GitVersioning/CloudBuildServices/VisualStudioTeamServices.cs new file mode 100644 index 000000000..0df9332da --- /dev/null +++ b/src/NerdBank.GitVersioning/CloudBuildServices/VisualStudioTeamServices.cs @@ -0,0 +1,37 @@ +namespace Nerdbank.GitVersioning.CloudBuildServices +{ + using System; + using System.IO; + + /// + /// + /// + /// + /// The VSTS-specific properties referenced here are documented here: + /// https://msdn.microsoft.com/en-us/Library/vs/alm/Build/scripts/variables + /// + internal class VisualStudioTeamServices : ICloudBuild + { + public bool IsPullRequest => false; // VSTS doesn't define this. + + public string BuildingTag => null; // VSTS doesn't define this. + + public string BuildingBranch => Environment.GetEnvironmentVariable("BUILD_SOURCEBRANCH"); + + public string BuildingRef => this.BuildingBranch; + + public string GitCommitId => null; + + public bool IsApplicable => !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("SYSTEM_TEAMPROJECTID")); + + public void SetCloudBuildNumber(string buildNumber, TextWriter stdout, TextWriter stderr) + { + (stdout ?? Console.Out).WriteLine($"##vso[build.updatebuildnumber]{buildNumber}"); + } + + public void SetCloudBuildVariable(string name, string value, TextWriter stdout, TextWriter stderr) + { + (stdout ?? Console.Out).WriteLine($"##vso[task.setvariable variable={name};]{value}"); + } + } +} diff --git a/src/NerdBank.GitVersioning/ICloudBuild.cs b/src/NerdBank.GitVersioning/ICloudBuild.cs new file mode 100644 index 000000000..53df9fffc --- /dev/null +++ b/src/NerdBank.GitVersioning/ICloudBuild.cs @@ -0,0 +1,52 @@ +namespace Nerdbank.GitVersioning +{ + using System.IO; + + /// + /// Defines cloud build provider functionality. + /// + public interface ICloudBuild + { + /// + /// Gets a value indicating whether the active cloud build matches what this instance supports. + /// + bool IsApplicable { get; } + + /// + /// Gets a value indicating whether a cloud build is validating a pull request. + /// + bool IsPullRequest { get; } + + /// + /// Gets the branch being built by a cloud build, if applicable. + /// + string BuildingBranch { get; } + + /// + /// Gets the tag being built by a cloud build, if applicable. + /// + string BuildingTag { get; } + + /// + /// Gets the git commit ID being built by a cloud build, if applicable. + /// + string GitCommitId { get; } + + /// + /// Sets the build number for the cloud build, if supported. + /// + /// The build number to set. + /// An optional redirection for what should be written to the standard out stream. + /// An optional redirection for what should be written to the standard error stream. + void SetCloudBuildNumber(string buildNumber, TextWriter stdout, TextWriter stderr); + + /// + /// Sets a cloud build variable, if supported. + /// + /// The name of the variable. + /// The value for the variable. + /// An optional redirection for what should be written to the standard out stream. + /// An optional redirection for what should be written to the standard error stream. + void SetCloudBuildVariable(string name, string value, TextWriter stdout, TextWriter stderr); + } +} diff --git a/src/NerdBank.GitVersioning/NerdBank.GitVersioning.csproj b/src/NerdBank.GitVersioning/NerdBank.GitVersioning.csproj index aae10dd7e..35ca5ab47 100644 --- a/src/NerdBank.GitVersioning/NerdBank.GitVersioning.csproj +++ b/src/NerdBank.GitVersioning/NerdBank.GitVersioning.csproj @@ -59,6 +59,11 @@ + + + + + @@ -67,6 +72,7 @@ + diff --git a/src/NerdBank.GitVersioning/VersionOracle.cs b/src/NerdBank.GitVersioning/VersionOracle.cs new file mode 100644 index 000000000..f456f8b3f --- /dev/null +++ b/src/NerdBank.GitVersioning/VersionOracle.cs @@ -0,0 +1,277 @@ +namespace Nerdbank.GitVersioning +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Text; + using System.Text.RegularExpressions; + using System.Threading.Tasks; + using Validation; + + /// + /// Assembles version information in a variety of formats. + /// + public class VersionOracle + { + /// + /// The cloud build suppport, if any. + /// + private readonly ICloudBuild cloudBuild; + + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// + /// + public static VersionOracle Create(string projectDirectory, string gitRepoDirectory = null, ICloudBuild cloudBuild = null) + { + Requires.NotNull(projectDirectory, nameof(projectDirectory)); + if (string.IsNullOrEmpty(gitRepoDirectory)) + { + gitRepoDirectory = projectDirectory; + } + + using (var git = OpenGitRepo(gitRepoDirectory)) + { + return new VersionOracle(projectDirectory, git, cloudBuild); + } + } + + /// + /// Initializes a new instance of the class. + /// + public VersionOracle(string projectDirectory, LibGit2Sharp.Repository repo, ICloudBuild cloudBuild) + { + this.cloudBuild = cloudBuild; + this.VersionOptions = + VersionFile.GetVersion(repo, projectDirectory) ?? + VersionFile.GetVersion(projectDirectory); + + var repoRoot = repo?.Info?.WorkingDirectory?.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + var relativeRepoProjectDirectory = !string.IsNullOrWhiteSpace(repoRoot) + ? projectDirectory.Substring(repoRoot.Length).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) + : null; + + var commit = repo?.Head.Commits.FirstOrDefault(); + this.GitCommitId = commit?.Id.Sha ?? cloudBuild?.GitCommitId ?? null; + this.GitVersionHeight = repo?.GetVersionHeight(relativeRepoProjectDirectory) ?? 0; + this.BuildingRef = cloudBuild?.BuildingTag ?? cloudBuild?.BuildingBranch ?? repo?.Head.CanonicalName; + + // Override the typedVersion with the special build number and revision components, when available. + this.TypedVersion = repo?.GetIdAsVersion(relativeRepoProjectDirectory, this.GitVersionHeight) ?? this.VersionOptions?.Version.Version; + this.TypedVersion = this.TypedVersion ?? new Version(); + + this.CloudBuildNumberOptions = this.VersionOptions?.CloudBuild?.BuildNumber ?? new VersionOptions.CloudBuildNumberOptions(); + + if (!string.IsNullOrEmpty(this.BuildingRef) && this.VersionOptions?.PublicReleaseRefSpec?.Length > 0) + { + this.PublicRelease = this.VersionOptions.PublicReleaseRefSpec.Any( + expr => Regex.IsMatch(this.BuildingRef, expr)); + } + } + + /// + /// Gets the BuildNumber to set the cloud build to (if applicable). + /// + public string CloudBuildNumber + { + get + { + var commitIdOptions = this.CloudBuildNumberOptions.IncludeCommitId ?? new VersionOptions.CloudBuildNumberCommitIdOptions(); + bool includeCommitInfo = commitIdOptions.When == VersionOptions.CloudBuildNumberCommitWhen.Always || + (commitIdOptions.When == VersionOptions.CloudBuildNumberCommitWhen.NonPublicReleaseOnly && !this.PublicRelease); + bool commitIdInRevision = includeCommitInfo && commitIdOptions.Where == VersionOptions.CloudBuildNumberCommitWhere.FourthVersionComponent; + bool commitIdInBuildMetadata = includeCommitInfo && commitIdOptions.Where == VersionOptions.CloudBuildNumberCommitWhere.BuildMetadata; + Version buildNumberVersion = commitIdInRevision ? this.TypedVersion : this.TypedVersionWithoutRevision; + string buildNumberMetadata = FormatBuildMetadata(commitIdInBuildMetadata ? this.BuildMetadataWithCommitId : this.BuildMetadata); + return buildNumberVersion + this.PrereleaseVersion + buildNumberMetadata; + } + } + + /// + /// Gets a value indicating whether the cloud build number should be set. + /// + public bool SetCloudBuildNumber => this.CloudBuildNumberOptions.Enabled; + + /// + /// Gets the build metadata identifiers, including the git commit ID as the first identifier if appropriate. + /// + public IEnumerable BuildMetadataWithCommitId + { + get + { + if (!string.IsNullOrEmpty(this.GitCommitId)) + { + yield return $"g{this.GitCommitId.Substring(0, 10)}"; + } + + foreach (string identifier in this.BuildMetadata) + { + yield return identifier; + } + } + } + + /// + /// Gets the version options used to initialize this instance. + /// + private VersionOptions VersionOptions { get; } + + /// + /// Gets the version string to use for the . + /// + public Version AssemblyVersion => GetAssemblyVersion(this.TypedVersion, this.VersionOptions).EnsureNonNegativeComponents(); + + /// + /// Gets the version string to use for the . + /// + public Version AssemblyFileVersion => this.TypedVersion; + + /// + /// Gets the version string to use for the . + /// + public string AssemblyInformationalVersion => + $"{this.TypedVersion.ToStringSafe(3)}{this.PrereleaseVersion}{FormatBuildMetadata(this.BuildMetadataWithCommitId)}"; + + /// + /// Gets or sets a value indicating whether the project is building + /// in PublicRelease mode. + /// + public bool PublicRelease { get; set; } + + /// + /// Gets the prerelease version information. + /// + public string PrereleaseVersion => this.VersionOptions?.Version.Prerelease ?? string.Empty; + + /// + /// Gets the version string to use in the official release name (lacks revision number). + /// + [Obsolete("Use " + nameof(TypedVersionWithoutRevision) + " instead")] + public string SimpleVersion => this.TypedVersionWithoutRevision.ToString(); + + /// + /// Gets the version information without a Revision component. + /// + public Version TypedVersionWithoutRevision => this.TypedVersion.Build >= 0 + ? new Version(this.TypedVersion.Major, this.TypedVersion.Minor, this.TypedVersion.Build) + : new Version(this.TypedVersion.Major, this.TypedVersion.Minor); + + /// + /// Gets the build number (git height) for this version. + /// + public int BuildNumber => Math.Max(0, this.TypedVersion.Build); + + /// + /// Gets or sets the major.minor version string. + /// + /// + /// The x.y string (no build number or revision number). + /// + public Version MajorMinorVersion => new Version(this.TypedVersion.Major, this.TypedVersion.Minor); + + /// + /// Gets the Git revision control commit id for HEAD (the current source code version). + /// + public string GitCommitId { get; } + + /// + /// Gets the first several characters of the Git revision control commit id for HEAD (the current source code version). + /// + public string GitCommitIdShort => string.IsNullOrEmpty(this.GitCommitId) ? null : this.GitCommitId.Substring(0, 10); + + /// + /// Gets the number of commits in the longest single path between + /// the specified commit and the most distant ancestor (inclusive) + /// that set the version to the value at HEAD. + /// + public int GitVersionHeight { get; } + + private string BuildingRef { get; } + + /// + /// Gets the version for this project, with up to 4 components. + /// + public Version TypedVersion { get; } + + /// + /// Gets a value indicating whether to set cloud build version variables. + /// + public bool SetCloudBuildVersionVars => this.VersionOptions?.CloudBuild?.SetVersionVariables + ?? (new VersionOptions.CloudBuildOptions()).SetVersionVariables; + + /// + /// Gets a dictionary of cloud build variables that applies to this project, + /// regardless of the current setting of . + /// + public IDictionary CloudBuildVersionVars + { + get + { + return new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "GitAssemblyInformationalVersion", this.AssemblyInformationalVersion }, + { "GitBuildVersion", this.TypedVersion.ToString() }, + }; + } + } + + /// + /// Gets the list of build metadata identifiers to include in semver version strings. + /// + public List BuildMetadata { get; } = new List(); + + /// + /// Gets the +buildMetadata fragment for the semantic version. + /// + public string BuildMetadataFragment => FormatBuildMetadata(this.BuildMetadataWithCommitId); + + /// + /// Gets the version to use for NuGet packages. + /// + public string NuGetPackageVersion => + $"{this.TypedVersion.ToStringSafe(3)}{this.PrereleaseVersion}{this.SemVer1GitCommitIdShortPackageSuffix}"; + + /// + /// Gets the version to use for NPM packages. + /// + public string NpmPackageVersion => this.NuGetPackageVersion; + + private string SemVer1GitCommitIdShortPackageSuffix => + this.PublicRelease ? string.Empty : $"-g{this.GitCommitIdShort}"; + + private VersionOptions.CloudBuildNumberOptions CloudBuildNumberOptions { get; } + + private static string FormatBuildMetadata(IEnumerable identifiers) => + (identifiers?.Any() ?? false) ? "+" + string.Join(".", identifiers) : string.Empty; + + private static LibGit2Sharp.Repository OpenGitRepo(string repoRoot) + { + Requires.NotNullOrEmpty(repoRoot, nameof(repoRoot)); + while (!Directory.Exists(Path.Combine(repoRoot, ".git"))) + { + repoRoot = Path.GetDirectoryName(repoRoot); + if (repoRoot == null) + { + return null; + } + } + + return new LibGit2Sharp.Repository(repoRoot); + } + + private static Version GetAssemblyVersion(Version version, VersionOptions versionOptions) + { + var assemblyVersion = versionOptions?.AssemblyVersion?.Version ?? new System.Version(version.Major, version.Minor); + assemblyVersion = new System.Version( + assemblyVersion.Major, + assemblyVersion.Minor, + versionOptions?.AssemblyVersion?.Precision >= VersionOptions.VersionPrecision.Build ? version.Build : 0, + versionOptions?.AssemblyVersion?.Precision >= VersionOptions.VersionPrecision.Revision ? version.Revision : 0); + return assemblyVersion.EnsureNonNegativeComponents(4); + } + } +} diff --git a/src/Nerdbank.GitVersioning.NuGet/Nerdbank.GitVersioning.NuGet.nuproj b/src/Nerdbank.GitVersioning.NuGet/Nerdbank.GitVersioning.NuGet.nuproj index 011fd1a63..a3b7cd172 100644 --- a/src/Nerdbank.GitVersioning.NuGet/Nerdbank.GitVersioning.NuGet.nuproj +++ b/src/Nerdbank.GitVersioning.NuGet/Nerdbank.GitVersioning.NuGet.nuproj @@ -47,9 +47,7 @@ tools\version.schema.json - - diff --git a/src/Nerdbank.GitVersioning.NuGet/build/AppVeyor.targets b/src/Nerdbank.GitVersioning.NuGet/build/AppVeyor.targets deleted file mode 100644 index 4d1ba180a..000000000 --- a/src/Nerdbank.GitVersioning.NuGet/build/AppVeyor.targets +++ /dev/null @@ -1,34 +0,0 @@ - - - - - <_NBGV_BuildServerSoftware>AppVeyor - <_NBGV_IsPullRequest Condition=" '$(APPVEYOR_PULL_REQUEST_NUMBER)' != '' ">true - <_NBGV_BuildingTag Condition=" '$(APPVEYOR_REPO_TAG)' == 'true' ">refs/tags/$(APPVEYOR_REPO_TAG_NAME) - - - <_NBGV_BuildingBranch Condition=" '$(_NBGV_IsPullRequest)' != 'true' and '$(APPVEYOR_REPO_BRANCH)' != '' ">refs/heads/$(APPVEYOR_REPO_BRANCH) - - - - - - - - - - - diff --git a/src/Nerdbank.GitVersioning.NuGet/build/NerdBank.GitVersioning.targets b/src/Nerdbank.GitVersioning.NuGet/build/NerdBank.GitVersioning.targets index 26a90c778..16354f751 100644 --- a/src/Nerdbank.GitVersioning.NuGet/build/NerdBank.GitVersioning.targets +++ b/src/Nerdbank.GitVersioning.NuGet/build/NerdBank.GitVersioning.targets @@ -23,11 +23,10 @@ + - - <_NBGV_BuildingRef>$(_NBGV_BuildingTag) @@ -48,47 +47,28 @@ GitRepoRoot="$(GitRepoRoot)"> + + - + + + - - + + + + - - $(BuildVersionNumberComponent) - $([System.Text.RegularExpressions.Regex]::Match($(BuildNumber), '(\d+)').Groups[1].Value) - $([System.Text.RegularExpressions.Regex]::Match($(BuildNumber), '(\d+)\.(\d+)').Groups[2].Value) - $(BuildNumberFirstComponent) - $(BuildNumberFirstComponent).$(BuildNumberSecondComponent) - - - $(BUILD_VCS_NUMBER) - - $(GitCommitId.Substring(0,10)) - $(MajorMinorVersion).$(BuildNumberFirstComponent) - $(MajorMinorVersion).$(BuildNumberFirstAndSecondComponentsIfApplicable)$(PrereleaseVersion)$(SemVerBuildSuffix) - $(BuildVersion) - $(BuildVersion3Components)$(PrereleaseVersion) - - - $(NuGetPackageVersion)-g$(GitCommitIdShort) + + $(MajorMinorVersion).$(BuildVersionNumberComponent) - - - $(AssemblyInformationalVersion) - - - $(BuildVersion) - - @@ -97,6 +77,22 @@ + + + + + + + + diff --git a/src/Nerdbank.GitVersioning.NuGet/build/VisualStudioTeamServices.targets b/src/Nerdbank.GitVersioning.NuGet/build/VisualStudioTeamServices.targets deleted file mode 100644 index 9c5d23e6c..000000000 --- a/src/Nerdbank.GitVersioning.NuGet/build/VisualStudioTeamServices.targets +++ /dev/null @@ -1,40 +0,0 @@ - - - - - <_NBGV_BuildServerSoftware>VisualStudioTeamServices - <_NBGV_BuildingBranch>$(BUILD_SOURCEBRANCH) - <_NBGV_BuildingRef>$(_NBGV_BuildingBranch) - - - - - - - - - - - - - - - - - diff --git a/src/Nerdbank.GitVersioning.NuGet/tools/Get-Version.ps1 b/src/Nerdbank.GitVersioning.NuGet/tools/Get-Version.ps1 index e8e3316d4..87e0bdcc9 100644 --- a/src/Nerdbank.GitVersioning.NuGet/tools/Get-Version.ps1 +++ b/src/Nerdbank.GitVersioning.NuGet/tools/Get-Version.ps1 @@ -1,21 +1,18 @@ <# .SYNOPSIS Finds the git commit ID that was built to produce some specific version of an assembly. -.DESCRIPTION -TODO -.PARAMETER GitPath -The path to the git repo root. If omitted, the current working directory is assumed. -.PARAMETER AssemblyPath -Path to the assembly to read the version from. -.PARAMETER Version -The major.minor.build.revision version string or semver-compliant version read from the assembly. .PARAMETER ProjectDirectory The directory of the project that built the assembly, within the git repo. +.PARAMETER PublicRelease +Forces version generation to consider this a public release. +Similar to the MSBuild /p:PublicRelease=true switch. #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter()] - [string]$ProjectDirectory="." + [string]$ProjectDirectory=".", + [Parameter()] + [switch]$PublicRelease ) if (!$DependencyBasePath) { $DependencyBasePath = "$PSScriptRoot\..\build" } @@ -25,39 +22,14 @@ $null = [Reflection.Assembly]::LoadFile((Resolve-Path "$DependencyBasePath\LibGi $null = [Reflection.Assembly]::LoadFile((Resolve-Path "$DependencyBasePath\Newtonsoft.Json.dll")) $ProjectDirectory = Resolve-Path $ProjectDirectory -$GitPath = $ProjectDirectory -while (!(Test-Path "$GitPath\.git") -and $GitPath.Length -gt 0) { - $GitPath = Split-Path $GitPath -} - -if ($GitPath -eq '') { - Write-Error "Unable to find git repo in $ProjectDirectory." - return 1 -} - -$RepoRelativeProjectDirectory = $ProjectDirectory.Substring($GitPath.Length).TrimStart([IO.Path]::DirectorySeparatorChar, [IO.Path]::AltDirectorySeparatorChar) -$repo = New-Object LibGit2Sharp.Repository($GitPath) try { - $Head = [System.Linq.Enumerable]::First($repo.Head.Commits) - $versionOptions = [NerdBank.GitVersioning.VersionFile]::GetVersion($repo, $RepoRelativeProjectDirectory) - $version = [NerdBank.GitVersioning.GitExtensions]::GetIdAsVersion($repo, $RepoRelativeProjectDirectory) - $SimpleVersion = New-Object Version ($version.Major, $version.Minor, $version.Build) - $MajorMinorVersion = New-Object Version ($version.Major, $version.Minor) - $PrereleaseVersion = $versionOptions.Version.Prerelease - $SemVer1 = "{0}{1}-g{2}" -f $SimpleVersion, $PrereleaseVersion, $Head.Id.Sha.Substring(0,10) - $SemVer2 = "{0}{1}+g{2}" -f $SimpleVersion, $PrereleaseVersion, $Head.Id.Sha.Substring(0,10) - $RichObject = New-Object PSObject - Add-Member -InputObject $RichObject -MemberType NoteProperty -Name Version -Value $version - Add-Member -InputObject $RichObject -MemberType NoteProperty -Name SimpleVersion -Value $SimpleVersion - Add-Member -InputObject $RichObject -MemberType NoteProperty -Name MajorMinorVersion -Value $MajorMinorVersion - Add-Member -InputObject $RichObject -MemberType NoteProperty -Name PrereleaseVersion -Value $PrereleaseVersion - Add-Member -InputObject $RichObject -MemberType NoteProperty -Name CommitId -Value $Head.Id.Sha - Add-Member -InputObject $RichObject -MemberType NoteProperty -Name CommitIdShort -Value $Head.Id.Sha.Substring(0, 10) - Add-Member -InputObject $RichObject -MemberType NoteProperty -Name VersionHeight -Value $version.Build - Add-Member -InputObject $RichObject -MemberType NoteProperty -Name SemVer1 -Value $SemVer1 - Add-Member -InputObject $RichObject -MemberType NoteProperty -Name SemVer2 -Value $SemVer2 - $RichObject -} finally { - $repo.Dispose() + $CloudBuild = [Nerdbank.GitVersioning.CloudBuild]::Active + $VersionOracle = [Nerdbank.GitVersioning.VersionOracle]::Create($ProjectDirectory, $null, $CloudBuild) + $VersionOracle.PublicRelease = $PublicRelease + + $VersionOracle } +finally { + # the try is here so Powershell aborts after first failed step. +} \ No newline at end of file diff --git a/src/Nerdbank.GitVersioning.Tasks/GetBuildVersion.cs b/src/Nerdbank.GitVersioning.Tasks/GetBuildVersion.cs index 4ae21382e..765dfad45 100644 --- a/src/Nerdbank.GitVersioning.Tasks/GetBuildVersion.cs +++ b/src/Nerdbank.GitVersioning.Tasks/GetBuildVersion.cs @@ -62,6 +62,18 @@ public GetBuildVersion() [Output] public string AssemblyVersion { get; private set; } + /// + /// Gets the version string to use for the . + /// + [Output] + public string AssemblyFileVersion { get; private set; } + + /// + /// Gets the version string to use for the . + /// + [Output] + public string AssemblyInformationalVersion { get; private set; } + /// /// Gets the version string to use in the official release name (lacks revision number). /// @@ -92,6 +104,9 @@ public GetBuildVersion() [Output] public string GitCommitId { get; private set; } + [Output] + public string GitCommitIdShort { get; private set; } + /// /// Gets the number of commits in the longest single path between /// the specified commit and the most distant ancestor (inclusive) @@ -106,6 +121,12 @@ public GetBuildVersion() [Output] public string BuildMetadataFragment { get; private set; } + [Output] + public string NuGetPackageVersion { get; private set; } + + [Output] + public string NpmPackageVersion { get; private set; } + /// /// Gets the build number (git height) for this version. /// @@ -118,87 +139,47 @@ public GetBuildVersion() [Output] public string CloudBuildNumber { get; private set; } - /// - /// Gets a value indicating whether to set cloud build version variables. - /// [Output] - public bool SetCloudBuildVersionVars { get; private set; } + public ITaskItem[] CloudBuildVersionVars { get; private set; } public override bool Execute() { try { - Version typedVersion; - VersionOptions versionOptions; - using (var git = this.OpenGitRepo()) + var cloudBuild = CloudBuild.Active; + var oracle = VersionOracle.Create(Environment.CurrentDirectory, this.GitRepoRoot, cloudBuild); + if (!string.IsNullOrEmpty(this.DefaultPublicRelease)) { - var repoRoot = git?.Info?.WorkingDirectory?.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); - var relativeRepoProjectDirectory = !string.IsNullOrWhiteSpace(repoRoot) - ? Environment.CurrentDirectory.Substring(repoRoot.Length).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) - : null; - - var commit = git?.Head.Commits.FirstOrDefault(); - this.GitCommitId = commit?.Id.Sha ?? string.Empty; - this.GitVersionHeight = git?.GetVersionHeight(relativeRepoProjectDirectory) ?? 0; - if (string.IsNullOrEmpty(this.BuildingRef)) - { - this.BuildingRef = git?.Head.CanonicalName; - } - - versionOptions = - VersionFile.GetVersion(git, Environment.CurrentDirectory) ?? - VersionFile.GetVersion(Environment.CurrentDirectory); - - this.PublicRelease = string.Equals(this.DefaultPublicRelease, "true", StringComparison.OrdinalIgnoreCase); - if (string.IsNullOrEmpty(this.DefaultPublicRelease)) - { - if (!string.IsNullOrEmpty(this.BuildingRef) && versionOptions?.PublicReleaseRefSpec?.Length > 0) - { - this.PublicRelease = versionOptions.PublicReleaseRefSpec.Any( - expr => Regex.IsMatch(this.BuildingRef, expr)); - } - } - - this.PrereleaseVersion = versionOptions?.Version.Prerelease ?? string.Empty; - - // Override the typedVersion with the special build number and revision components, when available. - typedVersion = git?.GetIdAsVersion(relativeRepoProjectDirectory, this.GitVersionHeight) ?? versionOptions?.Version.Version; + oracle.PublicRelease = string.Equals(this.DefaultPublicRelease, "true", StringComparison.OrdinalIgnoreCase); } - typedVersion = typedVersion ?? new Version(); - var typedVersionWithoutRevision = typedVersion.Build >= 0 - ? new Version(typedVersion.Major, typedVersion.Minor, typedVersion.Build) - : new Version(typedVersion.Major, typedVersion.Minor); - this.SimpleVersion = typedVersionWithoutRevision.ToString(); - var majorMinorVersion = new Version(typedVersion.Major, typedVersion.Minor); - this.MajorMinorVersion = majorMinorVersion.ToString(); - Version assemblyVersion = GetAssemblyVersion(typedVersion, versionOptions); - this.AssemblyVersion = assemblyVersion.ToStringSafe(4); - this.BuildNumber = Math.Max(0, typedVersion.Build); - this.Version = typedVersion.ToString(); - - this.SetCloudBuildVersionVars = versionOptions?.CloudBuild?.SetVersionVariables - ?? (new VersionOptions.CloudBuildOptions()).SetVersionVariables; - - var buildMetadata = this.BuildMetadata?.ToList() ?? new List(); - if (!string.IsNullOrEmpty(this.GitCommitId)) + if (this.BuildMetadata != null) { - buildMetadata.Insert(0, $"g{this.GitCommitId.Substring(0, 10)}"); + oracle.BuildMetadata.AddRange(this.BuildMetadata); } - this.BuildMetadataFragment = FormatBuildMetadata(buildMetadata); - - var buildNumber = versionOptions?.CloudBuild?.BuildNumber ?? new VersionOptions.CloudBuildNumberOptions(); - if (buildNumber.Enabled) + this.PublicRelease = oracle.PublicRelease; + this.Version = oracle.TypedVersion.ToString(); + this.AssemblyVersion = oracle.AssemblyVersion.ToString(); + this.AssemblyFileVersion = oracle.AssemblyFileVersion.ToString(); + this.AssemblyInformationalVersion = oracle.AssemblyInformationalVersion; + this.SimpleVersion = oracle.TypedVersionWithoutRevision.ToString(); + this.MajorMinorVersion = oracle.MajorMinorVersion.ToString(); + this.BuildNumber = oracle.BuildNumber; + this.PrereleaseVersion = oracle.PrereleaseVersion; + this.GitCommitId = oracle.GitCommitId; + this.GitCommitIdShort = oracle.GitCommitIdShort; + this.GitVersionHeight = oracle.GitVersionHeight; + this.BuildMetadataFragment = oracle.BuildMetadataFragment; + this.CloudBuildNumber = oracle.SetCloudBuildNumber ? oracle.CloudBuildNumber : null; + this.NuGetPackageVersion = oracle.NuGetPackageVersion; + this.NpmPackageVersion = oracle.NpmPackageVersion; + + if (oracle.SetCloudBuildVersionVars) { - var commitIdOptions = buildNumber.IncludeCommitId ?? new VersionOptions.CloudBuildNumberCommitIdOptions(); - bool includeCommitInfo = commitIdOptions.When == VersionOptions.CloudBuildNumberCommitWhen.Always || - (commitIdOptions.When == VersionOptions.CloudBuildNumberCommitWhen.NonPublicReleaseOnly && !this.PublicRelease); - bool commitIdInRevision = includeCommitInfo && commitIdOptions.Where == VersionOptions.CloudBuildNumberCommitWhere.FourthVersionComponent; - bool commitIdInBuildMetadata = includeCommitInfo && commitIdOptions.Where == VersionOptions.CloudBuildNumberCommitWhere.BuildMetadata; - Version buildNumberVersion = commitIdInRevision ? typedVersion : typedVersionWithoutRevision; - string buildNumberMetadata = FormatBuildMetadata(commitIdInBuildMetadata ? (IEnumerable)buildMetadata : this.BuildMetadata); - this.CloudBuildNumber = buildNumberVersion + this.PrereleaseVersion + buildNumberMetadata; + this.CloudBuildVersionVars = oracle.CloudBuildVersionVars + .Select(item => new TaskItem(item.Key, new Dictionary { { "Value", item.Value } })) + .ToArray(); } } catch (ArgumentOutOfRangeException ex) @@ -209,38 +190,5 @@ public override bool Execute() return true; } - - private static string FormatBuildMetadata(IEnumerable identifiers) - { - return identifiers?.Any() ?? false - ? "+" + string.Join(".", identifiers) - : string.Empty; - } - - private static Version GetAssemblyVersion(Version version, VersionOptions versionOptions) - { - var assemblyVersion = versionOptions?.AssemblyVersion?.Version ?? new System.Version(version.Major, version.Minor); - assemblyVersion = new System.Version( - assemblyVersion.Major, - assemblyVersion.Minor, - versionOptions?.AssemblyVersion?.Precision >= VersionOptions.VersionPrecision.Build ? version.Build : 0, - versionOptions?.AssemblyVersion?.Precision >= VersionOptions.VersionPrecision.Revision ? version.Revision : 0); - return assemblyVersion; - } - - private LibGit2Sharp.Repository OpenGitRepo() - { - string repoRoot = string.IsNullOrEmpty(this.GitRepoRoot) ? Environment.CurrentDirectory : this.GitRepoRoot; - while (!Directory.Exists(Path.Combine(repoRoot, ".git"))) - { - repoRoot = Path.GetDirectoryName(repoRoot); - if (repoRoot == null) - { - return null; - } - } - - return new LibGit2Sharp.Repository(repoRoot); - } } } diff --git a/src/Nerdbank.GitVersioning.Tasks/Nerdbank.GitVersioning.Tasks.csproj b/src/Nerdbank.GitVersioning.Tasks/Nerdbank.GitVersioning.Tasks.csproj index 14e6dc467..c4b9f385c 100644 --- a/src/Nerdbank.GitVersioning.Tasks/Nerdbank.GitVersioning.Tasks.csproj +++ b/src/Nerdbank.GitVersioning.Tasks/Nerdbank.GitVersioning.Tasks.csproj @@ -63,6 +63,7 @@ + diff --git a/src/Nerdbank.GitVersioning.Tasks/SetCloudBuildVariables.cs b/src/Nerdbank.GitVersioning.Tasks/SetCloudBuildVariables.cs new file mode 100644 index 000000000..e77f286d4 --- /dev/null +++ b/src/Nerdbank.GitVersioning.Tasks/SetCloudBuildVariables.cs @@ -0,0 +1,80 @@ +namespace Nerdbank.GitVersioning.Tasks +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Text; + using Microsoft.Build.Framework; + using Microsoft.Build.Utilities; + + public class SetCloudBuildVariables : Task + { + public ITaskItem[] CloudBuildVersionVars { get; set; } + + public string CloudBuildNumber { get; set; } + + public override bool Execute() + { + var cloudBuild = CloudBuild.Active; + if (cloudBuild != null) + { + // Take care in a unit test environment because it would actually + // adversely impact the build variables of the cloud build underway that + // is running the tests. + bool isUnitTest = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("_NBGV_UnitTest")); + var testStdOut = new StringBuilder(); + var testStdErr = new StringBuilder(); + TextWriter stdout = isUnitTest ? new StringWriter(testStdOut) : Console.Out; + TextWriter stderr = isUnitTest ? new StringWriter(testStdErr) : Console.Error; + + if (!string.IsNullOrWhiteSpace(this.CloudBuildNumber)) + { + cloudBuild.SetCloudBuildNumber(this.CloudBuildNumber, stdout, stderr); + } + + if (this.CloudBuildVersionVars != null) + { + foreach (var variable in this.CloudBuildVersionVars) + { + cloudBuild.SetCloudBuildVariable(variable.ItemSpec, variable.GetMetadata("Value"), stdout, stderr); + } + } + + if (isUnitTest) + { + PipeOutputToMSBuildLog(testStdOut.ToString(), warning: false); + PipeOutputToMSBuildLog(testStdErr.ToString(), warning: true); + } + } + else + { + this.Log.LogMessage(MessageImportance.Low, "No supported cloud build detected, so no variables or build number set."); + } + + return !this.Log.HasLoggedErrors; + } + + private void PipeOutputToMSBuildLog(string output, bool warning) + { + using (var logReader = new StringReader(output)) + { + string line; + while ((line = logReader.ReadLine()) != null) + { + // The prefix is presumed to nullify the effect in a real cloud build, + // yet make it detectable by a unit test. + string message = $"UnitTest: {line}"; + if (warning) + { + this.Log.LogWarning(message); + } + else + { + this.Log.LogMessage(message); + } + } + } + } + } +} From a1e5795910292cc50a505d44e9aec6921f166a6c Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 18 Jun 2016 23:08:49 -0700 Subject: [PATCH 2/6] Rename some properties to make them more friendly --- src/NerdBank.GitVersioning/VersionOracle.cs | 46 ++++++++----------- .../GetBuildVersion.cs | 10 ++-- 2 files changed, 25 insertions(+), 31 deletions(-) diff --git a/src/NerdBank.GitVersioning/VersionOracle.cs b/src/NerdBank.GitVersioning/VersionOracle.cs index f456f8b3f..0d9471272 100644 --- a/src/NerdBank.GitVersioning/VersionOracle.cs +++ b/src/NerdBank.GitVersioning/VersionOracle.cs @@ -57,12 +57,12 @@ public VersionOracle(string projectDirectory, LibGit2Sharp.Repository repo, IClo var commit = repo?.Head.Commits.FirstOrDefault(); this.GitCommitId = commit?.Id.Sha ?? cloudBuild?.GitCommitId ?? null; - this.GitVersionHeight = repo?.GetVersionHeight(relativeRepoProjectDirectory) ?? 0; + this.VersionHeight = repo?.GetVersionHeight(relativeRepoProjectDirectory) ?? 0; this.BuildingRef = cloudBuild?.BuildingTag ?? cloudBuild?.BuildingBranch ?? repo?.Head.CanonicalName; // Override the typedVersion with the special build number and revision components, when available. - this.TypedVersion = repo?.GetIdAsVersion(relativeRepoProjectDirectory, this.GitVersionHeight) ?? this.VersionOptions?.Version.Version; - this.TypedVersion = this.TypedVersion ?? new Version(); + this.Version = repo?.GetIdAsVersion(relativeRepoProjectDirectory, this.VersionHeight) ?? this.VersionOptions?.Version.Version; + this.Version = this.Version ?? new Version(); this.CloudBuildNumberOptions = this.VersionOptions?.CloudBuild?.BuildNumber ?? new VersionOptions.CloudBuildNumberOptions(); @@ -85,7 +85,7 @@ public string CloudBuildNumber (commitIdOptions.When == VersionOptions.CloudBuildNumberCommitWhen.NonPublicReleaseOnly && !this.PublicRelease); bool commitIdInRevision = includeCommitInfo && commitIdOptions.Where == VersionOptions.CloudBuildNumberCommitWhere.FourthVersionComponent; bool commitIdInBuildMetadata = includeCommitInfo && commitIdOptions.Where == VersionOptions.CloudBuildNumberCommitWhere.BuildMetadata; - Version buildNumberVersion = commitIdInRevision ? this.TypedVersion : this.TypedVersionWithoutRevision; + Version buildNumberVersion = commitIdInRevision ? this.Version : this.SimpleVersion; string buildNumberMetadata = FormatBuildMetadata(commitIdInBuildMetadata ? this.BuildMetadataWithCommitId : this.BuildMetadata); return buildNumberVersion + this.PrereleaseVersion + buildNumberMetadata; } @@ -94,7 +94,7 @@ public string CloudBuildNumber /// /// Gets a value indicating whether the cloud build number should be set. /// - public bool SetCloudBuildNumber => this.CloudBuildNumberOptions.Enabled; + public bool CloudBuildNumberEnabled => this.CloudBuildNumberOptions.Enabled; /// /// Gets the build metadata identifiers, including the git commit ID as the first identifier if appropriate. @@ -123,18 +123,18 @@ public IEnumerable BuildMetadataWithCommitId /// /// Gets the version string to use for the . /// - public Version AssemblyVersion => GetAssemblyVersion(this.TypedVersion, this.VersionOptions).EnsureNonNegativeComponents(); + public Version AssemblyVersion => GetAssemblyVersion(this.Version, this.VersionOptions).EnsureNonNegativeComponents(); /// /// Gets the version string to use for the . /// - public Version AssemblyFileVersion => this.TypedVersion; + public Version AssemblyFileVersion => this.Version; /// /// Gets the version string to use for the . /// public string AssemblyInformationalVersion => - $"{this.TypedVersion.ToStringSafe(3)}{this.PrereleaseVersion}{FormatBuildMetadata(this.BuildMetadataWithCommitId)}"; + $"{this.Version.ToStringSafe(3)}{this.PrereleaseVersion}{FormatBuildMetadata(this.BuildMetadataWithCommitId)}"; /// /// Gets or sets a value indicating whether the project is building @@ -147,23 +147,17 @@ public IEnumerable BuildMetadataWithCommitId /// public string PrereleaseVersion => this.VersionOptions?.Version.Prerelease ?? string.Empty; - /// - /// Gets the version string to use in the official release name (lacks revision number). - /// - [Obsolete("Use " + nameof(TypedVersionWithoutRevision) + " instead")] - public string SimpleVersion => this.TypedVersionWithoutRevision.ToString(); - /// /// Gets the version information without a Revision component. /// - public Version TypedVersionWithoutRevision => this.TypedVersion.Build >= 0 - ? new Version(this.TypedVersion.Major, this.TypedVersion.Minor, this.TypedVersion.Build) - : new Version(this.TypedVersion.Major, this.TypedVersion.Minor); + public Version SimpleVersion => this.Version.Build >= 0 + ? new Version(this.Version.Major, this.Version.Minor, this.Version.Build) + : new Version(this.Version.Major, this.Version.Minor); /// - /// Gets the build number (git height) for this version. + /// Gets the build number (git height + offset) for this version. /// - public int BuildNumber => Math.Max(0, this.TypedVersion.Build); + public int BuildNumber => Math.Max(0, this.Version.Build); /// /// Gets or sets the major.minor version string. @@ -171,7 +165,7 @@ public IEnumerable BuildMetadataWithCommitId /// /// The x.y string (no build number or revision number). /// - public Version MajorMinorVersion => new Version(this.TypedVersion.Major, this.TypedVersion.Minor); + public Version MajorMinorVersion => new Version(this.Version.Major, this.Version.Minor); /// /// Gets the Git revision control commit id for HEAD (the current source code version). @@ -188,24 +182,24 @@ public IEnumerable BuildMetadataWithCommitId /// the specified commit and the most distant ancestor (inclusive) /// that set the version to the value at HEAD. /// - public int GitVersionHeight { get; } + public int VersionHeight { get; } private string BuildingRef { get; } /// /// Gets the version for this project, with up to 4 components. /// - public Version TypedVersion { get; } + public Version Version { get; } /// /// Gets a value indicating whether to set cloud build version variables. /// - public bool SetCloudBuildVersionVars => this.VersionOptions?.CloudBuild?.SetVersionVariables + public bool CloudBuildVersionVarsEnabled => this.VersionOptions?.CloudBuild?.SetVersionVariables ?? (new VersionOptions.CloudBuildOptions()).SetVersionVariables; /// /// Gets a dictionary of cloud build variables that applies to this project, - /// regardless of the current setting of . + /// regardless of the current setting of . /// public IDictionary CloudBuildVersionVars { @@ -214,7 +208,7 @@ public IDictionary CloudBuildVersionVars return new Dictionary(StringComparer.OrdinalIgnoreCase) { { "GitAssemblyInformationalVersion", this.AssemblyInformationalVersion }, - { "GitBuildVersion", this.TypedVersion.ToString() }, + { "GitBuildVersion", this.Version.ToString() }, }; } } @@ -233,7 +227,7 @@ public IDictionary CloudBuildVersionVars /// Gets the version to use for NuGet packages. /// public string NuGetPackageVersion => - $"{this.TypedVersion.ToStringSafe(3)}{this.PrereleaseVersion}{this.SemVer1GitCommitIdShortPackageSuffix}"; + $"{this.Version.ToStringSafe(3)}{this.PrereleaseVersion}{this.SemVer1GitCommitIdShortPackageSuffix}"; /// /// Gets the version to use for NPM packages. diff --git a/src/Nerdbank.GitVersioning.Tasks/GetBuildVersion.cs b/src/Nerdbank.GitVersioning.Tasks/GetBuildVersion.cs index 765dfad45..d2a757329 100644 --- a/src/Nerdbank.GitVersioning.Tasks/GetBuildVersion.cs +++ b/src/Nerdbank.GitVersioning.Tasks/GetBuildVersion.cs @@ -159,23 +159,23 @@ public override bool Execute() } this.PublicRelease = oracle.PublicRelease; - this.Version = oracle.TypedVersion.ToString(); + this.Version = oracle.Version.ToString(); this.AssemblyVersion = oracle.AssemblyVersion.ToString(); this.AssemblyFileVersion = oracle.AssemblyFileVersion.ToString(); this.AssemblyInformationalVersion = oracle.AssemblyInformationalVersion; - this.SimpleVersion = oracle.TypedVersionWithoutRevision.ToString(); + this.SimpleVersion = oracle.SimpleVersion.ToString(); this.MajorMinorVersion = oracle.MajorMinorVersion.ToString(); this.BuildNumber = oracle.BuildNumber; this.PrereleaseVersion = oracle.PrereleaseVersion; this.GitCommitId = oracle.GitCommitId; this.GitCommitIdShort = oracle.GitCommitIdShort; - this.GitVersionHeight = oracle.GitVersionHeight; + this.GitVersionHeight = oracle.VersionHeight; this.BuildMetadataFragment = oracle.BuildMetadataFragment; - this.CloudBuildNumber = oracle.SetCloudBuildNumber ? oracle.CloudBuildNumber : null; + this.CloudBuildNumber = oracle.CloudBuildNumberEnabled ? oracle.CloudBuildNumber : null; this.NuGetPackageVersion = oracle.NuGetPackageVersion; this.NpmPackageVersion = oracle.NpmPackageVersion; - if (oracle.SetCloudBuildVersionVars) + if (oracle.CloudBuildVersionVarsEnabled) { this.CloudBuildVersionVars = oracle.CloudBuildVersionVars .Select(item => new TaskItem(item.Key, new Dictionary { { "Value", item.Value } })) From 74368eb8a4e2297bd629b310df8f7973a8684088 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 18 Jun 2016 23:19:06 -0700 Subject: [PATCH 3/6] Add SemVer1 and SemVer2 properties to the VersionOracle --- src/NerdBank.GitVersioning/VersionOracle.cs | 27 ++++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/NerdBank.GitVersioning/VersionOracle.cs b/src/NerdBank.GitVersioning/VersionOracle.cs index 0d9471272..cab0f1403 100644 --- a/src/NerdBank.GitVersioning/VersionOracle.cs +++ b/src/NerdBank.GitVersioning/VersionOracle.cs @@ -226,22 +226,41 @@ public IDictionary CloudBuildVersionVars /// /// Gets the version to use for NuGet packages. /// - public string NuGetPackageVersion => - $"{this.Version.ToStringSafe(3)}{this.PrereleaseVersion}{this.SemVer1GitCommitIdShortPackageSuffix}"; + public string NuGetPackageVersion => this.SemVer1; /// /// Gets the version to use for NPM packages. /// - public string NpmPackageVersion => this.NuGetPackageVersion; + public string NpmPackageVersion => this.SemVer1; - private string SemVer1GitCommitIdShortPackageSuffix => + /// + /// Gets a SemVer 1.0 compliant string that represents this version, including the -gCOMMITID suffix + /// when is false. + /// + public string SemVer1 => + $"{this.Version.ToStringSafe(3)}{this.PrereleaseVersion}{this.SemVer1BuildMetadata}"; + + /// + /// Gets a SemVer 2.0 compliant string that represents this version, including a +gCOMMITID suffix + /// when is false. + /// + public string SemVer2 => + $"{this.Version.ToStringSafe(3)}{this.PrereleaseVersion}{this.SemVer2BuildMetadata}"; + + private string SemVer1BuildMetadata => this.PublicRelease ? string.Empty : $"-g{this.GitCommitIdShort}"; + private string SemVer2BuildMetadata => + FormatBuildMetadata(this.PublicRelease ? this.BuildMetadata : this.BuildMetadataWithCommitId); + private VersionOptions.CloudBuildNumberOptions CloudBuildNumberOptions { get; } private static string FormatBuildMetadata(IEnumerable identifiers) => (identifiers?.Any() ?? false) ? "+" + string.Join(".", identifiers) : string.Empty; + private static string FormatBuildMetadataSemVerV1(IEnumerable identifiers) => + (identifiers?.Any() ?? false) ? "-" + string.Join("-", identifiers) : string.Empty; + private static LibGit2Sharp.Repository OpenGitRepo(string repoRoot) { Requires.NotNullOrEmpty(repoRoot, nameof(repoRoot)); From d1455c73bd6a64d6fe4367b07a33bfd78690ffa3 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 18 Jun 2016 23:24:56 -0700 Subject: [PATCH 4/6] Fix Get-Version.ps1 to let PublicRelease use its default value --- src/Nerdbank.GitVersioning.NuGet/tools/Get-Version.ps1 | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/Nerdbank.GitVersioning.NuGet/tools/Get-Version.ps1 b/src/Nerdbank.GitVersioning.NuGet/tools/Get-Version.ps1 index 87e0bdcc9..ad6a50570 100644 --- a/src/Nerdbank.GitVersioning.NuGet/tools/Get-Version.ps1 +++ b/src/Nerdbank.GitVersioning.NuGet/tools/Get-Version.ps1 @@ -3,16 +3,11 @@ Finds the git commit ID that was built to produce some specific version of an assembly. .PARAMETER ProjectDirectory The directory of the project that built the assembly, within the git repo. -.PARAMETER PublicRelease -Forces version generation to consider this a public release. -Similar to the MSBuild /p:PublicRelease=true switch. #> [CmdletBinding(SupportsShouldProcess)] Param( [Parameter()] - [string]$ProjectDirectory=".", - [Parameter()] - [switch]$PublicRelease + [string]$ProjectDirectory="." ) if (!$DependencyBasePath) { $DependencyBasePath = "$PSScriptRoot\..\build" } @@ -26,8 +21,6 @@ $ProjectDirectory = Resolve-Path $ProjectDirectory try { $CloudBuild = [Nerdbank.GitVersioning.CloudBuild]::Active $VersionOracle = [Nerdbank.GitVersioning.VersionOracle]::Create($ProjectDirectory, $null, $CloudBuild) - $VersionOracle.PublicRelease = $PublicRelease - $VersionOracle } finally { From f3fd3e39f191d7d21c4fb8ef03ad6e5eec839a32 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 18 Jun 2016 23:38:35 -0700 Subject: [PATCH 5/6] Rename getGitVersion and add more fields to interface --- src/nerdbank-gitversioning.npm/gulpfile.js | 2 +- src/nerdbank-gitversioning.npm/ts/index.ts | 45 ++++++++++++++++------ src/nerdbank-gitversioning.npm/ts/main.ts | 2 +- 3 files changed, 36 insertions(+), 13 deletions(-) diff --git a/src/nerdbank-gitversioning.npm/gulpfile.js b/src/nerdbank-gitversioning.npm/gulpfile.js index f78731194..4058ccf67 100644 --- a/src/nerdbank-gitversioning.npm/gulpfile.js +++ b/src/nerdbank-gitversioning.npm/gulpfile.js @@ -52,7 +52,7 @@ gulp.task('setPackageVersion', ['copyPackageContents'], function() { gulp.task('setPackageVersionToken', ['copyPackageContents'], function() { var nbgv = require(`./${outDir}`); - return nbgv.getGitVersion() + return nbgv.getVersion() .then(function(v) { var replacements = { version: { current: v.semVer1 } diff --git a/src/nerdbank-gitversioning.npm/ts/index.ts b/src/nerdbank-gitversioning.npm/ts/index.ts index 78bf8ef24..3de8bd8ec 100644 --- a/src/nerdbank-gitversioning.npm/ts/index.ts +++ b/src/nerdbank-gitversioning.npm/ts/index.ts @@ -10,14 +10,29 @@ import {execAsync} from './asyncprocess'; * The various aspects of a version that can be calculated. */ export interface IGitVersion { - version: string; - simpleVersion: string; - majorMinorVersoin: string; - commitId: string; - commitIdShort: string; - versionHeight: string; - semVer1: string; - semVer2: string; + cloudBuildNumber: string, + cloudBuildNumberEnabled: boolean, + buildMetadataWithCommitId: string, + assemblyVersion: string, + assemblyFileVersion: string, + assemblyInformationalVersion: string, + publicRelease: boolean, + prereleaseVersion: string, + simpleVersion: string, + buildNumber: string, + majorMinorVersion: string, + gitCommitId: string, + gitCommitIdShort: string, + versionHeight: string, + version: string, + cloudBuildVersionVarsEnabled: boolean, + cloudBuildVersionVars: string, + buildMetadata: string, + buildMetadataFragment: string, + nuGetPackageVersion: string, + npmPackageVersion: string, + semVer1: string, + semVer2: string } /** Gets the version of the Nerdbank.GitVersioning nuget package to download. */ @@ -34,7 +49,7 @@ function getNuGetPkgVersion() { * Gets an object describing various aspects of the version of a project. * @param projectDirectory The directory of the source code to get the version of. */ -export async function getGitVersion(projectDirectory?: string) : Promise { +export async function getVersion(projectDirectory?: string): Promise { projectDirectory = projectDirectory || '.'; var packageInstallPath = await installNuGetPackage('Nerdbank.GitVersioning', getNuGetPkgVersion()); var getVersionScriptPath = path.join(packageInstallPath.packageDir, "tools", "Get-Version.ps1"); @@ -47,7 +62,15 @@ export async function getGitVersion(projectDirectory?: string) : Promiseresult; @@ -61,7 +84,7 @@ export async function getGitVersion(projectDirectory?: string) : Promise Date: Sat, 18 Jun 2016 23:44:52 -0700 Subject: [PATCH 6/6] AppVeyor unit test run fixes --- .../BuildIntegrationTests.cs | 36 ++++++++++++------- .../CloudBuildServices/AppVeyor.cs | 9 +++-- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/NerdBank.GitVersioning.Tests/BuildIntegrationTests.cs b/src/NerdBank.GitVersioning.Tests/BuildIntegrationTests.cs index 87ea57f7e..95c01bda3 100644 --- a/src/NerdBank.GitVersioning.Tests/BuildIntegrationTests.cs +++ b/src/NerdBank.GitVersioning.Tests/BuildIntegrationTests.cs @@ -369,8 +369,8 @@ public static IEnumerable CloudBuildOfBranch(string branchName) { return new object[][] { - new object[] { CloudBuild.AppVeyor.Add("APPVEYOR_REPO_BRANCH", branchName) }, - new object[] { CloudBuild.VSTS.Add( "BUILD_SOURCEBRANCH", $"refs/heads/{branchName}") }, + new object[] { CloudBuild.AppVeyor.SetItem("APPVEYOR_REPO_BRANCH", branchName) }, + new object[] { CloudBuild.VSTS.SetItem( "BUILD_SOURCEBRANCH", $"refs/heads/{branchName}") }, }; } @@ -536,12 +536,15 @@ public async Task PublicRelease_RegEx_SatisfiedByCheckedOutBranch() this.WriteVersionFile(versionOptions); this.InitializeSourceControl(); - // Check out a branch that conforms. - var releaseBranch = this.Repo.CreateBranch("release"); - this.Repo.Checkout(releaseBranch); - var buildResult = await this.BuildAsync(); - Assert.True(buildResult.PublicRelease); - AssertStandardProperties(versionOptions, buildResult); + using (ApplyEnvironmentVariables(CloudBuild.SuppressEnvironment)) + { + // Check out a branch that conforms. + var releaseBranch = this.Repo.CreateBranch("release"); + this.Repo.Checkout(releaseBranch); + var buildResult = await this.BuildAsync(); + Assert.True(buildResult.PublicRelease); + AssertStandardProperties(versionOptions, buildResult); + } } [Theory] @@ -884,10 +887,19 @@ public void Dispose() private static class CloudBuild { - public static readonly ImmutableDictionary VSTS = ImmutableDictionary.Empty - .Add("SYSTEM_TEAMPROJECTID", "1"); - public static readonly ImmutableDictionary AppVeyor = ImmutableDictionary.Empty - .Add("APPVEYOR", "True"); + public static readonly ImmutableDictionary SuppressEnvironment = ImmutableDictionary.Empty + // AppVeyor + .Add("APPVEYOR", string.Empty) + .Add("APPVEYOR_REPO_TAG", string.Empty) + .Add("APPVEYOR_REPO_TAG_NAME", string.Empty) + .Add("APPVEYOR_PULL_REQUEST_NUMBER", string.Empty) + // VSTS + .Add("SYSTEM_TEAMPROJECTID", string.Empty) + .Add("BUILD_SOURCEBRANCH", string.Empty); + public static readonly ImmutableDictionary VSTS = SuppressEnvironment + .SetItem("SYSTEM_TEAMPROJECTID", "1"); + public static readonly ImmutableDictionary AppVeyor = SuppressEnvironment + .SetItem("APPVEYOR", "True"); } private static class Targets diff --git a/src/NerdBank.GitVersioning/CloudBuildServices/AppVeyor.cs b/src/NerdBank.GitVersioning/CloudBuildServices/AppVeyor.cs index febc2c036..f054c4da9 100644 --- a/src/NerdBank.GitVersioning/CloudBuildServices/AppVeyor.cs +++ b/src/NerdBank.GitVersioning/CloudBuildServices/AppVeyor.cs @@ -52,8 +52,13 @@ private static void RunAppveyor(string args, TextWriter stdout, TextWriter stder { try { - Process.Start("appveyor", args) - .WaitForExit(); + // Skip this if this build is running in our own unit tests, since that can + // mess with AppVeyor's actual build information. + if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("_NBGV_UnitTest"))) + { + Process.Start("appveyor", args) + .WaitForExit(); + } } catch (Win32Exception ex) when ((uint)ex.HResult == 0x80004005) {