Skip to content

Commit e6d3897

Browse files
authored
Merge pull request #77 from AArnott/fix70
Parity between Get-Version.ps1 and GetBuildVersion MSBuild task
2 parents d6281ba + 784af64 commit e6d3897

File tree

20 files changed

+820
-332
lines changed

20 files changed

+820
-332
lines changed

src/NerdBank.GitVersioning.Tests/BuildIntegrationTests.cs

Lines changed: 113 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
public class BuildIntegrationTests : RepoTestBase
2828
{
2929
private const string GitVersioningTargetsFileName = "NerdBank.GitVersioning.targets";
30+
private const string UnitTestCloudBuildPrefix = "UnitTest: ";
3031
private static readonly string[] ToxicEnvironmentVariablePrefixes = new string[]
3132
{
3233
"APPVEYOR",
@@ -42,7 +43,6 @@ public class BuildIntegrationTests : RepoTestBase
4243
// Set global properties to neutralize environment variables
4344
// that might actually be defined by a CI that is building and running these tests.
4445
{ "PublicRelease", string.Empty },
45-
{ "_NBGV_UnitTest", "true" }
4646
};
4747
private Random random;
4848

@@ -59,6 +59,7 @@ public BuildIntegrationTests(ITestOutputHelper logger)
5959
this.LoadTargetsIntoProjectCollection();
6060
this.testProject = this.CreateProjectRootElement(this.projectDirectory, "test.proj");
6161
this.globalProperties.Add("NerdbankGitVersioningTasksPath", Environment.CurrentDirectory + "\\");
62+
Environment.SetEnvironmentVariable("_NBGV_UnitTest", "true");
6263

6364
// Sterilize the test of any environment variables.
6465
foreach (System.Collections.DictionaryEntry variable in Environment.GetEnvironmentVariables())
@@ -71,6 +72,12 @@ public BuildIntegrationTests(ITestOutputHelper logger)
7172
}
7273
}
7374

