Skip to content

Commit 8272cd4

Browse files
authored
Merge pull request #1284 from dotnet/copilot/enhance-git-engine-deactivation
Automatically disable git engine for Dependabot environments
2 parents 92595c2 + 129d681 commit 8272cd4

File tree

6 files changed

+186
-4
lines changed

6 files changed

+186
-4
lines changed

docfx/docs/shallow-cloning-agents.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,17 @@ See also [GitHub Copilot Coding Agent docs](https://docs.github.com/en/copilot/h
2727

2828
## Dependabot
2929

30+
**As of Nerdbank.GitVersioning v3.9, the git engine is automatically disabled when running under Dependabot**, eliminating the need for manual configuration in most cases.
31+
32+
Specifically, when the `DEPENDABOT` environment variable is set to `true` (case-insensitive) and the `NBGV_GitEngine` environment variable is **not** set, Nerdbank.GitVersioning automatically behaves as if `NBGV_GitEngine=Disabled`. This ensures that Dependabot runs succeed without any additional setup.
33+
34+
If you need to override this behavior for any reason, you can explicitly set the `NBGV_GitEngine` environment variable to your desired value, which will take precedence over the automatic Dependabot detection.
35+
36+
### Background
37+
3038
Dependabot does not yet allow configuring custom environment variables for its runtime environment.
3139
Consider up-voting [this issue](https://github.com/dependabot/dependabot-core/issues/4660).
3240
Be sure to vote up the top-level issue description as that tends to be the tally that maintainers pay attention to.
3341
But you may also upvote [this particular comment](https://github.com/dependabot/dependabot-core/issues/4660#issuecomment-3170935213) that describes our use case.
3442

35-
There is [a known workaround](https://github.com/dependabot/dependabot-core/issues/4660#issuecomment-3399907801) for the time being.
43+
There is [a known workaround](https://github.com/dependabot/dependabot-core/issues/4660#issuecomment-3399907801), but with the automatic detection feature, this workaround should no longer be necessary for most users.

src/NerdBank.GitVersioning/Commands/CloudCommand.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ public void SetBuildVariables(string projectDirectory, IEnumerable<string> metad
9797
activeCloudBuild = CloudBuild.SupportedCloudBuilds[matchingIndex];
9898
}
9999

100-
using var context = GitContext.Create(projectDirectory, engine: alwaysUseLibGit2 ? GitContext.Engine.ReadWrite : GitContext.Engine.ReadOnly);
100+
GitContext.Engine engine = GitContext.GetEffectiveGitEngine(alwaysUseLibGit2 ? GitContext.Engine.ReadWrite : GitContext.Engine.ReadOnly);
101+
using var context = GitContext.Create(projectDirectory, engine: engine);
101102
var oracle = new VersionOracle(context, cloudBuild: activeCloudBuild);
102103
if (metadata is not null)
103104
{

src/NerdBank.GitVersioning/GitContext.cs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,54 @@ public string RepoRelativeProjectDirectory
133133
/// </summary>
134134
protected string? DotGitPath { get; }
135135

136+
/// <summary>
137+
/// Gets the effective git engine to use, taking into account automatic disabling for Dependabot.
138+
/// This overload checks the NBGV_GitEngine environment variable and parses it automatically.
139+
/// </summary>
140+
/// <param name="defaultEngine">The engine to use if no environment variables dictate otherwise.</param>
141+
/// <returns>The engine to use.</returns>
142+
/// <remarks>
143+
/// If the NBGV_GitEngine environment variable is set, it takes precedence.
144+
/// Valid values are "LibGit2", "Managed", and "Disabled" (case-sensitive).
145+
/// Unrecognized values are treated as if the variable was not set, maintaining backward compatibility.
146+
/// Otherwise, if the DEPENDABOT environment variable is set to "true" (case-insensitive), returns <see cref="Engine.Disabled"/>.
147+
/// Otherwise, returns <paramref name="defaultEngine"/>.
148+
/// </remarks>
149+
public static Engine GetEffectiveGitEngine(Engine defaultEngine = Engine.ReadOnly)
150+
{
151+
// If NBGV_GitEngine is set, respect that setting regardless of Dependabot
152+
string? nbgvGitEngine = Environment.GetEnvironmentVariable("NBGV_GitEngine");
153+
if (!string.IsNullOrEmpty(nbgvGitEngine))
154+
{
155+
// Parse the NBGV_GitEngine value
156+
if (string.Equals(nbgvGitEngine, "LibGit2", StringComparison.Ordinal))
157+
{
158+
return Engine.ReadWrite;
159+
}
160+
else if (string.Equals(nbgvGitEngine, "Disabled", StringComparison.Ordinal))
161+
{
162+
return Engine.Disabled;
163+
}
164+
else if (string.Equals(nbgvGitEngine, "Managed", StringComparison.Ordinal))
165+
{
166+
return Engine.ReadOnly;
167+
}
168+
169+
// If unrecognized value, fall through to default logic.
170+
// This maintains backward compatibility where invalid environment variable values
171+
// are silently ignored rather than causing build failures.
172+
}
173+
174+
// If we're in a Dependabot environment and NBGV_GitEngine is not set, automatically disable the git engine
175+
if (IsDependabotEnvironment())
176+
{
177+
return Engine.Disabled;
178+
}
179+
180+
// Otherwise, use the default engine
181+
return defaultEngine;
182+
}
183+
136184
/// <summary>
137185
/// Creates a context for reading/writing version information at a given path and committish.
138186
/// </summary>
@@ -340,4 +388,14 @@ private static (string GitDirectory, string WorkingTreeDirectory)? FindGitDir(st
340388

341389
return null;
342390
}
391+
392+
/// <summary>
393+
/// Determines whether the current environment is running under Dependabot.
394+
/// </summary>
395+
/// <returns><see langword="true"/> if DEPENDABOT environment variable is set to "true" (case-insensitive); otherwise, <see langword="false"/>.</returns>
396+
private static bool IsDependabotEnvironment()
397+
{
398+
string? dependabotEnvVar = Environment.GetEnvironmentVariable("DEPENDABOT");
399+
return string.Equals(dependabotEnvVar, "true", StringComparison.OrdinalIgnoreCase);
400+
}
343401
}

src/Nerdbank.GitVersioning.Tasks/GetBuildVersion.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,9 +226,10 @@ protected override bool ExecuteInner()
226226
Requires.Argument(!containsDotDotSlash, nameof(this.ProjectPathRelativeToGitRepoRoot), "Path must not use ..\\");
227227
}
228228

229-
GitContext.Engine engine = GitContext.Engine.ReadOnly;
229+
GitContext.Engine engine;
230230
if (!string.IsNullOrWhiteSpace(this.GitEngine))
231231
{
232+
// MSBuild property GitEngine takes precedence over environment variables
232233
engine = this.GitEngine switch
233234
{
234235
"Managed" => GitContext.Engine.ReadOnly,
@@ -237,6 +238,11 @@ protected override bool ExecuteInner()
237238
_ => throw new ArgumentException("GitEngine property must be set to either \"Disabled\", \"Managed\" or \"LibGit2\" or left empty."),
238239
};
239240
}
241+
else
242+
{
243+
// Use environment variable logic (NBGV_GitEngine and DEPENDABOT)
244+
engine = GitContext.GetEffectiveGitEngine();
245+
}
240246

241247
ICloudBuild cloudBuild = CloudBuild.Active;
242248
this.Log.LogMessage(MessageImportance.Low, "Cloud build provider: {0}", cloudBuild?.GetType().Name ?? "None");

src/nbgv/Program.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,17 @@ private static void PrintException(Exception ex)
392392
}
393393
}
394394

395+
/// <summary>
396+
/// Gets the effective git engine to use based on environment variables and command settings.
397+
/// </summary>
398+
/// <param name="preferReadWrite">Whether to prefer ReadWrite (LibGit2) engine when not explicitly specified.</param>
399+
/// <returns>The engine to use.</returns>
400+
private static GitContext.Engine GetEffectiveGitEngine(bool preferReadWrite = false)
401+
{
402+
// Use the shared logic from GitContext which handles both NBGV_GitEngine and DEPENDABOT env vars
403+
return GitContext.GetEffectiveGitEngine(preferReadWrite ? GitContext.Engine.ReadWrite : GitContext.Engine.ReadOnly);
404+
}
405+
395406
private static int MainInner(string[] args)
396407
{
397408
try
@@ -588,7 +599,7 @@ private static Task<int> OnGetVersionCommand(string project, string[] metadata,
588599

589600
string searchPath = GetSpecifiedOrCurrentDirectoryPath(project);
590601

591-
using var context = GitContext.Create(searchPath, engine: AlwaysUseLibGit2 ? GitContext.Engine.ReadWrite : GitContext.Engine.ReadOnly);
602+
using var context = GitContext.Create(searchPath, engine: GetEffectiveGitEngine(preferReadWrite: AlwaysUseLibGit2));
592603
if (!context.IsRepository)
593604
{
594605
Console.Error.WriteLine("No git repo found at or above: \"{0}\"", searchPath);

test/Nerdbank.GitVersioning.Tests/GitContextTests.cs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,4 +189,102 @@ public void HeadCanonicalName_PackedHead()
189189
this.Context = this.CreateGitContext(Path.Combine(expandedRepo.RepoPath));
190190
Assert.Equal("refs/heads/main", this.Context.HeadCanonicalName);
191191
}
192+
193+
[Fact]
194+
public void GetEffectiveGitEngine_DefaultBehavior()
195+
{
196+
// Arrange: Clear both environment variables
197+
var originalDependabot = Environment.GetEnvironmentVariable("DEPENDABOT");
198+
var originalNbgvGitEngine = Environment.GetEnvironmentVariable("NBGV_GitEngine");
199+
try
200+
{
201+
Environment.SetEnvironmentVariable("DEPENDABOT", null);
202+
Environment.SetEnvironmentVariable("NBGV_GitEngine", null);
203+
204+
// Act & Assert: With no environment variables, should return default ReadOnly
205+
Assert.Equal(GitContext.Engine.ReadOnly, GitContext.GetEffectiveGitEngine());
206+
Assert.Equal(GitContext.Engine.ReadWrite, GitContext.GetEffectiveGitEngine(GitContext.Engine.ReadWrite));
207+
}
208+
finally
209+
{
210+
Environment.SetEnvironmentVariable("DEPENDABOT", originalDependabot);
211+
Environment.SetEnvironmentVariable("NBGV_GitEngine", originalNbgvGitEngine);
212+
}
213+
}
214+
215+
[Theory]
216+
[InlineData("true")]
217+
[InlineData("TRUE")]
218+
[InlineData("True")]
219+
public void GetEffectiveGitEngine_DependabotEnvironment_DisablesEngine(string dependabotValue)
220+
{
221+
// Arrange: Set DEPENDABOT=true and clear NBGV_GitEngine
222+
var originalDependabot = Environment.GetEnvironmentVariable("DEPENDABOT");
223+
var originalNbgvGitEngine = Environment.GetEnvironmentVariable("NBGV_GitEngine");
224+
try
225+
{
226+
Environment.SetEnvironmentVariable("DEPENDABOT", dependabotValue);
227+
Environment.SetEnvironmentVariable("NBGV_GitEngine", null);
228+
229+
// Act & Assert: Should return Disabled regardless of requested engine
230+
Assert.Equal(GitContext.Engine.Disabled, GitContext.GetEffectiveGitEngine());
231+
Assert.Equal(GitContext.Engine.Disabled, GitContext.GetEffectiveGitEngine(GitContext.Engine.ReadOnly));
232+
Assert.Equal(GitContext.Engine.Disabled, GitContext.GetEffectiveGitEngine(GitContext.Engine.ReadWrite));
233+
}
234+
finally
235+
{
236+
Environment.SetEnvironmentVariable("DEPENDABOT", originalDependabot);
237+
Environment.SetEnvironmentVariable("NBGV_GitEngine", originalNbgvGitEngine);
238+
}
239+
}
240+
241+
[Theory]
242+
[InlineData("false")]
243+
[InlineData("False")]
244+
[InlineData("0")]
245+
[InlineData("")]
246+
public void GetEffectiveGitEngine_DependabotNotTrue_UsesDefault(string dependabotValue)
247+
{
248+
// Arrange: Set DEPENDABOT to non-true value and clear NBGV_GitEngine
249+
var originalDependabot = Environment.GetEnvironmentVariable("DEPENDABOT");
250+
var originalNbgvGitEngine = Environment.GetEnvironmentVariable("NBGV_GitEngine");
251+
try
252+
{
253+
Environment.SetEnvironmentVariable("DEPENDABOT", dependabotValue);
254+
Environment.SetEnvironmentVariable("NBGV_GitEngine", null);
255+
256+
// Act & Assert: Should use default behavior
257+
Assert.Equal(GitContext.Engine.ReadOnly, GitContext.GetEffectiveGitEngine());
258+
Assert.Equal(GitContext.Engine.ReadWrite, GitContext.GetEffectiveGitEngine(GitContext.Engine.ReadWrite));
259+
}
260+
finally
261+
{
262+
Environment.SetEnvironmentVariable("DEPENDABOT", originalDependabot);
263+
Environment.SetEnvironmentVariable("NBGV_GitEngine", originalNbgvGitEngine);
264+
}
265+
}
266+
267+
[Theory]
268+
[InlineData("LibGit2", GitContext.Engine.ReadWrite)]
269+
[InlineData("Managed", GitContext.Engine.ReadOnly)]
270+
[InlineData("Disabled", GitContext.Engine.Disabled)]
271+
public void GetEffectiveGitEngine_NbgvGitEngineOverridesDependabot(string nbgvValue, GitContext.Engine expectedEngine)
272+
{
273+
// Arrange: Set both DEPENDABOT and NBGV_GitEngine
274+
var originalDependabot = Environment.GetEnvironmentVariable("DEPENDABOT");
275+
var originalNbgvGitEngine = Environment.GetEnvironmentVariable("NBGV_GitEngine");
276+
try
277+
{
278+
Environment.SetEnvironmentVariable("DEPENDABOT", "true");
279+
Environment.SetEnvironmentVariable("NBGV_GitEngine", nbgvValue);
280+
281+
// Act & Assert: NBGV_GitEngine should take precedence and be parsed correctly
282+
Assert.Equal(expectedEngine, GitContext.GetEffectiveGitEngine());
283+
}
284+
finally
285+
{
286+
Environment.SetEnvironmentVariable("DEPENDABOT", originalDependabot);
287+
Environment.SetEnvironmentVariable("NBGV_GitEngine", originalNbgvGitEngine);
288+
}
289+
}
192290
}

0 commit comments

Comments
 (0)