Skip to content

Commit 67f794a

Browse files
committed
Enhance branch label configuration with environment variable support
Added the ability to use environment variable placeholders in branch labels, allowing for dynamic label generation in CI/CD scenarios. Introduced fallback values for environment variables and updated related tests to ensure correct processing of these new features. Updated the GetBranchSpecificLabel method to handle environment variables even when regex does not match, maintaining backward compatibility.
1 parent ac50e99 commit 67f794a

File tree

4 files changed

+172
-7
lines changed

4 files changed

+172
-7
lines changed

docs/input/docs/reference/configuration.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -892,6 +892,32 @@ of `alpha.foo` with `label: 'alpha.{BranchName}'` and `regex: '^features?[\/-](?
892892

893893
Another example: branch `features/sc-12345/some-description` would become a pre-release label of `sc-12345` with `label: '{StoryNo}'` and `regex: '^features?[\/-](?<StoryNo>sc-\d+)[-/].+'`.
894894

895+
You can also use environment variable placeholders with the `{env:VARIABLE_NAME}` syntax. This is particularly useful for CI/CD scenarios, such as using GitHub Actions context variables in pull request labels.
896+
897+
For example, to use the GitHub Actions head ref in a pull request label:
898+
```yaml
899+
branches:
900+
pull-request:
901+
label: 'pr-{env:GITHUB_HEAD_REF}'
902+
regex: '^pull[/-]'
903+
```
904+
905+
You can combine regex placeholders with environment variables:
906+
```yaml
907+
branches:
908+
feature:
909+
label: '{BranchName}-{env:GITHUB_HEAD_REF}'
910+
regex: '^features?[\/-](?<BranchName>.+)'
911+
```
912+
913+
Environment variables support fallback values using the `{env:VARIABLE_NAME ?? "fallback"}` syntax:
914+
```yaml
915+
branches:
916+
pull-request:
917+
label: 'pr-{env:GITHUB_HEAD_REF ?? "unknown"}'
918+
regex: '^pull[/-]'
919+
```
920+
895921
**Note:** To clear a default use an empty string: `label: ''`
896922

897923
### increment

src/GitVersion.Configuration.Tests/Configuration/ConfigurationExtensionsTests.cs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using GitVersion.Configuration;
2+
using GitVersion.Core;
23
using GitVersion.Core.Tests.Helpers;
34
using GitVersion.Git;
45

@@ -55,4 +56,91 @@ public void EnsureGetBranchSpecificLabelWorksAsExpected(string branchName, strin
5556
var actual = effectiveConfiguration.GetBranchSpecificLabel(ReferenceName.FromBranchName(branchName), null);
5657
actual.ShouldBe(expectedLabel);
5758
}
59+
60+
[Test]
61+
public void EnsureGetBranchSpecificLabelProcessesEnvironmentVariables()
62+
{
63+
var environment = new TestEnvironment();
64+
environment.SetEnvironmentVariable("GITHUB_HEAD_REF", "feature-branch");
65+
66+
var configuration = GitFlowConfigurationBuilder.New
67+
.WithoutBranches()
68+
.WithBranch("pull-request", builder => builder
69+
.WithLabel("pr-{env:GITHUB_HEAD_REF}")
70+
.WithRegularExpression(@"^pull[/-]"))
71+
.Build();
72+
73+
var effectiveConfiguration = configuration.GetEffectiveConfiguration(ReferenceName.FromBranchName("pull-request"));
74+
var actual = effectiveConfiguration.GetBranchSpecificLabel(ReferenceName.FromBranchName("pull-request"), null, environment);
75+
actual.ShouldBe("pr-feature-branch");
76+
}
77+
78+
[Test]
79+
public void EnsureGetBranchSpecificLabelProcessesEnvironmentVariablesWithFallback()
80+
{
81+
var environment = new TestEnvironment();
82+
// Don't set GITHUB_HEAD_REF to test fallback
83+
84+
var configuration = GitFlowConfigurationBuilder.New
85+
.WithoutBranches()
86+
.WithBranch("pull-request", builder => builder
87+
.WithLabel("pr-{env:GITHUB_HEAD_REF ?? \"unknown\"}")
88+
.WithRegularExpression(@"^pull[/-]"))
89+
.Build();
90+
91+
var effectiveConfiguration = configuration.GetEffectiveConfiguration(ReferenceName.FromBranchName("pull-request"));
92+
var actual = effectiveConfiguration.GetBranchSpecificLabel(ReferenceName.FromBranchName("pull-request"), null, environment);
93+
actual.ShouldBe("pr-unknown");
94+
}
95+
96+
[Test]
97+
public void EnsureGetBranchSpecificLabelProcessesEnvironmentVariablesAndRegexPlaceholders()
98+
{
99+
var environment = new TestEnvironment();
100+
environment.SetEnvironmentVariable("GITHUB_HEAD_REF", "feature-branch");
101+
102+
var configuration = GitFlowConfigurationBuilder.New
103+
.WithoutBranches()
104+
.WithBranch("feature/test-branch", builder => builder
105+
.WithLabel("{BranchName}-{env:GITHUB_HEAD_REF}")
106+
.WithRegularExpression(@"^features?[\/-](?<BranchName>.+)"))
107+
.Build();
108+
109+
var effectiveConfiguration = configuration.GetEffectiveConfiguration(ReferenceName.FromBranchName("feature/test-branch"));
110+
var actual = effectiveConfiguration.GetBranchSpecificLabel(ReferenceName.FromBranchName("feature/test-branch"), null, environment);
111+
actual.ShouldBe("test-branch-feature-branch");
112+
}
113+
114+
[Test]
115+
public void EnsureGetBranchSpecificLabelWorksWithoutEnvironmentWhenNoEnvPlaceholders()
116+
{
117+
var configuration = GitFlowConfigurationBuilder.New
118+
.WithoutBranches()
119+
.WithBranch("feature/test", builder => builder
120+
.WithLabel("{BranchName}")
121+
.WithRegularExpression(@"^features?[\/-](?<BranchName>.+)"))
122+
.Build();
123+
124+
var effectiveConfiguration = configuration.GetEffectiveConfiguration(ReferenceName.FromBranchName("feature/test"));
125+
var actual = effectiveConfiguration.GetBranchSpecificLabel(ReferenceName.FromBranchName("feature/test"), null, null);
126+
actual.ShouldBe("test");
127+
}
128+
129+
[Test]
130+
public void EnsureGetBranchSpecificLabelProcessesEnvironmentVariablesEvenWhenRegexDoesNotMatch()
131+
{
132+
var environment = new TestEnvironment();
133+
environment.SetEnvironmentVariable("GITHUB_HEAD_REF", "feature-branch");
134+
135+
var configuration = GitFlowConfigurationBuilder.New
136+
.WithoutBranches()
137+
.WithBranch("pull-request", builder => builder
138+
.WithLabel("pr-{env:GITHUB_HEAD_REF}")
139+
.WithRegularExpression(@"^pull[/-]"))
140+
.Build();
141+
142+
var effectiveConfiguration = configuration.GetEffectiveConfiguration(ReferenceName.FromBranchName("other-branch"));
143+
var actual = effectiveConfiguration.GetBranchSpecificLabel(ReferenceName.FromBranchName("other-branch"), null, environment);
144+
actual.ShouldBe("pr-feature-branch");
145+
}
58146
}

src/GitVersion.Core/Extensions/ConfigurationExtensions.cs

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System.IO.Abstractions;
22
using GitVersion.Core;
33
using GitVersion.Extensions;
4+
using GitVersion.Formatting;
45
using GitVersion.Git;
56
using GitVersion.Helpers;
67
using GitVersion.VersionCalculation;
@@ -75,11 +76,11 @@ public static bool IsReleaseBranch(this IGitVersionConfiguration configuration,
7576
=> configuration.GetBranchConfiguration(branchName).IsReleaseBranch ?? false;
7677

7778
public static string? GetBranchSpecificLabel(
78-
this EffectiveConfiguration configuration, ReferenceName branchName, string? branchNameOverride)
79-
=> GetBranchSpecificLabel(configuration, branchName.WithoutOrigin, branchNameOverride);
79+
this EffectiveConfiguration configuration, ReferenceName branchName, string? branchNameOverride, IEnvironment? environment = null)
80+
=> GetBranchSpecificLabel(configuration, branchName.WithoutOrigin, branchNameOverride, environment);
8081

8182
public static string? GetBranchSpecificLabel(
82-
this EffectiveConfiguration configuration, string? branchName, string? branchNameOverride)
83+
this EffectiveConfiguration configuration, string? branchName, string? branchNameOverride, IEnvironment? environment = null)
8384
{
8485
configuration.NotNull();
8586

@@ -90,10 +91,43 @@ public static bool IsReleaseBranch(this IGitVersionConfiguration configuration,
9091
}
9192

9293
var effectiveBranchName = branchNameOverride ?? branchName;
93-
if (configuration.RegularExpression.IsNullOrWhiteSpace() || effectiveBranchName.IsNullOrEmpty()) return label;
94+
if (configuration.RegularExpression.IsNullOrWhiteSpace() || effectiveBranchName.IsNullOrEmpty())
95+
{
96+
// Even if regex doesn't match, we should still process environment variables
97+
if (environment is not null)
98+
{
99+
try
100+
{
101+
label = label.FormatWith(new { }, environment);
102+
}
103+
catch (ArgumentException)
104+
{
105+
// If environment variable is missing and no fallback, return label as-is
106+
// This maintains backward compatibility
107+
}
108+
}
109+
return label;
110+
}
111+
94112
var regex = RegexPatterns.Cache.GetOrAdd(configuration.RegularExpression);
95113
var match = regex.Match(effectiveBranchName);
96-
if (!match.Success) return label;
114+
if (!match.Success)
115+
{
116+
// Even if regex doesn't match, we should still process environment variables
117+
if (environment is not null)
118+
{
119+
try
120+
{
121+
label = label.FormatWith(new { }, environment);
122+
}
123+
catch (ArgumentException)
124+
{
125+
// If environment variable is missing and no fallback, return label as-is
126+
}
127+
}
128+
return label;
129+
}
130+
97131
foreach (var groupName in regex.GetGroupNames())
98132
{
99133
var groupValue = match.Groups[groupName].Value;
@@ -107,6 +141,21 @@ public static bool IsReleaseBranch(this IGitVersionConfiguration configuration,
107141
startIndex = index + escapedGroupValue.Length;
108142
}
109143
}
144+
145+
// Process environment variable placeholders after regex placeholders
146+
if (environment is not null)
147+
{
148+
try
149+
{
150+
label = label.FormatWith(new { }, environment);
151+
}
152+
catch (ArgumentException)
153+
{
154+
// If environment variable is missing and no fallback, return label as-is
155+
// This maintains backward compatibility
156+
}
157+
}
158+
110159
return label;
111160
}
112161

src/GitVersion.Core/VersionCalculation/VersionCalculators/NextVersionCalculator.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@ internal class NextVersionCalculator(
1414
IEnumerable<IDeploymentModeCalculator> deploymentModeCalculators,
1515
IEnumerable<IVersionStrategy> versionStrategies,
1616
IEffectiveBranchConfigurationFinder effectiveBranchConfigurationFinder,
17-
ITaggedSemanticVersionService taggedSemanticVersionService)
17+
ITaggedSemanticVersionService taggedSemanticVersionService,
18+
IEnvironment environment)
1819
: INextVersionCalculator
1920
{
2021
private readonly ILog log = log.NotNull();
2122
private readonly Lazy<GitVersionContext> versionContext = versionContext.NotNull();
2223
private readonly IVersionStrategy[] versionStrategies = [.. versionStrategies.NotNull()];
2324
private readonly IEffectiveBranchConfigurationFinder effectiveBranchConfigurationFinder = effectiveBranchConfigurationFinder.NotNull();
25+
private readonly IEnvironment environment = environment.NotNull();
2426

2527
private GitVersionContext Context => this.versionContext.Value;
2628

@@ -108,7 +110,7 @@ private bool TryGetSemanticVersion(
108110
{
109111
result = null;
110112

111-
var label = effectiveConfiguration.GetBranchSpecificLabel(Context.CurrentBranch.Name, null);
113+
var label = effectiveConfiguration.GetBranchSpecificLabel(Context.CurrentBranch.Name, null, this.environment);
112114
var currentCommitTaggedVersion = taggedSemanticVersionsOfCurrentCommit
113115
.Where(element => element.Value.IsMatchForBranchSpecificLabel(label)).Max();
114116

0 commit comments

Comments
 (0)