75+
protected override void Dispose(bool disposing)
76+
{
77+
Environment.SetEnvironmentVariable("_NBGV_UnitTest", string.Empty);
78+
base.Dispose(disposing);
79+
}
80+
7481
[Fact]
7582
public async Task GetBuildVersion_Returns_BuildVersion_Property()
7683
{
@@ -104,7 +111,7 @@ public async Task GetBuildVersion_OutsideGit_PointingToGit()
104111

105112
// Write the same version file to the 'real' repo
106113
this.WriteVersionFile(version);
107-
114+
108115
// Point the project to the 'real' repo
109116
this.testProject.AddProperty("GitRepoRoot", this.RepoPath);
110117

@@ -362,8 +369,8 @@ public static IEnumerable<object[]> CloudBuildOfBranch(string branchName)
362369
{
363370
return new object[][]
364371
{
365-
new object[] { CloudBuild.AppVeyor.Add("APPVEYOR_REPO_BRANCH", branchName) },
366-
new object[] { CloudBuild.VSTS.Add( "BUILD_SOURCEBRANCH", $"refs/heads/{branchName}") },
372+
new object[] { CloudBuild.AppVeyor.SetItem("APPVEYOR_REPO_BRANCH", branchName) },
373+
new object[] { CloudBuild.VSTS.SetItem( "BUILD_SOURCEBRANCH", $"refs/heads/{branchName}") },
367374
};
368375
}
369376

@@ -382,14 +389,12 @@ public async Task PublicRelease_RegEx_SatisfiedByCI(IReadOnlyDictionary<string,
382389
// Don't actually switch the checked out branch in git. CI environment variables
383390
// should take precedence over actual git configuration. (Why? because these variables may
384391
// retain information about which tag was checked out on a detached head).
385-
foreach (var property in serverProperties)
392+
using (ApplyEnvironmentVariables(serverProperties))
386393
{
387-
this.globalProperties[property.Key] = property.Value;
394+
var buildResult = await this.BuildAsync();
395+
Assert.True(buildResult.PublicRelease);
396+
AssertStandardProperties(versionOptions, buildResult);
388397
}
389-
390-
var buildResult = await this.BuildAsync();
391-
Assert.True(buildResult.PublicRelease);
392-
AssertStandardProperties(versionOptions, buildResult);
393398
}
394399

395400
public static object[][] CloudBuildVariablesData
@@ -407,44 +412,42 @@ public static object[][] CloudBuildVariablesData
407412
[MemberData(nameof(CloudBuildVariablesData))]
408413
public async Task CloudBuildVariables_SetInCI(IReadOnlyDictionary<string, string> properties, string expectedMessage)
409414
{
410-
foreach (var property in properties)
411-
{
412-
this.globalProperties[property.Key] = property.Value;
413-
}
414-
415-
string keyName = "n1";
416-
string value = "v1";
417-
this.testProject.AddItem("CloudBuildVersionVars", keyName, new Dictionary<string, string> { { "Value", value } });
418-
419-
string alwaysExpectedMessage = expectedMessage
420-
.Replace("{NAME}", keyName)
421-
.Replace("{VALUE}", value);
422-
423-
var versionOptions = new VersionOptions
415+
using (ApplyEnvironmentVariables(properties))
424416
{
425-
Version = SemanticVersion.Parse("1.0"),
426-
CloudBuild = new VersionOptions.CloudBuildOptions { SetVersionVariables = true },
427-
};
428-
this.WriteVersionFile(versionOptions);
429-
this.InitializeSourceControl();
417+
string keyName = "n1";
418+
string value = "v1";
419+
this.testProject.AddItem("CloudBuildVersionVars", keyName, new Dictionary<string, string> { { "Value", value } });
430420

431-
var buildResult = await this.BuildAsync();
432-
AssertStandardProperties(versionOptions, buildResult);
433-
string conditionallyExpectedMessage = expectedMessage
434-
.Replace("{NAME}", "GitBuildVersion")
435-
.Replace("{VALUE}", buildResult.BuildVersion);
436-
Assert.Contains(alwaysExpectedMessage, buildResult.LoggedEvents.Select(e => e.Message.TrimEnd()));
437-
Assert.Contains(conditionallyExpectedMessage, buildResult.LoggedEvents.Select(e => e.Message.TrimEnd()));
421+
string alwaysExpectedMessage = UnitTestCloudBuildPrefix + expectedMessage
422+
.Replace("{NAME}", keyName)
423+
.Replace("{VALUE}", value);
438424

439-
versionOptions.CloudBuild.SetVersionVariables = false;
440-
this.WriteVersionFile(versionOptions);
441-
buildResult = await this.BuildAsync();
442-
AssertStandardProperties(versionOptions, buildResult);
443-
conditionallyExpectedMessage = expectedMessage
444-
.Replace("{NAME}", "GitBuildVersion")
445-
.Replace("{VALUE}", buildResult.BuildVersion);
446-
Assert.Contains(alwaysExpectedMessage, buildResult.LoggedEvents.Select(e => e.Message.TrimEnd()));
447-
Assert.DoesNotContain(conditionallyExpectedMessage, buildResult.LoggedEvents.Select(e => e.Message.TrimEnd()));
425+
var versionOptions = new VersionOptions
426+
{
427+
Version = SemanticVersion.Parse("1.0"),
428+
CloudBuild = new VersionOptions.CloudBuildOptions { SetVersionVariables = true },
429+
};
430+
this.WriteVersionFile(versionOptions);
431+
this.InitializeSourceControl();
432+
433+
var buildResult = await this.BuildAsync();
434+
AssertStandardProperties(versionOptions, buildResult);
435+
string conditionallyExpectedMessage = UnitTestCloudBuildPrefix + expectedMessage
436+
.Replace("{NAME}", "GitBuildVersion")
437+
.Replace("{VALUE}", buildResult.BuildVersion);
438+
Assert.Contains(alwaysExpectedMessage, buildResult.LoggedEvents.Select(e => e.Message.TrimEnd()));
439+
Assert.Contains(conditionallyExpectedMessage, buildResult.LoggedEvents.Select(e => e.Message.TrimEnd()));
440+
441+
versionOptions.CloudBuild.SetVersionVariables = false;
442+
this.WriteVersionFile(versionOptions);
443+
buildResult = await this.BuildAsync();
444+
AssertStandardProperties(versionOptions, buildResult);
445+
conditionallyExpectedMessage = UnitTestCloudBuildPrefix + expectedMessage
446+
.Replace("{NAME}", "GitBuildVersion")
447+
.Replace("{VALUE}", buildResult.BuildVersion);
448+
Assert.Contains(alwaysExpectedMessage, buildResult.LoggedEvents.Select(e => e.Message.TrimEnd()));
449+
Assert.DoesNotContain(conditionallyExpectedMessage, buildResult.LoggedEvents.Select(e => e.Message.TrimEnd()));
450+
}
448451
}
449452

