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
2 changes: 1 addition & 1 deletion build/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<NamespacePrefix>Purview.Aspire.ResourceKit</NamespacePrefix>
<NamespacePrefix>Purview.SourceGeneratorFramework</NamespacePrefix>
<ExcludePurviewTelemetry>true</ExcludePurviewTelemetry>
</PropertyGroup>

Expand Down
6 changes: 3 additions & 3 deletions build/PipelineCLI/GlobalUsings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
global using ModularPipelines.Extensions;
global using Octokit;
global using Octokit.Internal;
global using Purview.Aspire.ResourceKit.PipelineCLI.Helpers;
global using Purview.Aspire.ResourceKit.PipelineCLI.Modules;
global using Purview.Aspire.ResourceKit.PipelineCLI.Settings;
global using Purview.SourceGeneratorFramework.PipelineCLI.Helpers;
global using Purview.SourceGeneratorFramework.PipelineCLI.Modules;
global using Purview.SourceGeneratorFramework.PipelineCLI.Settings;
2 changes: 1 addition & 1 deletion build/PipelineCLI/Helpers/DotNetCLIOptions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using ModularPipelines.Options;

namespace Purview.Aspire.ResourceKit.PipelineCLI.Helpers;
namespace Purview.SourceGeneratorFramework.PipelineCLI.Helpers;

