Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ For recipe compatible with Cake Script Runners see Cake.Issues.Recipe.</Descript
<PackageReference Include="Cake.Frosting.Issues.Reporting.Generic" Version="5.7.0" />
<PackageReference Include="Cake.Frosting.Issues.Reporting.Console" Version="5.7.0" />
<PackageReference Include="Cake.GitHub" Version="1.0.0" />
<PackageReference Include="GitReader" Version="1.4.0" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,11 @@ public enum RepositoryInfoProviderType
/// Read repository information using Git CLI.
/// Requires Git CLI to be available in path.
/// </summary>
Cli
Cli,

/// <summary>
/// Read repository information using GitReader library.
/// Pure .NET implementation without external dependencies.
/// </summary>
GitReader
}
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ private static IRepositoryInfoProvider DetermineRepositoryInfoProvider(
case RepositoryInfoProviderType.Cli:
context.Information("Using Git CLI for providing repository information");
return new CliRepositoryInfoProvider();
case RepositoryInfoProviderType.GitReader:
context.Information("Using GitReader for providing repository information");
return new GitReaderRepositoryInfoProvider();
default:
throw new NotImplementedException("Unsupported repository info provider");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
namespace Cake.Frosting.Issues.Recipe;

using Cake.Core;
using Cake.Core.IO;
using GitReader;
using GitReader.Structures;
using System.IO;

/// <summary>
/// Provider to retrieve repository information using <see href="https://github.com/kekyo/GitReader">GitReader library</see>.
/// </summary>
internal sealed class GitReaderRepositoryInfoProvider : IRepositoryInfoProvider
{
/// <inheritdoc />
public DirectoryPath GetRepositoryRootDirectory(ICakeContext context, DirectoryPath buildRootDirectory)
{
context.NotNull();
buildRootDirectory.NotNull();

// Search for .git directory starting from build root and going up
var currentDir = new DirectoryInfo(buildRootDirectory.FullPath);
while (currentDir != null)
{
var gitDir = System.IO.Path.Combine(currentDir.FullName, ".git");
if (Directory.Exists(gitDir) || File.Exists(gitDir))
{
return new DirectoryPath(currentDir.FullName);
}
currentDir = currentDir.Parent;
}

throw new DirectoryNotFoundException($"Could not find Git repository root starting from {buildRootDirectory.FullPath}");
}

/// <inheritdoc />
public Uri GetRepositoryRemoteUrl(ICakeContext context, DirectoryPath repositoryRootDirectory)
{
context.NotNull();
repositoryRootDirectory.NotNull();

try
{
// Read the origin remote URL from .git/config
var configPath = System.IO.Path.Combine(repositoryRootDirectory.FullPath, ".git", "config");
if (File.Exists(configPath))
{
var configLines = File.ReadAllLines(configPath);
bool inOriginRemote = false;

foreach (var line in configLines)
{
var trimmedLine = line.Trim();

if (trimmedLine == "[remote \"origin\"]")
{
inOriginRemote = true;
continue;
}

if (trimmedLine.StartsWith("[") && inOriginRemote)

Check warning on line 60 in Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/RepositoryInfo/GitReaderRepositoryInfoProvider.cs

View workflow job for this annotation

GitHub Actions / Build

The behavior of 'string.StartsWith(string)' could vary based on the current user's locale settings. Replace this call in 'Cake.Frosting.Issues.Recipe.GitReaderRepositoryInfoProvider.GetRepositoryRemoteUrl(Cake.Core.ICakeContext, Cake.Core.IO.DirectoryPath)' with a call to 'string.StartsWith(string, System.StringComparison)'. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1310)

Check warning on line 60 in Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/RepositoryInfo/GitReaderRepositoryInfoProvider.cs

View workflow job for this annotation

GitHub Actions / Build

Use 'string.StartsWith(char)' instead of 'string.StartsWith(string)' when you have a string with a single char (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1866)

Check warning on line 60 in Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/RepositoryInfo/GitReaderRepositoryInfoProvider.cs

View workflow job for this annotation

GitHub Actions / Build

The behavior of 'string.StartsWith(string)' could vary based on the current user's locale settings. Replace this call in 'Cake.Frosting.Issues.Recipe.GitReaderRepositoryInfoProvider.GetRepositoryRemoteUrl(Cake.Core.ICakeContext, Cake.Core.IO.DirectoryPath)' with a call to 'string.StartsWith(string, System.StringComparison)'. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1310)

Check warning on line 60 in Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/RepositoryInfo/GitReaderRepositoryInfoProvider.cs

View workflow job for this annotation

GitHub Actions / Build

Use 'string.StartsWith(char)' instead of 'string.StartsWith(string)' when you have a string with a single char (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1866)

Check warning on line 60 in Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/RepositoryInfo/GitReaderRepositoryInfoProvider.cs

View workflow job for this annotation

GitHub Actions / Build

The behavior of 'string.StartsWith(string)' could vary based on the current user's locale settings. Replace this call in 'Cake.Frosting.Issues.Recipe.GitReaderRepositoryInfoProvider.GetRepositoryRemoteUrl(Cake.Core.ICakeContext, Cake.Core.IO.DirectoryPath)' with a call to 'string.StartsWith(string, System.StringComparison)'. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1310)

Check warning on line 60 in Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/RepositoryInfo/GitReaderRepositoryInfoProvider.cs

View workflow job for this annotation

GitHub Actions / Build

Use 'string.StartsWith(char)' instead of 'string.StartsWith(string)' when you have a string with a single char (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1866)

Check warning on line 60 in Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/RepositoryInfo/GitReaderRepositoryInfoProvider.cs

View workflow job for this annotation

GitHub Actions / Build

The behavior of 'string.StartsWith(string)' could vary based on the current user's locale settings. Replace this call in 'Cake.Frosting.Issues.Recipe.GitReaderRepositoryInfoProvider.GetRepositoryRemoteUrl(Cake.Core.ICakeContext, Cake.Core.IO.DirectoryPath)' with a call to 'string.StartsWith(string, System.StringComparison)'. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1310)

Check warning on line 60 in Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/RepositoryInfo/GitReaderRepositoryInfoProvider.cs

View workflow job for this annotation

GitHub Actions / Build

Use 'string.StartsWith(char)' instead of 'string.StartsWith(string)' when you have a string with a single char (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1866)
{
// Exited the origin remote section
break;
}

if (inOriginRemote && trimmedLine.StartsWith("url = "))

Check warning on line 66 in Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/RepositoryInfo/GitReaderRepositoryInfoProvider.cs

View workflow job for this annotation

GitHub Actions / Build

The behavior of 'string.StartsWith(string)' could vary based on the current user's locale settings. Replace this call in 'Cake.Frosting.Issues.Recipe.GitReaderRepositoryInfoProvider.GetRepositoryRemoteUrl(Cake.Core.ICakeContext, Cake.Core.IO.DirectoryPath)' with a call to 'string.StartsWith(string, System.StringComparison)'. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1310)

Check warning on line 66 in Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/RepositoryInfo/GitReaderRepositoryInfoProvider.cs

View workflow job for this annotation

GitHub Actions / Build

The behavior of 'string.StartsWith(string)' could vary based on the current user's locale settings. Replace this call in 'Cake.Frosting.Issues.Recipe.GitReaderRepositoryInfoProvider.GetRepositoryRemoteUrl(Cake.Core.ICakeContext, Cake.Core.IO.DirectoryPath)' with a call to 'string.StartsWith(string, System.StringComparison)'. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1310)

Check warning on line 66 in Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/RepositoryInfo/GitReaderRepositoryInfoProvider.cs

View workflow job for this annotation

GitHub Actions / Build

The behavior of 'string.StartsWith(string)' could vary based on the current user's locale settings. Replace this call in 'Cake.Frosting.Issues.Recipe.GitReaderRepositoryInfoProvider.GetRepositoryRemoteUrl(Cake.Core.ICakeContext, Cake.Core.IO.DirectoryPath)' with a call to 'string.StartsWith(string, System.StringComparison)'. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1310)
{
var url = trimmedLine.Substring(6); // Remove "url = "
return new Uri(url);
}
}
}

throw new InvalidOperationException("Could not find origin remote URL in repository configuration");
}
catch (Exception ex)
{
throw new InvalidOperationException($"Failed to read repository remote URL from {repositoryRootDirectory.FullPath}", ex);
}
}

/// <inheritdoc />
public string GetCommitId(ICakeContext context, DirectoryPath repositoryRootDirectory)
{
context.NotNull();
repositoryRootDirectory.NotNull();

try
{
// Use async method with GetAwaiter().GetResult() pattern as used in other parts of Cake
using var repository = Repository.Factory.OpenStructureAsync(repositoryRootDirectory.FullPath).GetAwaiter().GetResult();

if (repository.Head is Branch head)
{
var commit = head.GetHeadCommitAsync().GetAwaiter().GetResult();
return commit.Hash.ToString();
}

throw new InvalidOperationException("Could not find HEAD commit in repository");
}
catch (Exception ex)
{
throw new InvalidOperationException($"Failed to read commit ID from {repositoryRootDirectory.FullPath}", ex);
}
}
}
1 change: 1 addition & 0 deletions Cake.Issues.Recipe/Content/addins.cake
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@
#addin nuget:?package=Cake.AzureDevOps&version=5.0.2
#addin nuget:?package=Cake.GitHub&version=1.0.0
#addin nuget:?package=Octokit&version=13.0.1
#addin nuget:?package=GitReader&version=1.4.0
3 changes: 3 additions & 0 deletions Cake.Issues.Recipe/Content/data/IssuesData.cake
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,9 @@ public class IssuesData
case RepositoryInfoProviderType.Cli:
context.Information("Using Git CLI for providing repository information");
return new CliRepositoryInfoProvider();
case RepositoryInfoProviderType.GitReader:
context.Information("Using GitReader for providing repository information");
return new GitReaderRepositoryInfoProvider();
default:
throw new NotImplementedException("Unsupported repository info provider");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,10 @@ public enum RepositoryInfoProviderType
/// Read repository information using Git CLI.
/// Requires Git CLI to be available in path.
/// </summary>
Cli
Cli,
/// <summary>
/// Read repository information using GitReader library.
/// Pure .NET implementation without external dependencies.
/// </summary>
GitReader
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/// <summary>
/// Provider to retrieve repository information using <see href="https://github.com/kekyo/GitReader">GitReader library</see>.
/// </summary>
public class GitReaderRepositoryInfoProvider : IRepositoryInfoProvider
{
/// <inheritdoc />
public DirectoryPath GetRepositoryRootDirectory(ICakeContext context, DirectoryPath buildRootDirectory)
{
context.NotNull();
buildRootDirectory.NotNull();

// Search for .git directory starting from build root and going up
var currentDir = new DirectoryInfo(buildRootDirectory.FullPath);
while (currentDir != null)
{
var gitDir = System.IO.Path.Combine(currentDir.FullName, ".git");
if (Directory.Exists(gitDir) || File.Exists(gitDir))
{
return new DirectoryPath(currentDir.FullName);
}
currentDir = currentDir.Parent;
}

throw new DirectoryNotFoundException($"Could not find Git repository root starting from {buildRootDirectory.FullPath}");
}

/// <inheritdoc />
public Uri GetRepositoryRemoteUrl(ICakeContext context, DirectoryPath repositoryRootDirectory)
{
context.NotNull();
repositoryRootDirectory.NotNull();

try
{
// Read the origin remote URL from .git/config
var configPath = System.IO.Path.Combine(repositoryRootDirectory.FullPath, ".git", "config");
if (File.Exists(configPath))
{
var configLines = File.ReadAllLines(configPath);
bool inOriginRemote = false;

foreach (var line in configLines)
{
var trimmedLine = line.Trim();

if (trimmedLine == "[remote \"origin\"]")
{
inOriginRemote = true;
continue;
}

if (trimmedLine.StartsWith("[") && inOriginRemote)
{
// Exited the origin remote section
break;
}

if (inOriginRemote && trimmedLine.StartsWith("url = "))
{
var url = trimmedLine.Substring(6); // Remove "url = "
return new Uri(url);
}
}
}

throw new InvalidOperationException("Could not find origin remote URL in repository configuration");
}
catch (Exception ex)
{
throw new InvalidOperationException($"Failed to read repository remote URL from {repositoryRootDirectory.FullPath}", ex);
}
}

/// <inheritdoc />
public string GetCommitId(ICakeContext context, DirectoryPath repositoryRootDirectory)
{
context.NotNull();
repositoryRootDirectory.NotNull();

try
{
// Use async method with GetAwaiter().GetResult() pattern as used in other parts of Cake
using var repository = GitReader.Repository.Factory.OpenStructureAsync(repositoryRootDirectory.FullPath).GetAwaiter().GetResult();

if (repository.Head is GitReader.Structures.Branch head)
{
var commit = head.GetHeadCommitAsync().GetAwaiter().GetResult();
return commit.Hash.ToString();
}

throw new InvalidOperationException("Could not find HEAD commit in repository");
}
catch (Exception ex)
{
throw new InvalidOperationException($"Failed to read commit ID from {repositoryRootDirectory.FullPath}", ex);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#load IRepositoryInfoProvider.cake
#load CakeGitRepositoryInfoProvider.cake
#load CliRepositoryInfoProvider.cake
#load CliRepositoryInfoProvider.cake
#load GitReaderRepositoryInfoProvider.cake
Loading