Skip to content
Merged
4 changes: 4 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
<TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks>
</PropertyGroup>

<PropertyGroup Condition="$(MSBuildProjectName) == 'TUnit.Pipeline'">
<TargetFrameworks>net10.0</TargetFrameworks>
</PropertyGroup>

<PropertyGroup>
<LibraryTargetFramework Condition="$(TargetFramework.StartsWith('net4'))">netstandard2.0</LibraryTargetFramework>
<LibraryTargetFramework Condition="!$(TargetFramework.StartsWith('net4'))">$(TargetFramework)</LibraryTargetFramework>
Expand Down
8 changes: 4 additions & 4 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@
<PackageVersion Include="Microsoft.Testing.Platform" Version="2.0.2" />
<PackageVersion Include="Microsoft.Testing.Platform.MSBuild" Version="2.0.2" />
<PackageVersion Include="System.Threading.Channels" Version="9.0.0" />
<PackageVersion Include="ModularPipelines.DotNet" Version="2.48.30" />
<PackageVersion Include="ModularPipelines.Git" Version="2.48.30" />
<PackageVersion Include="ModularPipelines.GitHub" Version="2.48.30" />
<PackageVersion Include="ModularPipelines.DotNet" Version="3.0.1" />
<PackageVersion Include="ModularPipelines.Git" Version="3.0.1" />
<PackageVersion Include="ModularPipelines.GitHub" Version="3.0.1" />
<PackageVersion Include="MSTest" Version="4.0.2" />
<PackageVersion Include="MSTest.TestAdapter" Version="4.0.2" />
<PackageVersion Include="MSTest.TestFramework" Version="4.0.2" />
Expand Down Expand Up @@ -101,4 +101,4 @@
<PackageVersion Include="xunit.v3.assert" Version="3.2.2" />
<PackageVersion Include="xunit.v3.extensibility.core" Version="3.2.2" />
</ItemGroup>
</Project>
</Project>
47 changes: 22 additions & 25 deletions TUnit.Pipeline/Modules/Abstract/TestBaseModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
using ModularPipelines.Context;
using ModularPipelines.DotNet.Extensions;
using ModularPipelines.DotNet.Options;
using ModularPipelines.Enums;
using ModularPipelines.Models;
using ModularPipelines.Modules;
using ModularPipelines.Options;

namespace TUnit.Pipeline.Modules.Abstract;

Expand All @@ -24,17 +24,19 @@ protected virtual IEnumerable<string> TestableFrameworks
}
}

