Skip to content
Merged
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
6 changes: 6 additions & 0 deletions src/NetPackageAnalyzer/AnalyzeMerge/AnalyzeMerge.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,10 @@

</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\NPA.ProcessRunner\NPA.ProcessRunner.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="TestableIO.System.IO.Abstractions.Wrappers" Version="22.1.0" />
</ItemGroup>
</Project>
18 changes: 11 additions & 7 deletions src/NetPackageAnalyzer/AnalyzeMerge/AnalyzeMergeData.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
namespace AnalyzeMerge;
using System.IO.Abstractions;

namespace AnalyzeMerge;
public class AnalyzeMergeData
{
private readonly string folder;
private readonly IFileSystem fileSystem;
private string folderRoot;
public AnalyzeMergeData(string folder)
public AnalyzeMergeData(string folder, IFileSystem? fileSystem = null)
{
this.folder = folder;
this.fileSystem = fileSystem ?? new FileSystem();
folderRoot=folder;
}

Expand Down Expand Up @@ -79,13 +83,13 @@ public async Task<string> GenerateNow()
data.Analyze();
var g = new TemplateGenerator();
var folderResults = Path.Combine(folderRoot, "changes");
if(!Path.Exists(folderResults))
Directory.CreateDirectory(folderResults);
if(!fileSystem.Directory.Exists(folderResults))
fileSystem.Directory.CreateDirectory(folderResults);
var file = Path.Combine(folderResults, $"Changes_{currentBranch1.FriendlyName}.html");
await File.WriteAllTextAsync(file, await g.Generate_DisplayAllFiles (data));
await fileSystem.File.WriteAllTextAsync(file, await g.Generate_DisplayAllFiles (data));

file = Path.Combine(folderResults, $"Changes_{currentBranch1.FriendlyName}.md");
await File.WriteAllTextAsync(file, await g.Generate_DisplayAllFilesMD(data));
await fileSystem.File.WriteAllTextAsync(file, await g.Generate_DisplayAllFilesMD(data));

//Console.WriteLine("=>"+name);
//Branch? currentBranch = null;
Expand Down Expand Up @@ -202,4 +206,4 @@ public async Task<string> GenerateNow()
// .Single();

//}
}
}
27 changes: 13 additions & 14 deletions src/NetPackageAnalyzer/AnalyzeMerge/ProcessOutput.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
namespace AnalyzeMerge;
using NPA.ProcessRunner;

