Skip to content

Commit 54c1ed0

Browse files
authored
Merge pull request #3069 from davidkeaveny/feature/add-bitbucket-build-agent
Add build agent for BitBucket Pipelines
2 parents dfa2b21 + ae36224 commit 54c1ed0

File tree

4 files changed

+328
-0
lines changed

4 files changed

+328
-0
lines changed
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
---
2+
Order: 35
3+
Title: BitBucket Pipelines
4+
Description: Details on the Atlassian BitBucket Pipelines support in GitVersion
5+
---
6+
7+
## Basic Usage
8+
9+
To use GitVersion with Atlassian BitBucket Pipelines, you will need to install and run the GitVersion CLI tool
10+
in your build step.
11+
12+
## Executing GitVersion
13+
14+
### Using the GitVersion CLI tool
15+
16+
An example pipeline is shown below:
17+
18+
```yml
19+
image: mcr.microsoft.com/dotnet/sdk:6.0
20+
21+
clone:
22+
depth: full
23+
24+
pipelines:
25+
default:
26+
- step:
27+
name: Version and build
28+
script:
29+
- export PATH="$PATH:/root/.dotnet/tools"
30+
- dotnet tool install --global GitVersion.Tool --version 5.*
31+
- dotnet-gitversion /buildserver
32+
- source gitversion.properties
33+
- echo Building with semver $GITVERSION_FULLSEMVER
34+
- dotnet build
35+
```
36+
37+
:::{.alert .alert-danger}
38+
**Important**
39+
40+
You must set the `clone:depth` setting as shown above; without it, BitBucket Pipelines will perform a shallow clone, which will
41+
cause GitVersion will display an error message.
42+
:::
43+
44+
When the action `dotnet-gitversion /buildserver` is executed, it will detect that it is running in BitBucket Pipelines by the presence of
45+
the `BITBUCKET_WORKSPACE` environment variable, which is set by the BitBucket Pipelines engine. It will generate a text file named `gitversion.properties`
46+
which contains all the output of the GitVersion tool, exported as individual environment variables prefixed with `GITVERSION_`.
47+
These environment variables can then be imported back into the build step using the `source gitversion.properties` action.
48+
49+
If you want to share the text file across multiple build steps, then you will need to save it as an artifact. A more complex example pipeline
50+
is shown below:
51+
52+
```yml
53+
image: mcr.microsoft.com/dotnet/sdk:6.0
54+
55+
clone:
56+
depth: full
57+
58+
pipelines:
59+
default:
60+
- step:
61+
name: Version
62+
script:
63+
- export PATH="$PATH:/root/.dotnet/tools"
64+
- dotnet tool install --global GitVersion.Tool --version 5.*
65+
- dotnet-gitversion /buildserver
66+
artifacts:
67+
- gitversion.properties
68+
- step:
69+
name: Build
70+
script:
71+
- source gitversion.properties
72+
- echo Building with semver $GITVERSION_FULLSEMVER
73+
- dotnet build
74+
```
75+
76+
[Variables and Secrets](https://support.atlassian.com/bitbucket-cloud/docs/variables-and-secrets/)
77+
[Clone Options](https://bitbucket.org/blog/support-for-more-clone-options-at-the-step-level)

src/GitVersion.App.Tests/PullRequestInBuildAgentTest.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,19 @@ public async Task VerifyTravisCIPullRequest(string pullRequestRef)
128128
await VerifyPullRequestVersionIsCalculatedProperly(pullRequestRef, env);
129129
}
130130

131+
132+
[TestCaseSource(nameof(PrMergeRefs))]
133+
public async Task VerifyBitBucketPipelinesPullRequest(string pullRequestRef)
134+
{
135+
136+
var env = new Dictionary<string, string>
137+
{
138+
{ BitBucketPipelines.EnvironmentVariableName, "MyWorkspace" },
139+
{ BitBucketPipelines.PullRequestEnvironmentVariableName, pullRequestRef }
140+
};
141+
await VerifyPullRequestVersionIsCalculatedProperly(pullRequestRef, env);
142+
}
143+
131144
private static async Task VerifyPullRequestVersionIsCalculatedProperly(string pullRequestRef, Dictionary<string, string> env)
132145
{
133146
using var fixture = new EmptyRepositoryFixture();
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
using GitVersion.BuildAgents;
2+
using GitVersion.Core.Tests.Helpers;
3+
using GitVersion.Helpers;
4+
using GitVersion.VersionCalculation;
5+
using Microsoft.Extensions.DependencyInjection;
6+
using NUnit.Framework;
7+
using Shouldly;
8+
9+
namespace GitVersion.Core.Tests.BuildAgents;
10+
11+
[TestFixture]
12+
public class BitBucketPipelinesTests : TestBase
13+
{
14+
private IEnvironment environment;
15+
private BitBucketPipelines buildServer;
16+
private IServiceProvider sp;
17+
18+
[SetUp]
19+
public void SetEnvironmentVariableForTest()
20+
{
21+
this.sp = ConfigureServices(services => services.AddSingleton<BitBucketPipelines>());
22+
this.environment = sp.GetRequiredService<IEnvironment>();
23+
this.buildServer = sp.GetRequiredService<BitBucketPipelines>();
24+
25+
this.environment.SetEnvironmentVariable(BitBucketPipelines.EnvironmentVariableName, "MyWorkspace");
26+
}
27+
28+
29+
[Test]
30+
public void CanNotApplyToCurrentContextWhenEnvironmentVariableNotSet()
31+
{
32+
// Arrange
33+
this.environment.SetEnvironmentVariable(BitBucketPipelines.EnvironmentVariableName, "");
34+
35+
// Act
36+
var result = this.buildServer.CanApplyToCurrentContext();
37+
38+
// Assert
39+
result.ShouldBeFalse();
40+
}
41+
42+
[Test]
43+
public void CalculateVersionOnMainBranch()
44+
{
45+
// Arrange
46+
this.environment.SetEnvironmentVariable(BitBucketPipelines.BranchEnvironmentVariableName, "refs/heads/main");
47+
48+
var vars = new TestableVersionVariables(fullSemVer: "1.2.3");
49+
var vsVersion = this.buildServer.GenerateSetVersionMessage(vars);
50+
51+
vsVersion.ShouldBe("1.2.3");
52+
}
53+
54+
[Test]
55+
public void CalculateVersionOnDevelopBranch()
56+
{
57+
// Arrange
58+
this.environment.SetEnvironmentVariable(BitBucketPipelines.BranchEnvironmentVariableName, "refs/heads/develop");
59+
60+
var vars = new TestableVersionVariables(fullSemVer: "1.2.3-unstable.4");
61+
var vsVersion = this.buildServer.GenerateSetVersionMessage(vars);
62+
63+
vsVersion.ShouldBe("1.2.3-unstable.4");
64+
}
65+
66+
[Test]
67+
public void CalculateVersionOnFeatureBranch()
68+
{
69+
// Arrange
70+
this.environment.SetEnvironmentVariable(BitBucketPipelines.BranchEnvironmentVariableName, "refs/heads/feature/my-work");
71+
72+
var vars = new TestableVersionVariables(fullSemVer: "1.2.3-beta.4");
73+
var vsVersion = this.buildServer.GenerateSetVersionMessage(vars);
74+
75+
vsVersion.ShouldBe("1.2.3-beta.4");
76+
}
77+
78+
[Test]
79+
public void GetCurrentBranchShouldHandleBranches()
80+
{
81+
// Arrange
82+
this.environment.SetEnvironmentVariable(BitBucketPipelines.BranchEnvironmentVariableName, "refs/heads/feature/my-work");
83+
84+
// Act
85+
var result = this.buildServer.GetCurrentBranch(false);
86+
87+
// Assert
88+
result.ShouldBe($"refs/heads/feature/my-work");
89+
}
90+
91+
[Test]
92+
public void GetCurrentBranchShouldHandleTags()
93+
{
94+
// Arrange
95+
this.environment.SetEnvironmentVariable(BitBucketPipelines.BranchEnvironmentVariableName, null);
96+
this.environment.SetEnvironmentVariable(BitBucketPipelines.TagEnvironmentVariableName, "refs/heads/tags/1.2.3");
97+
98+
// Act
99+
var result = this.buildServer.GetCurrentBranch(false);
100+
101+
// Assert
102+
result.ShouldBeNull();
103+
}
104+
105+
[Test]
106+
public void GetCurrentBranchShouldHandlePullRequests()
107+
{
108+
// Arrange
109+
this.environment.SetEnvironmentVariable(BitBucketPipelines.BranchEnvironmentVariableName, null);
110+
this.environment.SetEnvironmentVariable(BitBucketPipelines.TagEnvironmentVariableName, null);
111+
this.environment.SetEnvironmentVariable(BitBucketPipelines.PullRequestEnvironmentVariableName, "refs/pull/1/merge");
112+
113+
// Act
114+
var result = this.buildServer.GetCurrentBranch(false);
115+
116+
// Assert
117+
result.ShouldBeNull();
118+
}
119+
120+
121+
[Test]
122+
public void WriteAllVariablesToTheTextWriter()
123+
{
124+
var assemblyLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
125+
assemblyLocation.ShouldNotBeNull();
126+
var f = PathHelper.Combine(assemblyLocation, "gitversion.properties");
127+
128+
try
129+
{
130+
AssertVariablesAreWrittenToFile(f);
131+
}
132+
finally
133+
{
134+
File.Delete(f);
135+
}
136+
}
137+
138+
private void AssertVariablesAreWrittenToFile(string file)
139+
{
140+
var writes = new List<string?>();
141+
var semanticVersion = new SemanticVersion
142+
{
143+
Major = 1,
144+
Minor = 2,
145+
Patch = 3,
146+
PreReleaseTag = "beta1",
147+
BuildMetaData = "5"
148+
};
149+
150+
semanticVersion.BuildMetaData.CommitDate = new DateTimeOffset(2022, 4, 6, 16, 10, 59, TimeSpan.FromHours(10));
151+
semanticVersion.BuildMetaData.Sha = "f28807e615e9f06aec8a33c87780374e0c1f6fb8";
152+
153+
var config = new TestEffectiveConfiguration();
154+
var variableProvider = this.sp.GetRequiredService<IVariableProvider>();
155+
156+
var variables = variableProvider.GetVariablesFor(semanticVersion, config, false);
157+
158+
this.buildServer.WithPropertyFile(file);
159+
160+
this.buildServer.WriteIntegration(writes.Add, variables);
161+
162+
writes[1].ShouldBe("1.2.3-beta.1+5");
163+
164+
File.Exists(file).ShouldBe(true);
165+
166+
var props = File.ReadAllText(file);
167+
168+
props.ShouldContain("export GITVERSION_MAJOR=1");
169+
props.ShouldContain("export GITVERSION_MINOR=2");
170+
props.ShouldContain("export GITVERSION_SHA=f28807e615e9f06aec8a33c87780374e0c1f6fb8");
171+
props.ShouldContain("export GITVERSION_COMMITDATE=2022-04-06");
172+
}
173+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
using GitVersion.Logging;
2+
using GitVersion.OutputVariables;
3+
4+
namespace GitVersion.BuildAgents;
5+
6+
public class BitBucketPipelines : BuildAgentBase
7+
{
8+
public const string EnvironmentVariableName = "BITBUCKET_WORKSPACE";
9+
public const string BranchEnvironmentVariableName = "BITBUCKET_BRANCH";
10+
public const string TagEnvironmentVariableName = "BITBUCKET_TAG";
11+
public const string PullRequestEnvironmentVariableName = "BITBUCKET_PR_ID";
12+
private string? file;
13+
14+
public BitBucketPipelines(IEnvironment environment, ILog log) : base(environment, log) => WithPropertyFile("gitversion.properties");
15+
16+
protected override string EnvironmentVariable => EnvironmentVariableName;
17+
18+
public override string? GenerateSetVersionMessage(VersionVariables variables) => variables.FullSemVer;
19+
20+
public void WithPropertyFile(string propertiesFileName) => this.file = propertiesFileName;
21+
22+
public override string[] GenerateSetParameterMessage(string name, string value) => new[]
23+
{
24+
$"GITVERSION_{name.ToUpperInvariant()}={value}"
25+
};
26+
27+
public override void WriteIntegration(Action<string?> writer, VersionVariables variables, bool updateBuildNumber = true)
28+
{
29+
if (this.file is null)
30+
return;
31+
32+
base.WriteIntegration(writer, variables, updateBuildNumber);
33+
writer($"Outputting variables to '{this.file}' ... ");
34+
writer("To import the file into your build environment, add the following line to your build step:");
35+
writer($" - source {this.file}");
36+
writer("");
37+
writer("To reuse the file across build steps, add the file as a build artifact:");
38+
writer(" artifacts:");
39+
writer($" - {this.file}");
40+
41+
var exports = variables
42+
.Select(variable => $"export GITVERSION_{variable.Key.ToUpperInvariant()}={variable.Value}")
43+
.ToList();
44+
45+
File.WriteAllLines(this.file, exports);
46+
}
47+
48+
public override string? GetCurrentBranch(bool usingDynamicRepos)
49+
{
50+
var branchName = EvaluateEnvironmentVariable(BranchEnvironmentVariableName);
51+
if (branchName != null && branchName.StartsWith("refs/heads/"))
52+
{
53+
return branchName;
54+
}
55+
56+
return null;
57+
}
58+
59+
private string? EvaluateEnvironmentVariable(string variableName)
60+
{
61+
var branchName = Environment.GetEnvironmentVariable(variableName);
62+
Log.Info("Evaluating environment variable {0} : {1}", variableName, branchName!);
63+
return branchName;
64+
}
65+
}

0 commit comments

Comments
 (0)