protected sealed override async Task<IReadOnlyList<CommandResult>?> ExecuteAsync(IPipelineContext context, CancellationToken cancellationToken)
protected sealed override async Task<IReadOnlyList<CommandResult>?> ExecuteAsync(IModuleContext context, CancellationToken cancellationToken)
{
var results = new List<CommandResult>();

foreach (var framework in TestableFrameworks)
{
var testResult = await SubModule(framework, async () =>
var testResult = await context.SubModule<CommandResult>(framework, async () =>
{
var testOptions = SetDefaults(await GetTestOptions(context, framework, cancellationToken));
var (testOptions, executionOptions) = await GetTestOptions(context, framework, cancellationToken);

return await context.DotNet().Run(testOptions, cancellationToken);
var finalExecutionOptions = SetDefaults(testOptions, executionOptions ?? new CommandExecutionOptions(), framework);

return await context.DotNet().Run(testOptions, finalExecutionOptions, cancellationToken);
});

results.Add(testResult);
Expand All @@ -43,35 +45,30 @@ protected virtual IEnumerable<string> TestableFrameworks
return results;
}

private DotNetRunOptions SetDefaults(DotNetRunOptions testOptions)
private CommandExecutionOptions SetDefaults(DotNetRunOptions testOptions, CommandExecutionOptions executionOptions, string framework)
{
if (testOptions.EnvironmentVariables?.Any(x => x.Key == "NET_VERSION") != true)
var envVars = executionOptions.EnvironmentVariables ?? new Dictionary<string, string?>();
if (!envVars.ContainsKey("NET_VERSION"))
{
testOptions = testOptions with
envVars = new Dictionary<string, string?>(envVars)
{
EnvironmentVariables = new Dictionary<string, string?>
{
["NET_VERSION"] = testOptions.Framework,
}
["NET_VERSION"] = framework
};
}

// Add hangdump flags with 20 minute timeout
var arguments = testOptions.Arguments?.ToList() ?? new List<string>();
if (!arguments.Contains("--hangdump"))
{
arguments.AddRange(["--hangdump", "--hangdump-filename", $"hangdump.{Environment.OSVersion.Platform}.{GetType().Name}.dmp", "--hangdump-timeout", "5m"]);
}

// Suppress output for successful operations, but show errors and basic info
testOptions = testOptions with
return executionOptions with
{
Arguments = arguments,
CommandLogging = CommandLogging.Input | CommandLogging.Error | CommandLogging.Duration | CommandLogging.ExitCode
EnvironmentVariables = envVars,
LogSettings = new CommandLoggingOptions
{
ShowCommandArguments = true,
ShowStandardError = true,
ShowExecutionTime = true,
ShowExitCode = true
}
};

return testOptions;
}

protected abstract Task<DotNetRunOptions> GetTestOptions(IPipelineContext context, string framework, CancellationToken cancellationToken);
protected abstract Task<(DotNetRunOptions Options, CommandExecutionOptions? ExecutionOptions)> GetTestOptions(IModuleContext context, string framework, CancellationToken cancellationToken);
}
23 changes: 18 additions & 5 deletions TUnit.Pipeline/Modules/AddLocalNuGetRepositoryModule.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,33 @@
using ModularPipelines.Context;
using ModularPipelines.DotNet.Extensions;
using ModularPipelines.DotNet.Options;
using ModularPipelines.Enums;
using ModularPipelines.FileSystem;
using ModularPipelines.Modules;
using ModularPipelines.Options;

namespace TUnit.Pipeline.Modules;

public class AddLocalNuGetRepositoryModule : Module<Folder>
{
protected override async Task<Folder?> ExecuteAsync(IPipelineContext context, CancellationToken cancellationToken)
protected override async Task<Folder?> ExecuteAsync(IModuleContext context, CancellationToken cancellationToken)
{
var folder = context.FileSystem.GetFolder(Environment.SpecialFolder.LocalApplicationData).GetFolder("LocalNuget").Create();
await context.DotNet().Nuget.Add.Source(new DotNetNugetAddSourceOptions(folder)
var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
var folder = new Folder(Path.Combine(localAppData, "LocalNuget"));
folder.Create();

await context.DotNet().Nuget.Add.Source(new DotNetNugetAddSourceOptions
{
Name = "LocalNuget",
Packagesourcepath = folder.Path,
}, new CommandExecutionOptions
{
CommandLogging = CommandLogging.Input | CommandLogging.Error | CommandLogging.Duration | CommandLogging.ExitCode
LogSettings = new CommandLoggingOptions
{
ShowCommandArguments = true,
ShowStandardError = true,
ShowExecutionTime = true,
ShowExitCode = true
}
}, cancellationToken);
return folder;
}
Expand Down
53 changes: 28 additions & 25 deletions TUnit.Pipeline/Modules/CommitFilesModule.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using ModularPipelines.Attributes;
using ModularPipelines.Configuration;
using ModularPipelines.Context;
using ModularPipelines.Extensions;
using ModularPipelines.Git.Attributes;
Expand All @@ -24,55 +25,62 @@ namespace TUnit.Pipeline.Modules;
[ModuleCategory("ReadMe")]
public class CommitFilesModule : Module<CommandResult>
{
protected override async Task<SkipDecision> ShouldSkip(IPipelineContext context)
{
var generateReadMeModule = GetModuleIfRegistered<GenerateReadMeModule>();

if (generateReadMeModule is null)
protected override ModuleConfiguration Configure() => ModuleConfiguration.Create()
.WithSkipWhen(async ctx =>
{
return "Nothing to commit";
}
var generateReadMeModule = ctx.GetModuleIfRegistered<GenerateReadMeModule>();

if (generateReadMeModule is null)
{
return SkipDecision.Skip("Nothing to commit");
}

var result = await generateReadMeModule;
var result = await generateReadMeModule;

return result.SkipDecision.ShouldSkip || !result.HasValue;
}
return result.IsSkipped || !result.IsSuccess
? SkipDecision.Skip("GenerateReadMeModule was skipped or has no value")
: SkipDecision.DoNotSkip;
})
.Build();

protected override async Task<CommandResult?> ExecuteAsync(IPipelineContext context, CancellationToken cancellationToken)
protected override async Task<CommandResult?> ExecuteAsync(IModuleContext context, CancellationToken cancellationToken)
{
var repositoryId = long.Parse(context.GitHub().EnvironmentVariables.RepositoryId!);

await context.Git().Commands.Config(new GitConfigOptions
{
Global = true,
Arguments = ["user.name", context.GitHub().EnvironmentVariables.Actor!]
}, cancellationToken);
}, token: cancellationToken);

await context.Git().Commands.Config(new GitConfigOptions
{
Global = true,
Arguments = ["user.email", $"{context.GitHub().EnvironmentVariables.ActorId!}_{context.GitHub().EnvironmentVariables.Actor!}@users.noreply.github.com"]
}, cancellationToken);
}, token: cancellationToken);

