diff --git a/src/GitVersion.Core.Tests/Extensions/GitToolsTestingExtensions.cs b/src/GitVersion.Core.Tests/Extensions/GitToolsTestingExtensions.cs index 20decb9958..32141671bb 100644 --- a/src/GitVersion.Core.Tests/Extensions/GitToolsTestingExtensions.cs +++ b/src/GitVersion.Core.Tests/Extensions/GitToolsTestingExtensions.cs @@ -54,10 +54,16 @@ public static IBranch CreateMockBranch(string name, params ICommit[] commits) public static void DumpGraph(this IRepository repository, Action? writer = null, int? maxCommits = null) => GitExtensions.DumpGraph(repository.ToGitRepository().Path, writer, maxCommits); - public static VersionVariables GetVersion(this RepositoryFixtureBase fixture, Config? configuration = null, IRepository? repository = null, string? commitId = null, bool onlyTrackedBranches = true, string? branch = null) + public static VersionVariables GetVersion( + this RepositoryFixtureBase fixture, + Config? configuration = null, + IRepository? repository = null, + string? commitId = null, + bool onlyTrackedBranches = true, + string? branch = null, + Action? configureServices = null) { configuration ??= new ConfigurationBuilder().Build(); - repository ??= fixture.Repository; var options = Options.Create(new GitVersionOptions @@ -73,11 +79,11 @@ public static VersionVariables GetVersion(this RepositoryFixtureBase fixture, Co }); var sp = ConfigureServices(services => services.AddSingleton(options)); + configureServices?.Invoke(sp); var variableProvider = sp.GetRequiredService(); var nextVersionCalculator = sp.GetRequiredService(); var contextOptions = sp.GetRequiredService>(); - var context = contextOptions.Value; try @@ -104,7 +110,15 @@ public static void WriteVersionVariables(this RepositoryFixtureBase fixture, str writer.Write(versionInfo.ToString()); } - public static void AssertFullSemver(this RepositoryFixtureBase fixture, string fullSemver, Config? configuration = null, IRepository? repository = null, string? commitId = null, bool onlyTrackedBranches = true, string? targetBranch = null) + public static void AssertFullSemver( + this RepositoryFixtureBase fixture, + string fullSemver, + Config? configuration = null, + IRepository? repository = null, + string? commitId = null, + bool onlyTrackedBranches = true, + string? targetBranch = null, + Action? configureServices = null) { configuration ??= new Config(); configuration = new ConfigurationBuilder().Add(configuration).Build(); @@ -112,7 +126,7 @@ public static void AssertFullSemver(this RepositoryFixtureBase fixture, string f try { - var variables = fixture.GetVersion(configuration, repository, commitId, onlyTrackedBranches, targetBranch); + var variables = fixture.GetVersion(configuration, repository, commitId, onlyTrackedBranches, targetBranch, configureServices); variables.FullSemVer.ShouldBe(fullSemver); } catch (Exception) diff --git a/src/GitVersion.Core.Tests/IntegrationTests/RemoteRepositoryScenarios.cs b/src/GitVersion.Core.Tests/IntegrationTests/RemoteRepositoryScenarios.cs index b85daea32e..012193824b 100644 --- a/src/GitVersion.Core.Tests/IntegrationTests/RemoteRepositoryScenarios.cs +++ b/src/GitVersion.Core.Tests/IntegrationTests/RemoteRepositoryScenarios.cs @@ -98,6 +98,23 @@ public void GivenARemoteGitRepositoryWhenCheckingOutDetachedHeadUsingExistingImp $"It looks like the branch being examined is a detached Head pointing to commit '{fixture.LocalRepositoryFixture.Repository.Head.Tip.Id.ToString(7)}'. Without a proper branch name GitVersion cannot determine the build version."); } + [Test] + public void GivenARemoteGitRepositoryWhenCheckingOutDetachedHeadWithEnvironmentVariableSucceeds() + { + using var fixture = new RemoteRepositoryFixture(); + var local = fixture.LocalRepositoryFixture; + Commands.Checkout(local.Repository, local.Repository.Head.Tip); + + fixture.AssertFullSemver("0.1.0+4", + repository: local.Repository, + configureServices: sp => + { + sp.GetRequiredService().SetEnvironmentVariable("GIT_BRANCH", "main"); + sp.GetRequiredService().Prepare(); + }); + } + + [Test] [Ignore("Needs more investigations.")] public void GivenARemoteGitRepositoryWhenCheckingOutDetachedHeadUsingTrackingBranchOnlyBehaviourShouldReturnVersion014Plus5() diff --git a/src/GitVersion.Core/BuildAgents/LocalBuild.cs b/src/GitVersion.Core/BuildAgents/LocalBuild.cs index 65f19663be..89ef1d9a51 100644 --- a/src/GitVersion.Core/BuildAgents/LocalBuild.cs +++ b/src/GitVersion.Core/BuildAgents/LocalBuild.cs @@ -5,10 +5,13 @@ namespace GitVersion.BuildAgents; public class LocalBuild : BuildAgentBase { - public LocalBuild(IEnvironment environment, ILog log) : base(environment, log) + public LocalBuild(IEnvironment environment, ILog log) + : base(environment, log) { } + protected override string EnvironmentVariable => string.Empty; + public override string? GetCurrentBranch(bool usingDynamicRepos) => Environment.GetEnvironmentVariable("GIT_BRANCH"); public override bool CanApplyToCurrentContext() => true; public override string? GenerateSetVersionMessage(VersionVariables variables) => null; public override string[] GenerateSetParameterMessage(string name, string value) => Array.Empty(); diff --git a/src/GitVersion.Core/Core/GitPreparer.cs b/src/GitVersion.Core/Core/GitPreparer.cs index f54ceefe38..1ef63aef72 100644 --- a/src/GitVersion.Core/Core/GitPreparer.cs +++ b/src/GitVersion.Core/Core/GitPreparer.cs @@ -44,8 +44,7 @@ public void Prepare() // Normalize if we are running on build server var normalizeGitDirectory = !gitVersionOptions.Settings.NoNormalize && this.buildAgent is not LocalBuild; var shouldCleanUpRemotes = this.buildAgent.ShouldCleanUpRemotes(); - var currentBranch = ResolveCurrentBranch(); - + var currentBranch = this.context.CurrentBranch?.Name.WithoutRemote; var dotGitDirectory = this.repositoryInfo.DotGitDirectory; var projectRoot = this.repositoryInfo.ProjectRootDirectory; @@ -79,18 +78,6 @@ private void PrepareInternal(bool normalizeGitDirectory, string? currentBranch, } } - private string? ResolveCurrentBranch() - { - var gitVersionOptions = this.options.Value; - var targetBranch = gitVersionOptions.RepositoryInfo.TargetBranch; - - var isDynamicRepository = !gitVersionOptions.RepositoryInfo.DynamicRepositoryClonePath.IsNullOrWhiteSpace(); - var currentBranch = this.buildAgent.GetCurrentBranch(isDynamicRepository) ?? targetBranch; - this.log.Info("Branch from build environment: " + currentBranch); - - return currentBranch; - } - private void CleanupDuplicateOrigin() { var remoteToKeep = DefaultRemoteName; diff --git a/src/GitVersion.Core/Core/GitVersionContextFactory.cs b/src/GitVersion.Core/Core/GitVersionContextFactory.cs index 07ad156e6d..a89635551e 100644 --- a/src/GitVersion.Core/Core/GitVersionContextFactory.cs +++ b/src/GitVersion.Core/Core/GitVersionContextFactory.cs @@ -1,6 +1,8 @@ +using GitVersion.BuildAgents; using GitVersion.Common; using GitVersion.Configuration; using GitVersion.Extensions; +using GitVersion.Logging; using Microsoft.Extensions.Options; namespace GitVersion; @@ -10,36 +12,81 @@ public class GitVersionContextFactory : IGitVersionContextFactory private readonly IConfigProvider configProvider; private readonly IRepositoryStore repositoryStore; private readonly IBranchConfigurationCalculator branchConfigurationCalculator; + private readonly IBuildAgent buildAgent; + private readonly ILog log; private readonly IOptions options; - public GitVersionContextFactory(IConfigProvider configProvider, IRepositoryStore repositoryStore, IBranchConfigurationCalculator branchConfigurationCalculator, IOptions options) + public GitVersionContextFactory(IConfigProvider configProvider, IRepositoryStore repositoryStore, IBranchConfigurationCalculator branchConfigurationCalculator, IBuildAgentResolver buildAgentResolver, ILog log, IOptions options) { this.configProvider = configProvider.NotNull(); this.repositoryStore = repositoryStore.NotNull(); this.branchConfigurationCalculator = branchConfigurationCalculator.NotNull(); + this.buildAgent = buildAgentResolver.NotNull().Resolve(); + this.log = log.NotNull(); this.options = options.NotNull(); } public GitVersionContext Create(GitVersionOptions gitVersionOptions) { - var currentBranch = this.repositoryStore.GetTargetBranch(gitVersionOptions.RepositoryInfo.TargetBranch); - if (currentBranch == null) - throw new InvalidOperationException("Need a branch to operate on"); + var branchCommit = ResolveCurrentBranchCommit(gitVersionOptions); + var currentBranch = branchCommit.Branch; + var currentCommit = branchCommit.Commit; + var configuration = this.configProvider.Provide(this.options.Value.ConfigInfo.OverrideConfig); + var currentBranchConfig = this.branchConfigurationCalculator.GetBranchConfiguration(currentBranch, currentCommit, configuration); + var effectiveConfiguration = configuration.CalculateEffectiveConfiguration(currentBranchConfig); + var currentCommitTaggedVersion = this.repositoryStore.GetCurrentCommitTaggedVersion(currentCommit, effectiveConfiguration); + var numberOfUncommittedChanges = this.repositoryStore.GetNumberOfUncommittedChanges(); + return new GitVersionContext(currentBranch, currentCommit, configuration, effectiveConfiguration, currentCommitTaggedVersion, numberOfUncommittedChanges); + } + + private BranchCommit ResolveCurrentBranchCommit(GitVersionOptions gitVersionOptions) + { + var currentBranch = ResolveCurrentBranch(); var currentCommit = this.repositoryStore.GetCurrentCommit(currentBranch, gitVersionOptions.RepositoryInfo.CommitId); - var configuration = this.configProvider.Provide(this.options.Value.ConfigInfo.OverrideConfig); if (currentBranch.IsDetachedHead) { - var branchForCommit = this.repositoryStore.GetBranchesContainingCommit(currentCommit, onlyTrackedBranches: gitVersionOptions.Settings.OnlyTrackedBranches).OnlyOrDefault(); - currentBranch = branchForCommit ?? currentBranch; + currentBranch = this.repositoryStore.GetBranchesContainingCommit(currentCommit, + onlyTrackedBranches: gitVersionOptions.Settings.OnlyTrackedBranches).OnlyOrDefault() + ?? currentBranch; } - var currentBranchConfig = this.branchConfigurationCalculator.GetBranchConfiguration(currentBranch, currentCommit, configuration); - var effectiveConfiguration = configuration.CalculateEffectiveConfiguration(currentBranchConfig); - var currentCommitTaggedVersion = this.repositoryStore.GetCurrentCommitTaggedVersion(currentCommit, effectiveConfiguration); - var numberOfUncommittedChanges = this.repositoryStore.GetNumberOfUncommittedChanges(); + return new BranchCommit(currentCommit, currentBranch); + } - return new GitVersionContext(currentBranch, currentCommit, configuration, effectiveConfiguration, currentCommitTaggedVersion, numberOfUncommittedChanges); + private IBranch ResolveCurrentBranch() + { + var currentBranchName = ResolveCurrentBranchName(); + var currentBranch = this.repositoryStore.GetTargetBranch(currentBranchName); + + if (currentBranch == null) + throw new InvalidOperationException("Need a branch to operate on"); + + return currentBranch; + } + + private string? ResolveCurrentBranchName() + { + var gitVersionOptions = this.options.Value; + var isDynamicRepository = !gitVersionOptions.RepositoryInfo.DynamicRepositoryClonePath.IsNullOrWhiteSpace(); + var currentBranch = this.buildAgent.GetCurrentBranch(isDynamicRepository); + + if (!currentBranch.IsNullOrWhiteSpace()) + { + this.log.Info($"Branch from build environment: {currentBranch}"); + return currentBranch; + } + + var targetBranch = gitVersionOptions.RepositoryInfo.TargetBranch; + + if (!targetBranch.IsNullOrWhiteSpace()) + { + this.log.Info($"Branch from Git repository: {targetBranch}"); + return targetBranch; + } + + this.log.Info($"No branch found in environment or repository; this is probably a detached HEAD."); + return null; } } diff --git a/src/GitVersion.Core/Model/GitVersionContext.cs b/src/GitVersion.Core/Model/GitVersionContext.cs index e712a76862..ecfff98128 100644 --- a/src/GitVersion.Core/Model/GitVersionContext.cs +++ b/src/GitVersion.Core/Model/GitVersionContext.cs @@ -3,12 +3,28 @@ namespace GitVersion; /// -/// Contextual information about where GitVersion is being run +/// Contextual information about where GitVersion is being run /// public class GitVersionContext { + public GitVersionContext( + IBranch currentBranch, + ICommit? currentCommit, + Config configuration, + EffectiveConfiguration effectiveConfiguration, + SemanticVersion currentCommitTaggedVersion, + int numberOfUncommittedChanges) + { + CurrentBranch = currentBranch; + CurrentCommit = currentCommit; + FullConfiguration = configuration; + Configuration = effectiveConfiguration; + CurrentCommitTaggedVersion = currentCommitTaggedVersion; + NumberOfUncommittedChanges = numberOfUncommittedChanges; + } + /// - /// Contains the raw configuration, use Configuration for specific config based on the current GitVersion context. + /// Contains the raw configuration, use Configuration for specific config based on the current GitVersion context. /// public Config FullConfiguration { get; } @@ -17,20 +33,5 @@ public class GitVersionContext public IBranch CurrentBranch { get; } public ICommit? CurrentCommit { get; } public bool IsCurrentCommitTagged => CurrentCommitTaggedVersion != null; - public int NumberOfUncommittedChanges { get; } - - public GitVersionContext(IBranch currentBranch, ICommit? currentCommit, - Config configuration, EffectiveConfiguration effectiveConfiguration, - SemanticVersion currentCommitTaggedVersion, int numberOfUncommittedChanges) - { - CurrentBranch = currentBranch; - CurrentCommit = currentCommit; - - FullConfiguration = configuration; - Configuration = effectiveConfiguration; - - CurrentCommitTaggedVersion = currentCommitTaggedVersion; - NumberOfUncommittedChanges = numberOfUncommittedChanges; - } }