450453
private static VersionOptions BuildNumberVersionOptionsBasis
@@ -483,16 +486,23 @@ public async Task BuildNumber_SetInCI(VersionOptions versionOptions, IReadOnlyDi
483486
{
484487
this.WriteVersionFile(versionOptions);
485488
this.InitializeSourceControl();
486-
487-
foreach (var property in properties)
489+
using (ApplyEnvironmentVariables(properties))
488490
{
489-
this.globalProperties[property.Key] = property.Value;
491+
var buildResult = await this.BuildAsync();
492+
AssertStandardProperties(versionOptions, buildResult);
493+
expectedBuildNumberMessage = expectedBuildNumberMessage.Replace("{CLOUDBUILDNUMBER}", buildResult.CloudBuildNumber);
494+
Assert.Contains(UnitTestCloudBuildPrefix + expectedBuildNumberMessage, buildResult.LoggedEvents.Select(e => e.Message.TrimEnd()));
490495
}
491496

492-
var buildResult = await this.BuildAsync();
493-
AssertStandardProperties(versionOptions, buildResult);
494-
expectedBuildNumberMessage = expectedBuildNumberMessage.Replace("{CLOUDBUILDNUMBER}", buildResult.CloudBuildNumber);
495-
Assert.Contains(expectedBuildNumberMessage, buildResult.LoggedEvents.Select(e => e.Message.TrimEnd()));
497+
versionOptions.CloudBuild.BuildNumber.Enabled = false;
498+
this.WriteVersionFile(versionOptions);
499+
using (ApplyEnvironmentVariables(properties))
500+
{
501+
var buildResult = await this.BuildAsync();
502+
AssertStandardProperties(versionOptions, buildResult);
503+
expectedBuildNumberMessage = expectedBuildNumberMessage.Replace("{CLOUDBUILDNUMBER}", buildResult.CloudBuildNumber);
504+
Assert.DoesNotContain(UnitTestCloudBuildPrefix + expectedBuildNumberMessage, buildResult.LoggedEvents.Select(e => e.Message.TrimEnd()));
505+
}
496506
}
497507

498508
[Theory]
@@ -526,12 +536,15 @@ public async Task PublicRelease_RegEx_SatisfiedByCheckedOutBranch()
526536
this.WriteVersionFile(versionOptions);
527537
this.InitializeSourceControl();
528538

529-
// Check out a branch that conforms.
530-
var releaseBranch = this.Repo.CreateBranch("release");
531-
this.Repo.Checkout(releaseBranch);
532-
var buildResult = await this.BuildAsync();
533-
Assert.True(buildResult.PublicRelease);
534-
AssertStandardProperties(versionOptions, buildResult);
539+
using (ApplyEnvironmentVariables(CloudBuild.SuppressEnvironment))
540+
{
541+
// Check out a branch that conforms.
542+
var releaseBranch = this.Repo.CreateBranch("release");
543+
this.Repo.Checkout(releaseBranch);
544+
var buildResult = await this.BuildAsync();
545+
Assert.True(buildResult.PublicRelease);
546+
AssertStandardProperties(versionOptions, buildResult);
547+
}
535548
}
536549

537550
[Theory]
@@ -705,6 +718,20 @@ private static Version GetExpectedAssemblyVersion(VersionOptions versionOptions,
705718
return assemblyVersion;
706719
}
707720