public sealed record DotNetCLIOptions : CommandLineToolOptions
{
Expand Down
9 changes: 6 additions & 3 deletions build/PipelineCLI/Helpers/PathHelpers.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
namespace Purview.Aspire.ResourceKit.PipelineCLI.Helpers;
namespace Purview.SourceGeneratorFramework.PipelineCLI.Helpers;

static class PathHelpers
{
public static string FindRepositoryRoot(string startDirectory)
public static string FindRepositoryRoot(string? startDirectory = null)
{
var directory = new DirectoryInfo(startDirectory);
if (string.IsNullOrEmpty(startDirectory))
startDirectory = PipelineProjectDirectory.Find();

DirectoryInfo? directory = new(startDirectory);
while (directory is not null)
{
if (File.Exists(Path.Combine(directory.FullName, "package.json")))
Expand Down
2 changes: 1 addition & 1 deletion build/PipelineCLI/Helpers/TestHelpers.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace Purview.Aspire.ResourceKit.PipelineCLI.Helpers;
namespace Purview.SourceGeneratorFramework.PipelineCLI.Helpers;

static class TestHelpers
{
Expand Down
2 changes: 1 addition & 1 deletion build/PipelineCLI/Modules/BuildModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using ModularPipelines.Models;
using ModularPipelines.Modules;

namespace Purview.Aspire.ResourceKit.PipelineCLI.Modules;
namespace Purview.SourceGeneratorFramework.PipelineCLI.Modules;

[ModuleCategory("Build")]
[DependsOn<RestoreModule>]
Expand Down
14 changes: 4 additions & 10 deletions build/PipelineCLI/Modules/CreateGitHubReleaseModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
using ModularPipelines.Models;
using ModularPipelines.Modules;

namespace Purview.Aspire.ResourceKit.PipelineCLI.Modules;
namespace Purview.SourceGeneratorFramework.PipelineCLI.Modules;

[ModuleCategory("Release")]
[DependsOn<PublishNuGetModule>]
Expand All @@ -17,16 +17,10 @@ protected override ModuleConfiguration Configure() =>
ModuleConfiguration
.Create()
.WithSkipWhen(_ =>
releaseSettings.Value.Mode is ReleaseMode.NuGet or ReleaseMode.GitHubRelease
? SkipDecision.DoNotSkip
: SkipDecision.Skip(
"Release publishing is disabled. Set Release__Mode=GitHubRelease or Release__Mode=NuGet to create a GitHub release."
)
)
.WithSkipWhen(_ =>
string.IsNullOrWhiteSpace(gitSettings.Value.GetGitHubToken())
releaseSettings.Value.Mode is not (ReleaseMode.NuGet or ReleaseMode.GitHubRelease)
|| string.IsNullOrWhiteSpace(gitSettings.Value.GetGitHubToken())
? SkipDecision.Skip(
"GitHub access token is not configured. Set GitHub__AccessToken or GITHUB_TOKEN to create a GitHub release."
"GitHub release creation is disabled. Set Release__Mode=NuGet (or GitHubRelease) and GITHUB_TOKEN to create a GitHub release."
)
: SkipDecision.DoNotSkip
)
Expand Down
29 changes: 23 additions & 6 deletions build/PipelineCLI/Modules/LintModule.cs
Original file line number Diff line number Diff line change
@@ -1,27 +1,44 @@
using ModularPipelines.Attributes;
using ModularPipelines.Configuration;
using ModularPipelines.Context;
using ModularPipelines.DotNet.Extensions;
using ModularPipelines.Models;
using ModularPipelines.Modules;

namespace Purview.Aspire.ResourceKit.PipelineCLI.Modules;
namespace Purview.SourceGeneratorFramework.PipelineCLI.Modules;

[ModuleCategory("Build")]
public sealed class LintModule : Module<CommandResult>
public sealed class LintModule(IOptions<BuildSettings> settings) : Module<CommandResult>
{
protected override ModuleConfiguration Configure() =>
ModuleConfiguration
.Create()
.WithSkipWhen(_ =>
settings.Value.RunLint
? SkipDecision.DoNotSkip
: SkipDecision.Skip("Linting is disabled. Set Build__RunLint=true to enable it.")
)
.Build();

protected override async Task<CommandResult?> ExecuteAsync(
IModuleContext context,
CancellationToken cancellationToken
)
{
var repositoryRoot = PathHelpers.FindRepositoryRoot();
var dotnet = context.DotNet();
await dotnet.Tool.Restore(new() { Interactive = false }, new(), cancellationToken);

var pipelineDirectory = PipelineProjectDirectory.Find();
var repositoryRoot = PathHelpers.FindRepositoryRoot(pipelineDirectory);
var restoreResult = await dotnet.Tool.Restore(
new() { Interactive = false, ToolManifest = Path.Combine(repositoryRoot, ".config", "dotnet-tools.json") },
new() { WorkingDirectory = repositoryRoot },
cancellationToken
);
if (restoreResult.ExitCode != 0)
return restoreResult;

// Restore worked, now run the linter
return await context.Shell.Command.ExecuteCommandLineTool(
DotNetCLIOptions.Create("tool", "run", "csharpier", "check", repositoryRoot),
new() { WorkingDirectory = repositoryRoot },
cancellationToken: cancellationToken
);
}
Expand Down
10 changes: 5 additions & 5 deletions build/PipelineCLI/Modules/PackModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
using ModularPipelines.Models;
using ModularPipelines.Modules;

namespace Purview.Aspire.ResourceKit.PipelineCLI.Modules;
namespace Purview.SourceGeneratorFramework.PipelineCLI.Modules;

[ModuleCategory("Build")]
[DependsOn<RunTestsModule>]
Expand All @@ -18,11 +18,11 @@ protected override ModuleConfiguration Configure() =>
ModuleConfiguration
.Create()
.WithSkipWhen(_ =>
releaseSettings.Value.Mode != ReleaseMode.None
? SkipDecision.DoNotSkip
: SkipDecision.Skip(
"Packing is disabled. Set Release__Mode to something other than None to enable it."
!settings.Value.RunPack || releaseSettings.Value.Mode == ReleaseMode.None
? SkipDecision.Skip(
"Packing is disabled. Set Build__RunPack=true and Release__Mode to something other than None to enable it."
)
: SkipDecision.DoNotSkip
)
.Build();

Expand Down
15 changes: 5 additions & 10 deletions build/PipelineCLI/Modules/PublishLocalNuGetModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
using ModularPipelines.Modules;
using NuGet.Versioning;

namespace Purview.Aspire.ResourceKit.PipelineCLI.Modules;
namespace Purview.SourceGeneratorFramework.PipelineCLI.Modules;

[ModuleCategory("Build")]
[DependsOn<PackModule>]
Expand All @@ -20,16 +20,11 @@ protected override ModuleConfiguration Configure() =>
ModuleConfiguration
.Create()
.WithSkipWhen(ctx =>
ctx.IsRunningLocally()
? SkipDecision.DoNotSkip
: SkipDecision.Skip("Local NuGet Feed publishing is disabled. This module can only be run locally.")
)
.WithSkipWhen(_ =>
releaseSettings.Value.Mode == ReleaseMode.LocalNuGet
? SkipDecision.DoNotSkip
: SkipDecision.Skip(
"Local NuGet Feed publishing is disabled. Set Release__Mode=LocalNuGet to enable it."
!ctx.IsRunningLocally() || releaseSettings.Value.Mode != ReleaseMode.LocalNuGet
? SkipDecision.Skip(
"Local NuGet Feed publishing is disabled. Run the pipeline locally with Release__Mode=LocalNuGet to enable it."
)
: SkipDecision.DoNotSkip
)
.Build();

Expand Down
27 changes: 14 additions & 13 deletions build/PipelineCLI/Modules/PublishNuGetModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
using ModularPipelines.Models;
using ModularPipelines.Modules;

namespace Purview.Aspire.ResourceKit.PipelineCLI.Modules;
namespace Purview.SourceGeneratorFramework.PipelineCLI.Modules;

[ModuleCategory("Release")]
[DependsOn<PackModule>]
Expand All @@ -20,16 +20,10 @@ protected override ModuleConfiguration Configure() =>
ModuleConfiguration
.Create()
.WithSkipWhen(_ =>
releaseSettings.Value.Mode == ReleaseMode.NuGet
? SkipDecision.DoNotSkip
: SkipDecision.Skip(
"Release publishing is disabled. Set Release__Mode=NuGet to publish packages to nuget.org."
)
)
.WithSkipWhen(_ =>
string.IsNullOrWhiteSpace(nugetSettings.Value.GetNuGetAPIKey())
releaseSettings.Value.Mode != ReleaseMode.NuGet
|| string.IsNullOrWhiteSpace(nugetSettings.Value.GetNuGetAPIKey())
? SkipDecision.Skip(
"NuGet API key is not set. Set NuGet__APIKey or NUGET_APIKEY to publish packages."
"NuGet publishing is disabled. Set Release__Mode=NuGet and NuGet__ApiKey (or NUGET_APIKEY) to publish packages to nuget.org."
)
: SkipDecision.DoNotSkip
)
Expand All @@ -40,9 +34,16 @@ protected override ModuleConfiguration Configure() =>
CancellationToken cancellationToken
)
{
var packages = Directory
.EnumerateFiles(buildSettings.Value.ArtifactsFolder, "*.nupkg", SearchOption.TopDirectoryOnly)
.ToList();
var artifactsFolder = buildSettings.Value.ArtifactsFolder;
if (!Directory.Exists(artifactsFolder))
{
throw new InvalidOperationException(
$"The artifacts folder '{artifactsFolder}' does not exist. "
+ "Ensure the pack step ran (Release__Mode must not be None) before publishing."
);
}

var packages = Directory.EnumerateFiles(artifactsFolder, "*.nupkg", SearchOption.TopDirectoryOnly).ToList();

if (packages.Count == 0)
{
Expand Down
2 changes: 1 addition & 1 deletion build/PipelineCLI/Modules/RestoreModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
using ModularPipelines.Models;
using ModularPipelines.Modules;

namespace Purview.Aspire.ResourceKit.PipelineCLI.Modules;
namespace Purview.SourceGeneratorFramework.PipelineCLI.Modules;

[ModuleCategory("Build")]
public class RestoreModule(IOptions<BuildSettings> settings) : Module<CommandResult>
Expand Down
73 changes: 66 additions & 7 deletions build/PipelineCLI/Modules/RunTestsModule.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.Diagnostics;
using System.Text.RegularExpressions;
using ModularPipelines.Attributes;
using ModularPipelines.Configuration;
using ModularPipelines.Context;
Expand All @@ -6,7 +8,7 @@
using ModularPipelines.Models;
using ModularPipelines.Modules;

namespace Purview.Aspire.ResourceKit.PipelineCLI.Modules;
namespace Purview.SourceGeneratorFramework.PipelineCLI.Modules;

[ModuleCategory("Build")]
[DependsOn<BuildModule>]
Expand All @@ -27,18 +29,26 @@ protected override ModuleConfiguration Configure() =>
CancellationToken cancellationToken
)
{
var testProjects = Directory.EnumerateFiles("src/tests", "*Tests.csproj", SearchOption.AllDirectories).ToList();
var testProjects = FilterTestProjects(
Directory.EnumerateFiles("src/tests", "*Tests.csproj", SearchOption.AllDirectories).ToList(),
settings.Value.TestProjects
);
if (testProjects.Count == 0)
{
context.Logger.LogWarning(
"No test projects found in 'src/tests', despite tests being enabled. Skipping test execution."
"No test projects matched 'src/tests' (filter: {TestProjects}), despite tests being enabled. Skipping test execution.",
settings.Value.TestProjects
);

return [];
}

var tasks = testProjects.Select(project =>
context
var timings = new List<(string Project, TimeSpan Elapsed, int ExitCode)>();

var tasks = testProjects.Select(async project =>
{
var stopwatch = Stopwatch.StartNew();
var result = await context
.DotNet()
.Test(
new DotNetTestOptions
Expand All @@ -50,9 +60,58 @@ CancellationToken cancellationToken
Arguments = ["--ignore-exit-code", "8", "--treenode-filter", settings.Value.TestFilter],
},
cancellationToken: cancellationToken
)
);
stopwatch.Stop();

lock (timings)
timings.Add((project, stopwatch.Elapsed, result.ExitCode));

return result;
});

var results = await Task.WhenAll(tasks);

context.Logger.LogInformation(
"Test run timings:{NewLine}{Timings}",
Environment.NewLine,
string.Join(
Environment.NewLine,
timings
.OrderByDescending(t => t.Elapsed)
.Select(t => $" {Path.GetFileName(t.Project)}: {t.Elapsed.TotalSeconds:F1}s (exit {t.ExitCode})")
)
);

return await Task.WhenAll(tasks);
return results;
}

static IReadOnlyList<string> FilterTestProjects(IReadOnlyList<string> projects, string filter)
{
if (string.IsNullOrWhiteSpace(filter) || filter.Trim() == "*")
return projects;

var patterns = filter
.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
.Select(ToRegexPattern)
.ToArray();

return projects
.Where(project =>
{
var fileName = Path.GetFileName(project);
return patterns.Any(pattern => Regex.IsMatch(fileName, pattern, RegexOptions.IgnoreCase));
})
.ToList();
}

static string ToRegexPattern(string entry)
{
if (entry.Contains('*', StringComparison.Ordinal))
{
var escaped = Regex.Escape(entry);
return "^" + escaped.Replace("\\*", ".*", StringComparison.Ordinal) + "$";
}

return "^" + Regex.Escape(entry) + "$";
}
}
2 changes: 1 addition & 1 deletion build/PipelineCLI/Modules/VersionModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using ModularPipelines.Modules;
using NuGet.Versioning;

namespace Purview.Aspire.ResourceKit.PipelineCLI.Modules;
namespace Purview.SourceGeneratorFramework.PipelineCLI.Modules;

[ModuleCategory("Build")]
public class VersionModule : Module<NuGetVersion>
Expand Down
2 changes: 1 addition & 1 deletion build/PipelineCLI/PipelineProjectDirectory.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System.Runtime.CompilerServices;

namespace Purview.Aspire.ResourceKit.PipelineCLI;
namespace Purview.SourceGeneratorFramework.PipelineCLI;

static class PipelineProjectDirectory
{
Expand Down
12 changes: 11 additions & 1 deletion build/PipelineCLI/Settings/BuildSettings.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System.ComponentModel.DataAnnotations;

namespace Purview.Aspire.ResourceKit.PipelineCLI.Settings;
namespace Purview.SourceGeneratorFramework.PipelineCLI.Settings;

public sealed class BuildSettings
{
Expand All @@ -21,4 +21,14 @@ public sealed class BuildSettings

[Required(AllowEmptyStrings = false)]
public string TestFilter { get; init; } = "/*/*/*/*/";

/// <summary>
/// Comma-separated list of test project file names (or glob patterns) to run.
/// Empty or "*" runs every test project under <c>src/tests</c>.
/// </summary>
public string TestProjects { get; init; } = "*";

public bool RunLint { get; init; } = true;

public bool RunPack { get; init; } = true;
}
Loading