var newBranchName = $"feature/readme-{Guid.NewGuid():N}";

await context.Git().Commands.Checkout(new GitCheckoutOptions(newBranchName, true), cancellationToken);
await context.Git().Commands.Checkout(new GitCheckoutOptions(newBranchName, true), token: cancellationToken);

await context.Git().Commands.Add(new GitAddOptions
{
Arguments = ["README.md"],
WorkingDirectory = context.Git().RootDirectory.AssertExists()
}, new CommandExecutionOptions
{
WorkingDirectory = context.Git().RootDirectory.AssertExists().Path
}, cancellationToken);

await context.Git().Commands.Commit(new GitCommitOptions
{
Message = "Update README.md"
}, cancellationToken);
}, token: cancellationToken);

await context.Git().Commands.Push(new GitPushOptions
{
Arguments = ["--set-upstream", "origin", newBranchName]
}, cancellationToken);
SetUpstream = true,
Arguments = ["origin", newBranchName]
}, token: cancellationToken);

await context.Git().Commands.Push(token: cancellationToken);

Expand All @@ -85,12 +93,7 @@ await context.Git().Commands.Push(new GitPushOptions
await context.GitHub().Client.Issue.Update(repositoryId, pr.Number,
issueUpdate);

return await context.Command.ExecuteCommandLineTool(new CommandLineToolOptions("gh", "pr", "merge", "--admin", "--squash", pr.Number.ToString())
{
EnvironmentVariables = new Dictionary<string, string?>
{
["GH_TOKEN"] = Environment.GetEnvironmentVariable("ADMIN_TOKEN")
}
}, cancellationToken);
var adminToken = Environment.GetEnvironmentVariable("ADMIN_TOKEN");
return await context.Shell.Bash.Command(new BashCommandOptions($"GH_TOKEN={adminToken} gh pr merge --admin --squash {pr.Number}"), cancellationToken);
}
}
6 changes: 3 additions & 3 deletions TUnit.Pipeline/Modules/CopyToLocalNuGetModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ namespace TUnit.Pipeline.Modules;
[DependsOn<AddLocalNuGetRepositoryModule>]
public class CopyToLocalNuGetModule : Module<List<File>>
{
protected override async Task<List<File>?> ExecuteAsync(IPipelineContext context, CancellationToken cancellationToken)
protected override async Task<List<File>?> ExecuteAsync(IModuleContext context, CancellationToken cancellationToken)
{
var folder = await GetModule<AddLocalNuGetRepositoryModule>();
var folder = await context.GetModule<AddLocalNuGetRepositoryModule>();
return context.Git().RootDirectory.GetFiles(x => x.Name.StartsWith("TUnit", StringComparison.InvariantCultureIgnoreCase) && x.Extension.EndsWith("nupkg"))
.Select(x => x.CopyTo(folder.Value!))
.Select(x => x.CopyTo(folder.ValueOrDefault!))
.ToList();
}
}
27 changes: 16 additions & 11 deletions TUnit.Pipeline/Modules/CreateReleaseModule.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using ModularPipelines.Attributes;
using ModularPipelines.Configuration;
using ModularPipelines.Context;
using ModularPipelines.Git.Attributes;
using ModularPipelines.GitHub.Extensions;
Expand All @@ -14,23 +15,27 @@ namespace TUnit.Pipeline.Modules;
[DependsOn<GenerateVersionModule>]
public class CreateReleaseModule : Module<Release>
{
protected override async Task<SkipDecision> ShouldSkip(IPipelineContext context)
{
if (GetModuleIfRegistered<UploadToNuGetModule>() is not { } uploadToNuGetModule)
protected override ModuleConfiguration Configure() => ModuleConfiguration.Create()
.WithSkipWhen(async ctx =>
{
return true;
}
if (ctx.GetModuleIfRegistered<UploadToNuGetModule>() is not { } uploadToNuGetModule)
{
return SkipDecision.Skip("UploadToNuGetModule not registered");
}

var result = await uploadToNuGetModule;
var result = await uploadToNuGetModule;

return result.SkipDecision.ShouldSkip;
}
return result.IsSkipped
? SkipDecision.Skip("UploadToNuGetModule was skipped")
: SkipDecision.DoNotSkip;
})
.Build();