721+
private static RestoreEnvironmentVariables ApplyEnvironmentVariables(IReadOnlyDictionary<string, string> variables)
722+
{
723+
Requires.NotNull(variables, nameof(variables));
724+
725+
var oldValues = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
726+
foreach (var variable in variables)
727+
{
728+
oldValues[variable.Key] = Environment.GetEnvironmentVariable(variable.Key);
729+
Environment.SetEnvironmentVariable(variable.Key, variable.Value);
730+
}
731+
732+
return new RestoreEnvironmentVariables(oldValues);
733+
}
734+
708735
private void AssertStandardProperties(VersionOptions versionOptions, BuildResults buildResult, string relativeProjectDirectory = null)
709736
{
710737
int versionHeight = this.Repo.GetVersionHeight(relativeProjectDirectory);
@@ -728,9 +755,6 @@ private void AssertStandardProperties(VersionOptions versionOptions, BuildResult
728755
Assert.Equal($"{assemblyVersion.Major}.{assemblyVersion.Minor}.{assemblyVersion.Build}.{assemblyVersion.Revision}", buildResult.AssemblyVersion);
729756

730757
Assert.Equal(idAsVersion.Build.ToString(), buildResult.BuildNumber);
731-
Assert.Equal(idAsVersion.Build.ToString(), buildResult.BuildNumberFirstAndSecondComponentsIfApplicable);
732-
Assert.Equal(idAsVersion.Build.ToString(), buildResult.BuildNumberFirstComponent);
733-
Assert.Equal(string.Empty, buildResult.BuildNumberSecondComponent);
734758
Assert.Equal($"{version}", buildResult.BuildVersion);
735759
Assert.Equal($"{idAsVersion.Major}.{idAsVersion.Minor}.{idAsVersion.Build}", buildResult.BuildVersion3Components);
736760
Assert.Equal(idAsVersion.Build.ToString(), buildResult.BuildVersionNumberComponent);
@@ -846,12 +870,36 @@ private void MakeItAVBProject()
846870
csharpImport.Project = @"$(MSBuildToolsPath)\Microsoft.VisualBasic.targets";
847871
}
848872

873+
private struct RestoreEnvironmentVariables : IDisposable
874+
{
875+
private readonly IReadOnlyDictionary<string, string> applyVariables;
876+
877+
internal RestoreEnvironmentVariables(IReadOnlyDictionary<string, string> applyVariables)
878+
{
879+
this.applyVariables = applyVariables;
880+
}
881+
882+
public void Dispose()
883+
{
884+
ApplyEnvironmentVariables(this.applyVariables);
885+
}
886+
}
887+
849888
private static class CloudBuild
850889
{
851-
public static readonly ImmutableDictionary<string, string> VSTS = ImmutableDictionary<string, string>.Empty
852-
.Add("SYSTEM_TEAMPROJECTID", "1");
853-
public static readonly ImmutableDictionary<string, string> AppVeyor = ImmutableDictionary<string, string>.Empty
854-
.Add("APPVEYOR", "True");
890+
public static readonly ImmutableDictionary<string, string> SuppressEnvironment = ImmutableDictionary<string, string>.Empty
891+
// AppVeyor
892+
.Add("APPVEYOR", string.Empty)
893+
.Add("APPVEYOR_REPO_TAG", string.Empty)
894+
.Add("APPVEYOR_REPO_TAG_NAME", string.Empty)
895+
.Add("APPVEYOR_PULL_REQUEST_NUMBER", string.Empty)
896+
// VSTS
897+
.Add("SYSTEM_TEAMPROJECTID", string.Empty)
898+
.Add("BUILD_SOURCEBRANCH", string.Empty);
899+
public static readonly ImmutableDictionary<string, string> VSTS = SuppressEnvironment
900+
.SetItem("SYSTEM_TEAMPROJECTID", "1");
901+
public static readonly ImmutableDictionary<string, string> AppVeyor = SuppressEnvironment
902+
.SetItem("APPVEYOR", "True");
855903
}
856904

857905
private static class Targets
@@ -882,9 +930,6 @@ internal BuildResults(BuildResult buildResult, IReadOnlyList<BuildEventArgs> log
882930
public string PrereleaseVersion => this.BuildResult.ProjectStateAfterBuild.GetPropertyValue("PrereleaseVersion");
883931
public string MajorMinorVersion => this.BuildResult.ProjectStateAfterBuild.GetPropertyValue("MajorMinorVersion");
884932
public string BuildVersionNumberComponent => this.BuildResult.ProjectStateAfterBuild.GetPropertyValue("BuildVersionNumberComponent");
885-
public string BuildNumberFirstComponent => this.BuildResult.ProjectStateAfterBuild.GetPropertyValue("BuildNumberFirstComponent");
886-
public string BuildNumberSecondComponent => this.BuildResult.ProjectStateAfterBuild.GetPropertyValue("BuildNumberSecondComponent");
887-
public string BuildNumberFirstAndSecondComponentsIfApplicable => this.BuildResult.ProjectStateAfterBuild.GetPropertyValue("BuildNumberFirstAndSecondComponentsIfApplicable");
888933
public string GitCommitIdShort => this.BuildResult.ProjectStateAfterBuild.GetPropertyValue("GitCommitIdShort");
889934
public string GitVersionHeight => this.BuildResult.ProjectStateAfterBuild.GetPropertyValue("GitVersionHeight");
890935
public string SemVerBuildSuffix => this.BuildResult.ProjectStateAfterBuild.GetPropertyValue("SemVerBuildSuffix");

src/NerdBank.GitVersioning.Tests/RepoTestBase.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22
{
33
using System;
44
using System.Collections.Generic;
5+
using System.Diagnostics;
56
using System.IO;
67
using System.Linq;
78
using System.Text;
89
using System.Threading.Tasks;
910
using LibGit2Sharp;
1011
using Validation;
1112
using Xunit.Abstractions;
12-
using System.Diagnostics;
13+
1314
public abstract class RepoTestBase : IDisposable
1415
{
1516
public RepoTestBase(ITestOutputHelper logger)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
namespace Nerdbank.GitVersioning
2+
{
3+
using System.Linq;
4+
using CloudBuildServices;
5+
6+
/// <summary>
7+
/// Provides access to cloud build providers.
8+
/// </summary>
9+
public static class CloudBuild
10+
{
11+
/// <summary>
12+
/// An array of cloud build systems we support.
13+
/// </summary>
14+
private static readonly ICloudBuild[] SupportedCloudBuilds = new ICloudBuild[] {
15+
new AppVeyor(),
16+
new VisualStudioTeamServices(),
17+
new TeamCity(),
18+
};
19+
20+
/// <summary>
21+
/// Gets the cloud build provider that applies to this build, if any.
22+
/// </summary>
23+
public static ICloudBuild Active => SupportedCloudBuilds.FirstOrDefault(cb => cb.IsApplicable);
24+
}
25+
}

0 commit comments

Comments
 (0)