namespace AnalyzeMerge;
class ProcessOutput
{
private readonly IProcessRunner processRunner;

public ProcessOutput(IProcessRunner? processRunner = null)
{
this.processRunner = processRunner ?? new SystemProcessRunner();
}

public string ExecuteGit(string folder, GitData gitData)
{
var args = gitData switch
Expand All @@ -24,19 +33,9 @@ public string ExecuteGit(string folder, GitData gitData)
Arguments = args
};

// Create and start the process
Process process = new Process
{
StartInfo = startInfo
};
process.Start();

// Read the output
string output = process.StandardOutput.ReadToEnd();
string errorOutput = process.StandardError.ReadToEnd();

// Wait for the process to exit
process.WaitForExit();
var result = processRunner.Run(startInfo);
string output = result.StandardOutput;
string errorOutput = result.StandardError;
if (errorOutput.Length > 0)
{
throw new ArgumentException(errorOutput);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,7 @@
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="TestableIO.System.IO.Abstractions.Wrappers" Version="22.1.0" />
</ItemGroup>
</Project>
13 changes: 8 additions & 5 deletions src/NetPackageAnalyzer/NPA.BigResources/ZipFiles.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
using System.IO.Compression;
using System.IO.Abstractions;
using System.IO.Compression;

namespace NPA.BigResources;

public class ZipBigFiles
{
//TODO : should contain files for Windows, Linux, MacOS
public static async Task SaveToFile(string folderPath, EmbeddedResource embeddedResource)
public static async Task SaveToFile(string folderPath, EmbeddedResource embeddedResource, IFileSystem? fileSystem = null)
{
if(Directory.Exists(folderPath) && Directory.EnumerateFiles(folderPath).Any())
fileSystem ??= new FileSystem();

if(fileSystem.Directory.Exists(folderPath) && fileSystem.Directory.EnumerateFiles(folderPath).Any())
{
return;
}
if(!Directory.Exists(folderPath))
if(!fileSystem.Directory.Exists(folderPath))
{
Directory.CreateDirectory(folderPath);
fileSystem.Directory.CreateDirectory(folderPath);
}
using var stream = EmbeddedResources.GetStream(embeddedResource);

Expand Down
39 changes: 17 additions & 22 deletions src/NetPackageAnalyzer/NPA.GitInfo/GitInfo.cs
Original file line number Diff line number Diff line change
@@ -1,40 +1,45 @@
using System.Diagnostics;
using System.Diagnostics;
using NPA.ProcessRunner;

namespace NPA.GitInfo;

public class GitInfo
{
private GitInfo()
public IProcessRunner ProcessRunner { get; set; }

private GitInfo(IProcessRunner? processRunner)
{
ProcessRunner = processRunner ?? new SystemProcessRunner();
}
public string Repository { get; set; } = string.Empty;
public string Branch { get; set; } = string.Empty;

public string Commit { get; set; }= string.Empty;

static Dictionary<string,GitInfo> gitInfo=[];
public static GitInfo Construct(string folder)
public static GitInfo Construct(string folder, IProcessRunner? processRunner = null)
{

processRunner = processRunner ?? new SystemProcessRunner();
if(gitInfo.ContainsKey(folder))
{
return gitInfo[folder];
}
var ret = new GitInfo();
ret.Branch = ExecuteGit(folder, "branch --show-current");
ret.Repository = ExecuteGit(folder, "config --get remote.origin.url");
var ret = new GitInfo(processRunner);
ret.Branch = ExecuteGit(folder, "branch --show-current", processRunner);
ret.Repository = ExecuteGit(folder, "config --get remote.origin.url", processRunner);
if(ret.Repository.EndsWith(".git"))
{
ret.Repository = ret.Repository.Substring(0, ret.Repository.Length - 4);
}
ret.Commit = ExecuteGit(folder, "rev-parse HEAD");
ret.Commit = ExecuteGit(folder, "rev-parse HEAD", processRunner);
gitInfo.Add(folder, ret);
return ret;
}

static string ExecuteGit(string folder, string args)
static string ExecuteGit(string folder, string args, IProcessRunner? processRunner)
{

processRunner ??= new SystemProcessRunner();

ProcessStartInfo startInfo = new ProcessStartInfo
{
Expand All @@ -49,19 +54,9 @@ static string ExecuteGit(string folder, string args)
Arguments = args
};

// Create and start the process
Process process = new Process
{
StartInfo = startInfo
};
process.Start();

// Read the output
string output = process.StandardOutput.ReadToEnd();
string errorOutput = process.StandardError.ReadToEnd();

// Wait for the process to exit
process.WaitForExit();
var result = processRunner.Run(startInfo);
string output = result.StandardOutput;
string errorOutput = result.StandardError;
if (errorOutput.Length > 0)
{
throw new ArgumentException(errorOutput);
Expand Down
3 changes: 3 additions & 0 deletions src/NetPackageAnalyzer/NPA.GitInfo/NPA.GitInfo.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\NPA.ProcessRunner\NPA.ProcessRunner.csproj" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
using NPA.ProcessRunner;

namespace NPA.GitSummary.Tests;

public class GitRepositorySummaryAnalyzerTests
{



[Fact]
public void CreateChurnHotspotReport_GroupsAndOrdersFiles()
{
var report = GitRepositorySummaryAnalyzer.CreateChurnHotspotReport(
["src/B.cs", "src/A.cs", "src/B.cs", "src/C.cs", "src/B.cs", "src/A.cs"],
topCount: 2,
since: "1 year ago");

Assert.Equal("1 year ago", report.Since);
Assert.Collection(
report.Files,
item =>
{
Assert.Equal("src/B.cs", item.FilePath);
Assert.Equal(3, item.Count);
},
item =>
{
Assert.Equal("src/A.cs", item.FilePath);
Assert.Equal(2, item.Count);
});
}

[Fact]
public void CreateBusFactorReport_ComputesTopContributorRatio()
{
var report = GitRepositorySummaryAnalyzer.CreateBusFactorReport(
["Alice", "Bob", "Alice", "Alice"],
topCount: 10);

Assert.Equal(4, report.TotalCommits);
Assert.NotNull(report.TopContributor);
Assert.Equal("Alice", report.TopContributor!.Contributor);
Assert.Equal(3, report.TopContributor.CommitCount);
Assert.Equal(0.75, report.TopContributorRatio);
}

[Fact]
public void CreateCommitVelocityReport_GroupsCommitsByMonth()
{
var report = GitRepositorySummaryAnalyzer.CreateCommitVelocityReport(
[
"2026-04-03T08:30:00+00:00",
"2026-04-01T09:00:00+00:00",
"2026-03-28T10:00:00+00:00"
]);

Assert.Collection(
report.Months,
item =>
{
Assert.Equal("2026-03", item.YearMonth);
Assert.Equal(1, item.CommitCount);
},
item =>
{
Assert.Equal("2026-04", item.YearMonth);
Assert.Equal(2, item.CommitCount);
});
}

[Fact]
public void CreateFirefightingReport_FiltersRelevantSubjects()
{
var report = GitRepositorySummaryAnalyzer.CreateFirefightingReport(
[
"2026-04-03T08:30:00+00:00\tabc123\tHotfix authentication flow",
"2026-04-02T08:30:00+00:00\tdef456\tAdd diagnostics report",
"2026-04-01T08:30:00+00:00\tghi789\tRollback failing deployment"
],
since: "1 year ago");

Assert.Equal("1 year ago", report.Since);
Assert.Collection(
report.Commits,
item =>
{
Assert.Equal("abc123", item.Sha);
Assert.Equal("Hotfix authentication flow", item.Subject);
},
item =>
{
Assert.Equal("ghi789", item.Sha);
Assert.Equal("Rollback failing deployment", item.Subject);
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<OutputType>Exe</OutputType>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageReference Include="xunit.v3" Version="3.2.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\NPA.ProcessRunner\NPA.ProcessRunner.csproj" />
<ProjectReference Include="..\NPA.GitSummary\NPA.GitSummary.csproj" />
</ItemGroup>

</Project>
Loading
Loading