diff --git a/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe.csproj b/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe.csproj
index 3b498e1e..274777d8 100644
--- a/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe.csproj
+++ b/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe.csproj
@@ -58,6 +58,7 @@ For recipe compatible with Cake Script Runners see Cake.Issues.Recipe.
+
diff --git a/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/Context/RepositoryInfoProviderType.cs b/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/Context/RepositoryInfoProviderType.cs
index 12947cdc..a7f12d59 100644
--- a/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/Context/RepositoryInfoProviderType.cs
+++ b/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/Context/RepositoryInfoProviderType.cs
@@ -15,5 +15,11 @@ public enum RepositoryInfoProviderType
/// Read repository information using Git CLI.
/// Requires Git CLI to be available in path.
///
- Cli
+ Cli,
+
+ ///
+ /// Read repository information using GitReader library.
+ /// Pure .NET implementation without external dependencies.
+ ///
+ GitReader
}
\ No newline at end of file
diff --git a/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/Context/State/IssuesState.cs b/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/Context/State/IssuesState.cs
index 1ada4bc4..bb97005f 100644
--- a/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/Context/State/IssuesState.cs
+++ b/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/Context/State/IssuesState.cs
@@ -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");
}
diff --git a/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/RepositoryInfo/GitReaderRepositoryInfoProvider.cs b/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/RepositoryInfo/GitReaderRepositoryInfoProvider.cs
new file mode 100644
index 00000000..7968fa25
--- /dev/null
+++ b/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/RepositoryInfo/GitReaderRepositoryInfoProvider.cs
@@ -0,0 +1,106 @@
+namespace Cake.Frosting.Issues.Recipe;
+
+using Cake.Core;
+using Cake.Core.IO;
+using GitReader;
+using GitReader.Structures;
+using System.IO;
+
+///
+/// Provider to retrieve repository information using GitReader library.
+///
+internal sealed class GitReaderRepositoryInfoProvider : IRepositoryInfoProvider
+{
+ ///
+ 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}");
+ }
+
+ ///
+ 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);
+ }
+ }
+
+ ///
+ 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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Cake.Issues.Recipe/Content/addins.cake b/Cake.Issues.Recipe/Content/addins.cake
index 5f6b2227..c7236663 100644
--- a/Cake.Issues.Recipe/Content/addins.cake
+++ b/Cake.Issues.Recipe/Content/addins.cake
@@ -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
diff --git a/Cake.Issues.Recipe/Content/data/IssuesData.cake b/Cake.Issues.Recipe/Content/data/IssuesData.cake
index 4a51565c..a03e01bc 100644
--- a/Cake.Issues.Recipe/Content/data/IssuesData.cake
+++ b/Cake.Issues.Recipe/Content/data/IssuesData.cake
@@ -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");
}
diff --git a/Cake.Issues.Recipe/Content/data/RepositoryInfoProviderType.cake b/Cake.Issues.Recipe/Content/data/RepositoryInfoProviderType.cake
index ee0be2d9..0f199d8f 100644
--- a/Cake.Issues.Recipe/Content/data/RepositoryInfoProviderType.cake
+++ b/Cake.Issues.Recipe/Content/data/RepositoryInfoProviderType.cake
@@ -12,5 +12,10 @@ public enum RepositoryInfoProviderType
/// Read repository information using Git CLI.
/// Requires Git CLI to be available in path.
///
- Cli
+ Cli,
+ ///
+ /// Read repository information using GitReader library.
+ /// Pure .NET implementation without external dependencies.
+ ///
+ GitReader
}
\ No newline at end of file
diff --git a/Cake.Issues.Recipe/Content/data/repositoryinfoprovider/GitReaderRepositoryInfoProvider.cake b/Cake.Issues.Recipe/Content/data/repositoryinfoprovider/GitReaderRepositoryInfoProvider.cake
new file mode 100644
index 00000000..b298b391
--- /dev/null
+++ b/Cake.Issues.Recipe/Content/data/repositoryinfoprovider/GitReaderRepositoryInfoProvider.cake
@@ -0,0 +1,98 @@
+///
+/// Provider to retrieve repository information using GitReader library.
+///
+public class GitReaderRepositoryInfoProvider : IRepositoryInfoProvider
+{
+ ///
+ 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}");
+ }
+
+ ///
+ 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);
+ }
+ }
+
+ ///
+ 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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Cake.Issues.Recipe/Content/data/repositoryinfoprovider/repositoryinfoprovider.cake b/Cake.Issues.Recipe/Content/data/repositoryinfoprovider/repositoryinfoprovider.cake
index 416f61a8..1d42fff7 100644
--- a/Cake.Issues.Recipe/Content/data/repositoryinfoprovider/repositoryinfoprovider.cake
+++ b/Cake.Issues.Recipe/Content/data/repositoryinfoprovider/repositoryinfoprovider.cake
@@ -1,3 +1,4 @@
#load IRepositoryInfoProvider.cake
#load CakeGitRepositoryInfoProvider.cake
-#load CliRepositoryInfoProvider.cake
\ No newline at end of file
+#load CliRepositoryInfoProvider.cake
+#load GitReaderRepositoryInfoProvider.cake
\ No newline at end of file