diff --git a/src/SharedBuild/Tasks/TestTask.cs b/src/SharedBuild/Tasks/TestTask.cs index e781c15..9f31b86 100644 --- a/src/SharedBuild/Tasks/TestTask.cs +++ b/src/SharedBuild/Tasks/TestTask.cs @@ -11,6 +11,8 @@ using Cake.Core.Diagnostics; using Cake.Core.IO; using Cake.Frosting; +using Grynwald.SharedBuild.Tools.TemporaryFiles; +using Microsoft.VisualBasic; namespace Grynwald.SharedBuild.Tasks; @@ -26,7 +28,7 @@ public override async Task RunAsync(IBuildContext context) if (context.TestSettings.CollectCodeCoverage) { - GenerateCoverageReport(context); + await GenerateCoverageReportAsync(context); } } @@ -139,7 +141,7 @@ await context.GitHubActions().Commands.UploadArtifact( } } - private void GenerateCoverageReport(IBuildContext context) + private async Task GenerateCoverageReportAsync(IBuildContext context) { context.EnsureDirectoryDoesNotExist(context.Output.CodeCoverageReportDirectory, new() { Force = true, Recursive = true }); @@ -168,21 +170,55 @@ private void GenerateCoverageReport(IBuildContext context) } ); + var coverageReportPath = context.Output.CodeCoverageReportDirectory.CombineWithFilePath("Cobertura.xml"); + // // Publish Code coverage report // if (context.AzurePipelines.IsActive) { - context.Log.Information("Publishing Code Coverage Results to Azure Pipelines"); - context.AzurePipelines.Commands.PublishCodeCoverage(new() - { - CodeCoverageTool = AzurePipelinesCodeCoverageToolType.Cobertura, - SummaryFileLocation = context.Output.CodeCoverageReportDirectory.CombineWithFilePath("Cobertura.xml"), - ReportDirectory = context.Output.CodeCoverageReportDirectory - }); + PublishCodeCoverageToAzurePipelines(context, coverageReportPath); + } + else if (context.GitHubActions.IsActive) + { + await PublishCodeCoverageToGitHubActionsAsync(context, coverageReportPath); } } + protected virtual void PublishCodeCoverageToAzurePipelines(IBuildContext context, FilePath coverageReportPath) + { + context.Log.Information("Publishing Code Coverage Results to Azure Pipelines"); + context.AzurePipelines.Commands.PublishCodeCoverage(new() + { + CodeCoverageTool = AzurePipelinesCodeCoverageToolType.Cobertura, + SummaryFileLocation = coverageReportPath, + ReportDirectory = context.Output.CodeCoverageReportDirectory + }); + } + + protected virtual async Task PublishCodeCoverageToGitHubActionsAsync(IBuildContext context, FilePath coverageReportPath) + { + context.Log.Information("Publishing Code Coverage Results to GitHub Actions"); + + using var temporaryDirectory = context.CreateTemporaryDirectory(); + + // Generate Markdown coverage report + context.ReportGenerator( + reports: [coverageReportPath], + targetDir: temporaryDirectory.Path, + settings: new ReportGeneratorSettings() + { + ReportTypes = [ReportGeneratorReportType.MarkdownSummaryGithub], + HistoryDirectory = context.Output.CodeCoverageHistoryDirectory, + } + ); + var markdownSummaryPath = context.FileSystem.GetFilePaths(temporaryDirectory.Path, "*.md").Single(); + + // Publish coverage file and Summary are artifacts + await context.GitHubActions().Commands.UploadArtifact(coverageReportPath, context.GitHubActions.ArtifactNames.TestResults); + await context.GitHubActions().Commands.UploadArtifact(markdownSummaryPath, context.GitHubActions.ArtifactNames.TestResults); + } + private static IReadOnlyDictionary GetTestRunNames(IBuildContext context, IEnumerable testResultPaths) { var testRunNamer = new TestRunNamer(context.Log, context.Environment, context.FileSystem); diff --git a/src/SharedBuild/Tools/TemporaryFiles/TemporaryDirectory.cs b/src/SharedBuild/Tools/TemporaryFiles/TemporaryDirectory.cs new file mode 100644 index 0000000..b338394 --- /dev/null +++ b/src/SharedBuild/Tools/TemporaryFiles/TemporaryDirectory.cs @@ -0,0 +1,17 @@ +using System; +using Cake.Core.IO; + +namespace Grynwald.SharedBuild.Tools.TemporaryFiles; + +public class TemporaryDirectory(DirectoryPath path, IFileSystem fileSystem) : IDisposable +{ + public DirectoryPath Path { get; } = path ?? throw new ArgumentNullException(nameof(path)); + + public void Dispose() + { + if (fileSystem.GetDirectory(Path).Exists) + { + fileSystem.GetDirectory(Path).Delete(recursive: true); + } + } +} diff --git a/src/SharedBuild/Tools/TemporaryFiles/TemporaryDirectoryAliases.cs b/src/SharedBuild/Tools/TemporaryFiles/TemporaryDirectoryAliases.cs new file mode 100644 index 0000000..b1e2359 --- /dev/null +++ b/src/SharedBuild/Tools/TemporaryFiles/TemporaryDirectoryAliases.cs @@ -0,0 +1,17 @@ +using System; +using Cake.Core; +using Cake.Core.IO; + +namespace Grynwald.SharedBuild.Tools.TemporaryFiles; + +public static class TemporaryDirectoryAliases +{ + public static TemporaryDirectory CreateTemporaryDirectory(this ICakeContext context) + { + var path = context.Environment + .GetSpecialPath(SpecialPath.LocalTemp) + .Combine($"{Guid.NewGuid():n}"); + + return new TemporaryDirectory(path, context.FileSystem); + } +}