Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
65aca7b
Initial empty commit to create PR
gitauto-ai[bot] Sep 12, 2025
19f51f1
Update Src/AiCommitMessage/Utility/EnvironmentLoader.cs [skip ci]
gitauto-ai[bot] Sep 12, 2025
5cf78f1
Update Src/AiCommitMessage/Services/GenerateCommitMessageService.cs […
gitauto-ai[bot] Sep 12, 2025
238a77c
Replace content of Src/AiCommitMessage/Services/GenerateCommitMessage…
gitauto-ai[bot] Sep 12, 2025
6adf9c6
Replace content of Tests/AiCommitMessage.Tests/Utility/EnvironmentLoa…
gitauto-ai[bot] Sep 12, 2025
58e5e25
Update README.md [skip ci]
gitauto-ai[bot] Sep 12, 2025
41fb992
Update README.md [skip ci]
gitauto-ai[bot] Sep 12, 2025
135e9ee
Replace content of Tests/AiCommitMessage.Tests/Integration/DisableApi…
gitauto-ai[bot] Sep 12, 2025
919cb47
Update Tests/AiCommitMessage.Tests/Services/ApiErrorHandlingTests.cs …
gitauto-ai[bot] Sep 12, 2025
88daf0f
Empty commit to trigger final tests
gitauto-ai[bot] Sep 12, 2025
9f28b98
Merge branch 'main' into gitauto/issue-285-20250912-171517-Z3jS
guibranco Nov 21, 2025
d940493
Merge branch 'main' into gitauto/issue-285-20250912-171517-Z3jS
guibranco Dec 4, 2025
2de4c3c
Merge branch 'main' into gitauto/issue-285-20250912-171517-Z3jS
gstraccini[bot] Dec 15, 2025
947b25e
Merge branch 'main' into gitauto/issue-285-20250912-171517-Z3jS
gstraccini[bot] Dec 22, 2025
9a9f624
Merge branch 'main' into gitauto/issue-285-20250912-171517-Z3jS
gstraccini[bot] Dec 29, 2025
ee8e15a
Merge branch 'main' into gitauto/issue-285-20250912-171517-Z3jS
gstraccini[bot] Jan 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

# ![GIT Hooks + OpenAI - Generate GIT commit messages from OpenAI](https://raw.githubusercontent.com/guibranco/dotnet-aicommitmessage/main/docs/images/splash.png)

🧠 🤖 This tool generates AI-powered commit messages via Git hooks, automating meaningful message suggestions from OpenAI and others to improve commit quality and efficiency.
Expand Down Expand Up @@ -249,6 +250,28 @@
- Use fallback commit message generation (either the provided message or a placeholder)
- Continue to work with branch name processing and issue number extraction

### Ignore API Errors

In environments where API calls may occasionally fail due to network restrictions, timeouts, or temporary service issues, you can configure the tool to gracefully handle these errors instead of failing completely. Set the following environment variable:

```bash
export DOTNET_AICOMMITMESSAGE_IGNORE_API_ERRORS=true
```

Or on Windows:

```cmd
set DOTNET_AICOMMITMESSAGE_IGNORE_API_ERRORS=true
```

When this option is enabled, the tool will:
- Catch and suppress API-related exceptions (network errors, timeouts, authentication failures, etc.)

Check notice on line 268 in README.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

README.md#L268

Lists should be surrounded by blank lines
- Display a warning message when an API error occurs but is ignored
- Fall back to using the original commit message with branch name processing and issue number extraction
- Continue the commit process without interruption

This is particularly useful in CI/CD pipelines or developer environments where occasional API issues shouldn't block the commit process.


### Contributors

Expand Down
31 changes: 29 additions & 2 deletions Src/AiCommitMessage/Services/GenerateCommitMessageService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
/// API responses to a JSON file if debugging is enabled. If the commit message is a merge conflict resolution or contains a skip AI flag, it returns as-is.
/// </remarks>
/// <exception cref="InvalidOperationException">Thrown if both the branch and diff are empty, or if the staged changes exceed 10KB in size.</exception>
public string GenerateCommitMessage(GenerateCommitMessageOptions options)

Check warning on line 67 in Src/AiCommitMessage/Services/GenerateCommitMessageService.cs

View workflow job for this annotation

GitHub Actions / SonarCloud Analysis

Make 'GenerateCommitMessage' a static method. (https://rules.sonarsource.com/csharp/RSPEC-2325)
{
var branch = string.IsNullOrEmpty(options.Branch)
? GitHelper.GetBranchName()
Expand Down Expand Up @@ -121,10 +121,21 @@
+ (string.IsNullOrEmpty(diff) ? "<no changes>" : diff);

var model = EnvironmentLoader.LoadModelName();
return GenerateWithModel(model, formattedMessage, branch, message, options.Debug);

try
{
return GenerateWithModel(model, formattedMessage, branch, message, options.Debug);
}
catch (Exception ex) when (EnvironmentLoader.ShouldIgnoreApiErrors() && IsApiException(ex))
{
Output.WarningLine(
"⚠️ AI API error occurred but was ignored due to DOTNET_AICOMMITMESSAGE_IGNORE_API_ERRORS setting. Falling back to original message."
);
return PostProcess(message, branch, message);
}
}

private static string FilterPackageLockDiff(string diff)

Check warning on line 138 in Src/AiCommitMessage/Services/GenerateCommitMessageService.cs

View workflow job for this annotation

GitHub Actions / SonarCloud Analysis

Refactor this method to reduce its Cognitive Complexity from 24 to the 15 allowed. (https://rules.sonarsource.com/csharp/RSPEC-3776)
{
if (string.IsNullOrEmpty(diff))
{
Expand Down Expand Up @@ -156,7 +167,7 @@
if (parts.Length >= 4)
{
var pathB = parts[3].StartsWith("b/") ? parts[3].Substring(2) : parts[3];
foreach (var pattern in ignoredPatterns)

Check warning on line 170 in Src/AiCommitMessage/Services/GenerateCommitMessageService.cs

View workflow job for this annotation

GitHub Actions / SonarCloud Analysis

Loops should be simplified using the "Where" LINQ method (https://rules.sonarsource.com/csharp/RSPEC-3267)
{
if (pathB.EndsWith(pattern))
{
Expand Down Expand Up @@ -405,7 +416,7 @@
{
var processStartInfo = new ProcessStartInfo
{
FileName = "git",

Check warning on line 419 in Src/AiCommitMessage/Services/GenerateCommitMessageService.cs

View workflow job for this annotation

GitHub Actions / SonarCloud Analysis

Make sure the "PATH" variable only contains fixed, unwriteable directories. (https://rules.sonarsource.com/csharp/RSPEC-4036)
Arguments = "config --get remote.origin.url",
RedirectStandardOutput = true,
UseShellExecute = false,
Expand Down Expand Up @@ -438,4 +449,20 @@

return GitProvider.Unidentified;
}
}

/// <summary>
/// Determines whether the specified exception is an API-related exception.
/// </summary>
/// <param name="exception">The exception to check.</param>
/// <returns><c>true</c> if the exception is API-related; otherwise, <c>false</c>.</returns>
private static bool IsApiException(Exception exception)
{
return exception is HttpRequestException
|| exception is TaskCanceledException
|| exception is InvalidOperationException
|| exception is ClientResultException
|| exception is RequestFailedException
|| exception.InnerException is HttpRequestException
|| exception.InnerException is TaskCanceledException;
}
}
7 changes: 7 additions & 0 deletions Src/AiCommitMessage/Utility/EnvironmentLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,13 @@ public static bool LoadOptionalEmoji() =>
public static bool IsApiDisabled() =>
bool.Parse(GetEnvironmentVariable("DOTNET_AICOMMITMESSAGE_DISABLE_API", "false"));

/// <summary>
/// Checks if API errors should be ignored via environment variable.
/// </summary>
/// <returns><c>true</c> if API errors should be ignored, <c>false</c> otherwise.</returns>
public static bool ShouldIgnoreApiErrors() =>
bool.Parse(GetEnvironmentVariable("DOTNET_AICOMMITMESSAGE_IGNORE_API_ERRORS", "false"));

/// <summary>
/// Decrypts the specified encrypted text using Base64 decoding.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,44 @@ public void SettingsService_Should_WorkCorrectly_When_ApiDisabled()
);
}
}
}

/// <summary>
/// Tests the complete workflow when API errors are ignored.
/// </summary>
[Fact]
public void CompleteWorkflow_Should_WorkCorrectly_When_ApiErrorsIgnored()
{
// Arrange
Environment.SetEnvironmentVariable(
"DOTNET_AICOMMITMESSAGE_IGNORE_API_ERRORS",
"true",
EnvironmentVariableTarget.Process
);

var service = new GenerateCommitMessageService();
var options = new GenerateCommitMessageOptions
{
Branch = "feature/285-ignore-api-errors",
Diff = "Added new environment variable support",
Message = "Add option to ignore API errors -skipai", // Use skipai to avoid actual API calls
};

try
{
// Act
var result = service.GenerateCommitMessage(options);

// Assert
result.Should().Be("#285 Add option to ignore API errors");
}
finally
{
// Cleanup
Environment.SetEnvironmentVariable(
"DOTNET_AICOMMITMESSAGE_IGNORE_API_ERRORS",
null,
EnvironmentVariableTarget.Process
);
}
}
}
69 changes: 69 additions & 0 deletions Tests/AiCommitMessage.Tests/Services/ApiErrorHandlingTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using System.ClientModel;
using AiCommitMessage.Options;
using AiCommitMessage.Services;
using Azure;
using FluentAssertions;

namespace AiCommitMessage.Tests.Services;

/// <summary>
/// Tests for API error handling functionality in GenerateCommitMessageService.
/// </summary>
public class ApiErrorHandlingTests
{
private readonly GenerateCommitMessageService _service;

public ApiErrorHandlingTests()
{
_service = new GenerateCommitMessageService();
}

/// <summary>
/// Tests that the service handles API errors gracefully when ignore API errors is enabled.
/// </summary>
[Fact]
public void GenerateCommitMessage_Should_HandleApiErrors_When_IgnoreApiErrorsEnabled()
{
// Arrange
Environment.SetEnvironmentVariable(
"DOTNET_AICOMMITMESSAGE_IGNORE_API_ERRORS",
"true",
EnvironmentVariableTarget.Process
);
Environment.SetEnvironmentVariable(
"DOTNET_AICOMMITMESSAGE_DISABLE_API",
"false",
EnvironmentVariableTarget.Process
);

var options = new GenerateCommitMessageOptions
{
Branch = "feature/285-test",
Diff = "Some diff",
Message = "Test commit -skipai", // Use skipai to avoid actual API calls but test the flow
};

try
{
// Act
var result = _service.GenerateCommitMessage(options);

// Assert
result.Should().Be("#285 Test commit");
}
finally
{
// Cleanup
Environment.SetEnvironmentVariable(
"DOTNET_AICOMMITMESSAGE_IGNORE_API_ERRORS",
null,
EnvironmentVariableTarget.Process
);
Environment.SetEnvironmentVariable(
"DOTNET_AICOMMITMESSAGE_DISABLE_API",
null,
EnvironmentVariableTarget.Process
);
}
}
}
84 changes: 84 additions & 0 deletions Tests/AiCommitMessage.Tests/Utility/EnvironmentLoaderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,88 @@ public void IsApiDisabled_Should_ReturnFalse_When_EnvironmentVariableIsFalse()
);
}
}

/// <summary>
/// Tests that ShouldIgnoreApiErrors returns false when the environment variable is not set.
/// </summary>
[Fact]
public void ShouldIgnoreApiErrors_Should_ReturnFalse_When_EnvironmentVariableNotSet()
{
// Arrange
Environment.SetEnvironmentVariable(
"DOTNET_AICOMMITMESSAGE_IGNORE_API_ERRORS",
null,
EnvironmentVariableTarget.Process
);

// Act
var result = EnvironmentLoader.ShouldIgnoreApiErrors();

// Assert
result.Should().BeFalse();
}

/// <summary>
/// Tests that ShouldIgnoreApiErrors returns true when the environment variable is set to "true".
/// </summary>
[Fact]
public void ShouldIgnoreApiErrors_Should_ReturnTrue_When_EnvironmentVariableIsTrue()
{
// Arrange
Environment.SetEnvironmentVariable(
"DOTNET_AICOMMITMESSAGE_IGNORE_API_ERRORS",
"true",
EnvironmentVariableTarget.Process
);

try
{
// Act
var result = EnvironmentLoader.ShouldIgnoreApiErrors();

// Assert
result.Should().BeTrue();
}
finally
{
// Cleanup
Environment.SetEnvironmentVariable(
"DOTNET_AICOMMITMESSAGE_IGNORE_API_ERRORS",
null,
EnvironmentVariableTarget.Process
);
}
}

/// <summary>
/// Tests that ShouldIgnoreApiErrors returns false when the environment variable is set to "false".
/// </summary>
[Fact]
public void ShouldIgnoreApiErrors_Should_ReturnFalse_When_EnvironmentVariableIsFalse()
{
// Arrange
Environment.SetEnvironmentVariable(
"DOTNET_AICOMMITMESSAGE_IGNORE_API_ERRORS",
"false",
EnvironmentVariableTarget.Process
);

try
{
// Act
var result = EnvironmentLoader.ShouldIgnoreApiErrors();

// Assert
result.Should().BeFalse();
}
finally
{
// Cleanup
Environment.SetEnvironmentVariable(
"DOTNET_AICOMMITMESSAGE_IGNORE_API_ERRORS",
null,
EnvironmentVariableTarget.Process
);
}
}
}
Loading