protected override async Task<Release?> ExecuteAsync(IPipelineContext context, CancellationToken cancellationToken)
protected override async Task<Release?> ExecuteAsync(IModuleContext context, CancellationToken cancellationToken)
{
var versionModule = await GetModule<GenerateVersionModule>();
var versionModule = await context.GetModule<GenerateVersionModule>();

var version = versionModule.Value!.SemVer;
var version = versionModule.ValueOrDefault!.SemVer;

var repositoryId = long.Parse(context.GitHub().EnvironmentVariables.RepositoryId!);

Expand Down
8 changes: 5 additions & 3 deletions TUnit.Pipeline/Modules/GenerateReadMeModule.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Text;
using System.IO.Compression;
using System.Text;
using Microsoft.Extensions.Logging;
using ModularPipelines.Attributes;
using ModularPipelines.Context;
Expand All @@ -20,7 +21,7 @@ namespace TUnit.Pipeline.Modules;
[ModuleCategory("ReadMe")]
public class GenerateReadMeModule : Module<File>
{
protected override async Task<File?> ExecuteAsync(IPipelineContext context, CancellationToken cancellationToken)
protected override async Task<File?> ExecuteAsync(IModuleContext context, CancellationToken cancellationToken)
{
var template = await context.Git()
.RootDirectory
Expand Down Expand Up @@ -90,7 +91,8 @@ public class GenerateReadMeModule : Module<File>
var downloadedZip = File.GetNewTemporaryFilePath();
await downloadedZip.WriteAsync(stream, cancellationToken);

var unzippedDirectory = context.Zip.UnZipToFolder(downloadedZip, Folder.CreateTemporaryFolder());
var unzippedDirectory = Folder.CreateTemporaryFolder();
ZipFile.ExtractToDirectory(downloadedZip.Path, unzippedDirectory.Path);

var markdownFile = unzippedDirectory.FindFile(x => x.Extension == ".md").AssertExists();

Expand Down
8 changes: 4 additions & 4 deletions TUnit.Pipeline/Modules/GenerateVersionModule.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using ModularPipelines.Context;
using ModularPipelines.Extensions;
using Microsoft.Extensions.Logging;
using ModularPipelines.Context;
using ModularPipelines.Git.Extensions;
using ModularPipelines.Git.Models;
using ModularPipelines.Modules;
Expand All @@ -8,11 +8,11 @@ namespace TUnit.Pipeline.Modules;

public class GenerateVersionModule : Module<GitVersionInformation>
{
protected override async Task<GitVersionInformation?> ExecuteAsync(IPipelineContext context, CancellationToken cancellationToken)
protected override async Task<GitVersionInformation?> ExecuteAsync(IModuleContext context, CancellationToken cancellationToken)
{
var versionInformation = await context.Git().Versioning.GetGitVersioningInformation();

context.LogOnPipelineEnd($"NuGet Version is: {versionInformation.SemVer}");
context.Logger.LogInformation("NuGet Version is: {SemVer}", versionInformation.SemVer);

return versionInformation;
}
Expand Down
2 changes: 1 addition & 1 deletion TUnit.Pipeline/Modules/GetPackageProjectsModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace TUnit.Pipeline.Modules;

public class GetPackageProjectsModule : Module<List<File>>
{
protected override async Task<List<File>?> ExecuteAsync(IPipelineContext context, CancellationToken cancellationToken)
protected override async Task<List<File>?> ExecuteAsync(IModuleContext context, CancellationToken cancellationToken)
{
await Task.CompletedTask;

Expand Down
Loading
Loading