diff --git a/docfx/docs/shallow-cloning-agents.md b/docfx/docs/shallow-cloning-agents.md index 6a818e846..54e0631c7 100644 --- a/docfx/docs/shallow-cloning-agents.md +++ b/docfx/docs/shallow-cloning-agents.md @@ -27,9 +27,17 @@ See also [GitHub Copilot Coding Agent docs](https://docs.github.com/en/copilot/h ## Dependabot +**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. + +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. + +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. + +### Background + Dependabot does not yet allow configuring custom environment variables for its runtime environment. Consider up-voting [this issue](https://github.com/dependabot/dependabot-core/issues/4660). Be sure to vote up the top-level issue description as that tends to be the tally that maintainers pay attention to. But you may also upvote [this particular comment](https://github.com/dependabot/dependabot-core/issues/4660#issuecomment-3170935213) that describes our use case. -There is [a known workaround](https://github.com/dependabot/dependabot-core/issues/4660#issuecomment-3399907801) for the time being. +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. diff --git a/src/NerdBank.GitVersioning/Commands/CloudCommand.cs b/src/NerdBank.GitVersioning/Commands/CloudCommand.cs index b742fac73..6993b3504 100644 --- a/src/NerdBank.GitVersioning/Commands/CloudCommand.cs +++ b/src/NerdBank.GitVersioning/Commands/CloudCommand.cs @@ -97,7 +97,8 @@ public void SetBuildVariables(string projectDirectory, IEnumerable metad activeCloudBuild = CloudBuild.SupportedCloudBuilds[matchingIndex]; } - using var context = GitContext.Create(projectDirectory, engine: alwaysUseLibGit2 ? GitContext.Engine.ReadWrite : GitContext.Engine.ReadOnly); + GitContext.Engine engine = GitContext.GetEffectiveGitEngine(alwaysUseLibGit2 ? GitContext.Engine.ReadWrite : GitContext.Engine.ReadOnly); + using var context = GitContext.Create(projectDirectory, engine: engine); var oracle = new VersionOracle(context, cloudBuild: activeCloudBuild); if (metadata is not null) { diff --git a/src/NerdBank.GitVersioning/GitContext.cs b/src/NerdBank.GitVersioning/GitContext.cs index 4db82865b..5c1970485 100644 --- a/src/NerdBank.GitVersioning/GitContext.cs +++ b/src/NerdBank.GitVersioning/GitContext.cs @@ -133,6 +133,54 @@ public string RepoRelativeProjectDirectory /// protected string? DotGitPath { get; } + /// + /// Gets the effective git engine to use, taking into account automatic disabling for Dependabot. + /// This overload checks the NBGV_GitEngine environment variable and parses it automatically. + /// + /// The engine to use if no environment variables dictate otherwise. + /// The engine to use. + /// + /// If the NBGV_GitEngine environment variable is set, it takes precedence. + /// Valid values are "LibGit2", "Managed", and "Disabled" (case-sensitive). + /// Unrecognized values are treated as if the variable was not set, maintaining backward compatibility. + /// Otherwise, if the DEPENDABOT environment variable is set to "true" (case-insensitive), returns . + /// Otherwise, returns . + /// + public static Engine GetEffectiveGitEngine(Engine defaultEngine = Engine.ReadOnly) + { + // If NBGV_GitEngine is set, respect that setting regardless of Dependabot + string? nbgvGitEngine = Environment.GetEnvironmentVariable("NBGV_GitEngine"); + if (!string.IsNullOrEmpty(nbgvGitEngine)) + { + // Parse the NBGV_GitEngine value + if (string.Equals(nbgvGitEngine, "LibGit2", StringComparison.Ordinal)) + { + return Engine.ReadWrite; + } + else if (string.Equals(nbgvGitEngine, "Disabled", StringComparison.Ordinal)) + { + return Engine.Disabled; + } + else if (string.Equals(nbgvGitEngine, "Managed", StringComparison.Ordinal)) + { + return Engine.ReadOnly; + } + + // If unrecognized value, fall through to default logic. + // This maintains backward compatibility where invalid environment variable values + // are silently ignored rather than causing build failures. + } + + // If we're in a Dependabot environment and NBGV_GitEngine is not set, automatically disable the git engine + if (IsDependabotEnvironment()) + { + return Engine.Disabled; + } + + // Otherwise, use the default engine + return defaultEngine; + } + /// /// Creates a context for reading/writing version information at a given path and committish. /// @@ -340,4 +388,14 @@ private static (string GitDirectory, string WorkingTreeDirectory)? FindGitDir(st return null; } + + /// + /// Determines whether the current environment is running under Dependabot. + /// + /// if DEPENDABOT environment variable is set to "true" (case-insensitive); otherwise, . + private static bool IsDependabotEnvironment() + { + string? dependabotEnvVar = Environment.GetEnvironmentVariable("DEPENDABOT"); + return string.Equals(dependabotEnvVar, "true", StringComparison.OrdinalIgnoreCase); + } } diff --git a/src/Nerdbank.GitVersioning.Tasks/GetBuildVersion.cs b/src/Nerdbank.GitVersioning.Tasks/GetBuildVersion.cs index 703591fa5..f0cd3cf31 100644 --- a/src/Nerdbank.GitVersioning.Tasks/GetBuildVersion.cs +++ b/src/Nerdbank.GitVersioning.Tasks/GetBuildVersion.cs @@ -226,9 +226,10 @@ protected override bool ExecuteInner() Requires.Argument(!containsDotDotSlash, nameof(this.ProjectPathRelativeToGitRepoRoot), "Path must not use ..\\"); } - GitContext.Engine engine = GitContext.Engine.ReadOnly; + GitContext.Engine engine; if (!string.IsNullOrWhiteSpace(this.GitEngine)) { + // MSBuild property GitEngine takes precedence over environment variables engine = this.GitEngine switch { "Managed" => GitContext.Engine.ReadOnly, @@ -237,6 +238,11 @@ protected override bool ExecuteInner() _ => throw new ArgumentException("GitEngine property must be set to either \"Disabled\", \"Managed\" or \"LibGit2\" or left empty."), }; } + else + { + // Use environment variable logic (NBGV_GitEngine and DEPENDABOT) + engine = GitContext.GetEffectiveGitEngine(); + } ICloudBuild cloudBuild = CloudBuild.Active; this.Log.LogMessage(MessageImportance.Low, "Cloud build provider: {0}", cloudBuild?.GetType().Name ?? "None"); diff --git a/src/nbgv/Program.cs b/src/nbgv/Program.cs index 1115a3a38..c7fb98c36 100644 --- a/src/nbgv/Program.cs +++ b/src/nbgv/Program.cs @@ -392,6 +392,17 @@ private static void PrintException(Exception ex) } } + /// + /// Gets the effective git engine to use based on environment variables and command settings. + /// + /// Whether to prefer ReadWrite (LibGit2) engine when not explicitly specified. + /// The engine to use. + private static GitContext.Engine GetEffectiveGitEngine(bool preferReadWrite = false) + { + // Use the shared logic from GitContext which handles both NBGV_GitEngine and DEPENDABOT env vars + return GitContext.GetEffectiveGitEngine(preferReadWrite ? GitContext.Engine.ReadWrite : GitContext.Engine.ReadOnly); + } + private static int MainInner(string[] args) { try @@ -588,7 +599,7 @@ private static Task OnGetVersionCommand(string project, string[] metadata, string searchPath = GetSpecifiedOrCurrentDirectoryPath(project); - using var context = GitContext.Create(searchPath, engine: AlwaysUseLibGit2 ? GitContext.Engine.ReadWrite : GitContext.Engine.ReadOnly); + using var context = GitContext.Create(searchPath, engine: GetEffectiveGitEngine(preferReadWrite: AlwaysUseLibGit2)); if (!context.IsRepository) { Console.Error.WriteLine("No git repo found at or above: \"{0}\"", searchPath); diff --git a/test/Nerdbank.GitVersioning.Tests/GitContextTests.cs b/test/Nerdbank.GitVersioning.Tests/GitContextTests.cs index 681b9e62e..9ba37e6a3 100644 --- a/test/Nerdbank.GitVersioning.Tests/GitContextTests.cs +++ b/test/Nerdbank.GitVersioning.Tests/GitContextTests.cs @@ -189,4 +189,102 @@ public void HeadCanonicalName_PackedHead() this.Context = this.CreateGitContext(Path.Combine(expandedRepo.RepoPath)); Assert.Equal("refs/heads/main", this.Context.HeadCanonicalName); } + + [Fact] + public void GetEffectiveGitEngine_DefaultBehavior() + { + // Arrange: Clear both environment variables + var originalDependabot = Environment.GetEnvironmentVariable("DEPENDABOT"); + var originalNbgvGitEngine = Environment.GetEnvironmentVariable("NBGV_GitEngine"); + try + { + Environment.SetEnvironmentVariable("DEPENDABOT", null); + Environment.SetEnvironmentVariable("NBGV_GitEngine", null); + + // Act & Assert: With no environment variables, should return default ReadOnly + Assert.Equal(GitContext.Engine.ReadOnly, GitContext.GetEffectiveGitEngine()); + Assert.Equal(GitContext.Engine.ReadWrite, GitContext.GetEffectiveGitEngine(GitContext.Engine.ReadWrite)); + } + finally + { + Environment.SetEnvironmentVariable("DEPENDABOT", originalDependabot); + Environment.SetEnvironmentVariable("NBGV_GitEngine", originalNbgvGitEngine); + } + } + + [Theory] + [InlineData("true")] + [InlineData("TRUE")] + [InlineData("True")] + public void GetEffectiveGitEngine_DependabotEnvironment_DisablesEngine(string dependabotValue) + { + // Arrange: Set DEPENDABOT=true and clear NBGV_GitEngine + var originalDependabot = Environment.GetEnvironmentVariable("DEPENDABOT"); + var originalNbgvGitEngine = Environment.GetEnvironmentVariable("NBGV_GitEngine"); + try + { + Environment.SetEnvironmentVariable("DEPENDABOT", dependabotValue); + Environment.SetEnvironmentVariable("NBGV_GitEngine", null); + + // Act & Assert: Should return Disabled regardless of requested engine + Assert.Equal(GitContext.Engine.Disabled, GitContext.GetEffectiveGitEngine()); + Assert.Equal(GitContext.Engine.Disabled, GitContext.GetEffectiveGitEngine(GitContext.Engine.ReadOnly)); + Assert.Equal(GitContext.Engine.Disabled, GitContext.GetEffectiveGitEngine(GitContext.Engine.ReadWrite)); + } + finally + { + Environment.SetEnvironmentVariable("DEPENDABOT", originalDependabot); + Environment.SetEnvironmentVariable("NBGV_GitEngine", originalNbgvGitEngine); + } + } + + [Theory] + [InlineData("false")] + [InlineData("False")] + [InlineData("0")] + [InlineData("")] + public void GetEffectiveGitEngine_DependabotNotTrue_UsesDefault(string dependabotValue) + { + // Arrange: Set DEPENDABOT to non-true value and clear NBGV_GitEngine + var originalDependabot = Environment.GetEnvironmentVariable("DEPENDABOT"); + var originalNbgvGitEngine = Environment.GetEnvironmentVariable("NBGV_GitEngine"); + try + { + Environment.SetEnvironmentVariable("DEPENDABOT", dependabotValue); + Environment.SetEnvironmentVariable("NBGV_GitEngine", null); + + // Act & Assert: Should use default behavior + Assert.Equal(GitContext.Engine.ReadOnly, GitContext.GetEffectiveGitEngine()); + Assert.Equal(GitContext.Engine.ReadWrite, GitContext.GetEffectiveGitEngine(GitContext.Engine.ReadWrite)); + } + finally + { + Environment.SetEnvironmentVariable("DEPENDABOT", originalDependabot); + Environment.SetEnvironmentVariable("NBGV_GitEngine", originalNbgvGitEngine); + } + } + + [Theory] + [InlineData("LibGit2", GitContext.Engine.ReadWrite)] + [InlineData("Managed", GitContext.Engine.ReadOnly)] + [InlineData("Disabled", GitContext.Engine.Disabled)] + public void GetEffectiveGitEngine_NbgvGitEngineOverridesDependabot(string nbgvValue, GitContext.Engine expectedEngine) + { + // Arrange: Set both DEPENDABOT and NBGV_GitEngine + var originalDependabot = Environment.GetEnvironmentVariable("DEPENDABOT"); + var originalNbgvGitEngine = Environment.GetEnvironmentVariable("NBGV_GitEngine"); + try + { + Environment.SetEnvironmentVariable("DEPENDABOT", "true"); + Environment.SetEnvironmentVariable("NBGV_GitEngine", nbgvValue); + + // Act & Assert: NBGV_GitEngine should take precedence and be parsed correctly + Assert.Equal(expectedEngine, GitContext.GetEffectiveGitEngine()); + } + finally + { + Environment.SetEnvironmentVariable("DEPENDABOT", originalDependabot); + Environment.SetEnvironmentVariable("NBGV_GitEngine", originalNbgvGitEngine); + } + } }