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
18 changes: 18 additions & 0 deletions .devops/workflows/Test_Devops_Build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -277,12 +277,30 @@ jobs:
artifact: DecSm.Atom.Tool-windows-latest
path: "$(Build.ArtifactStagingDirectory)/DecSm.Atom.Tool"

- task: DownloadPipelineArtifact@2
displayName: DecSm.Atom.Tool
inputs:
artifact: DecSm.Atom.Tool-windows-11-arm
path: "$(Build.ArtifactStagingDirectory)/DecSm.Atom.Tool"

- task: DownloadPipelineArtifact@2
displayName: DecSm.Atom.Tool
inputs:
artifact: DecSm.Atom.Tool-ubuntu-latest
path: "$(Build.ArtifactStagingDirectory)/DecSm.Atom.Tool"

- task: DownloadPipelineArtifact@2
displayName: DecSm.Atom.Tool
inputs:
artifact: DecSm.Atom.Tool-ubuntu-24.04-arm
path: "$(Build.ArtifactStagingDirectory)/DecSm.Atom.Tool"

- task: DownloadPipelineArtifact@2
displayName: DecSm.Atom.Tool
inputs:
artifact: DecSm.Atom.Tool-macos-15-intel
path: "$(Build.ArtifactStagingDirectory)/DecSm.Atom.Tool"

- task: DownloadPipelineArtifact@2
displayName: DecSm.Atom.Tool
inputs:
Expand Down
364 changes: 362 additions & 2 deletions .github/workflows/Build.yml

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions .github/workflows/Validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:
PackTool:
strategy:
matrix:
job-runs-on: [ windows-latest, ubuntu-latest, macos-latest ]
job-runs-on: [ windows-latest, windows-11-arm, ubuntu-latest, ubuntu-24.04-arm, macos-15-intel, macos-latest ]
runs-on: ${{ matrix.job-runs-on }}
steps:

Expand All @@ -71,7 +71,7 @@ jobs:
TestProjects:
strategy:
matrix:
job-runs-on: [ windows-latest, ubuntu-latest, macos-latest ]
job-runs-on: [ windows-latest, windows-11-arm, ubuntu-latest, ubuntu-24.04-arm, macos-15-intel, macos-latest ]
test-framework: [ net8.0, net9.0, net10.0 ]
runs-on: ${{ matrix.job-runs-on }}
steps:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
namespace DecSm.Atom.Module.GithubWorkflows.Tests.Workflows;

public sealed record GithubTestStep(string Text, GithubCustomStepOrder Order, int Priority = 0)
: GithubCustomStepOption(Order, Priority)
{
public override void WriteStep(GithubStepWriter writer)
{
using (writer.WriteSection("- name: Test step"))
writer.WriteLine($"run: echo \"{Text}\"");
}
}

[BuildDefinition]
public partial class GithubCustomStepBuild : MinimalBuildDefinition, IGithubWorkflows, ICustomStepTarget
{
public override IReadOnlyList<WorkflowDefinition> Workflows =>
[
new("github-custom-step-workflow")
{
Triggers = [ManualTrigger.Empty],
Targets = [WorkflowTargets.CustomStepTarget],
Options =
[
new GithubTestStep("Pre step 1", GithubCustomStepOrder.BeforeTarget, 1),
new GithubTestStep("Pre step 3", GithubCustomStepOrder.BeforeTarget, 3),
new GithubTestStep("Pre step 2", GithubCustomStepOrder.BeforeTarget, 2),
new GithubTestStep("Post step 3", GithubCustomStepOrder.AfterTarget, 3),
new GithubTestStep("Post step 1", GithubCustomStepOrder.AfterTarget, 1),
new GithubTestStep("Post step 2", GithubCustomStepOrder.AfterTarget, 2),
],
WorkflowTypes = [Github.WorkflowType],
},
];
}

public interface ICustomStepTarget
{
Target CustomStepTarget => t => t;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: github-custom-step-workflow

on:
workflow_dispatch:

jobs:

CustomStepTarget:
runs-on: ubuntu-latest
steps:

- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Test step
run: echo "Pre step 1"

- name: Test step
run: echo "Pre step 2"

- name: Test step
run: echo "Pre step 3"

- name: CustomStepTarget
id: CustomStepTarget
run: dotnet run --project AtomTest/AtomTest.csproj CustomStepTarget --skip --headless

- name: Test step
run: echo "Post step 1"

- name: Test step
run: echo "Post step 2"

- name: Test step
run: echo "Post step 3"
25 changes: 25 additions & 0 deletions DecSm.Atom.Module.GithubWorkflows.Tests/Workflows/WorkflowTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -411,4 +411,29 @@ public async Task PermissionsBuild_GeneratesWorkflow()
await Verify(workflow);
await TestContext.Out.WriteAsync(workflow);
}

[Test]
public async Task GithubCustomStepBuild_GeneratesWorkflow()
{
// Arrange
var fileSystem = FileSystemUtils.DefaultMockFileSystem;

var build = CreateTestHost<GithubCustomStepBuild>(fileSystem: fileSystem,
commandLineArgs: new(true, [new GenArg()]));

// Act
await build.RunAsync();

// Assert
fileSystem
.DirectoryInfo
.New(WorkflowDir)
.Exists
.ShouldBeTrue();

var workflow = await fileSystem.File.ReadAllTextAsync($"{WorkflowDir}github-custom-step-workflow.yml");

await Verify(workflow);
await TestContext.Out.WriteAsync(workflow);
}
}
45 changes: 45 additions & 0 deletions DecSm.Atom.Module.GithubWorkflows/Generation/GithubStepWriter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
namespace DecSm.Atom.Module.GithubWorkflows.Generation;

[PublicAPI]
public sealed record GithubStepWriter(StringBuilder Builder, int BaseIndentLevel)
{
private const int TabSize = 2;
private int _indentLevel = BaseIndentLevel;

/// <summary>
/// Writes a line of text to the output with the current indentation.
/// </summary>
/// <param name="value">The text to write. If null, an empty line is written.</param>
public void WriteLine(string? value = null)
{
if (_indentLevel > 0)
Builder.Append(new string(' ', _indentLevel));

Builder.AppendLine(value);
}

/// <summary>
/// Writes a section header and returns a disposable scope that manages indentation for the section's content.
/// </summary>
/// <param name="header">The header text for the section.</param>
/// <returns>A disposable object that decreases the indentation level upon disposal.</returns>
/// <example>
/// <code>
/// using (WriteSection("- checkout: self"))
/// WriteLine("fetchDepth: 0");
/// </code>
/// </example>
public IDisposable WriteSection(string header)
{
WriteLine(header);
_indentLevel += TabSize;

return new ActionScope(() => _indentLevel -= TabSize);
}

public void ResetIndent() =>
_indentLevel = BaseIndentLevel;

public override string ToString() =>
Builder.ToString();
}
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,22 @@ private void WriteStep(WorkflowModel workflow, WorkflowStepModel step, WorkflowJ
.DistinctBy(x => x.FeedName)
.ToList();

var syncAtomToolVersionToLibraryVersion = setupNugetSteps.Any(x => x.SyncAtomToolVersionToLibraryVersion);
var toolVersion = "";

if (syncAtomToolVersionToLibraryVersion)
{
if (SemVer.TryParse(typeof(AtomHost).Assembly
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
?.InformationalVersion ??
"",
out var semVer))
toolVersion = semVer;
else
throw new InvalidOperationException(
"Failed to parse DecSm.Atom.Host assembly version as SemVer for syncing atom tool version");
}

// If we know the SetupDotnet step was run for dotnet 10+,
// then we can use the dotnet tool exec command instead of installing the tool to run it
if (setupDotnetSteps.Any(x =>
Expand All @@ -502,8 +518,12 @@ private void WriteStep(WorkflowModel workflow, WorkflowStepModel step, WorkflowJ
using (WriteSection("run: |"))
{
foreach (var feedToAdd in feedsToAdd)
WriteLine(
$"dotnet tool exec decsm.atom.tool -y -- nuget-add --name \"{feedToAdd.FeedName}\" --url \"{feedToAdd.FeedUrl}\"");
if (syncAtomToolVersionToLibraryVersion)
WriteLine(
$"dotnet tool exec decsm.atom.tool@{toolVersion} -y -- nuget-add --name \"{feedToAdd.FeedName}\" --url \"{feedToAdd.FeedUrl}\"");
else
WriteLine(
$"dotnet tool exec decsm.atom.tool -y -- nuget-add --name \"{feedToAdd.FeedName}\" --url \"{feedToAdd.FeedUrl}\"");
}

WriteLine("shell: bash");
Expand Down Expand Up @@ -664,6 +684,26 @@ private void WriteCommandStep(
(string name, string value)[] extraParams,
bool includeId)
{
var customPreTargetSteps = workflowStep
.Options
.Concat(workflow.Options)
.OfType<IGithubCustomStepOption>()
.Where(x => x.Order is GithubCustomStepOrder.BeforeTarget)
.OrderBy(x => x.Priority)
.ToList();

if (customPreTargetSteps.Count > 0)
{
var writer = new GithubStepWriter(StringBuilder, IndentLevel);

foreach (var customPostStep in customPreTargetSteps)
{
customPostStep.WriteStep(writer);
writer.ResetIndent();
WriteLine();
}
}

using (WriteSection($"- name: {workflowStep.Name}"))
{
if (includeId)
Expand Down Expand Up @@ -818,6 +858,26 @@ private void WriteCommandStep(
WriteLine($"{key}: {value}");
}
}

var customPostTargetSteps = workflowStep
.Options
.Concat(workflow.Options)
.OfType<IGithubCustomStepOption>()
.Where(x => x.Order is GithubCustomStepOrder.AfterTarget)
.OrderBy(x => x.Priority)
.ToList();

if (customPostTargetSteps.Count > 0)
{
var writer = new GithubStepWriter(StringBuilder, IndentLevel);

foreach (var customPostStep in customPostTargetSteps)
{
WriteLine();
customPostStep.WriteStep(writer);
writer.ResetIndent();
}
}
}

private static string FindProjectPath(IAtomFileSystem fileSystem, string projectName)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace DecSm.Atom.Module.GithubWorkflows.Generation.Options;

[PublicAPI]
public abstract record GithubCustomStepOption(GithubCustomStepOrder Order, int Priority = 0) : IGithubCustomStepOption
{
public abstract void WriteStep(GithubStepWriter writer);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace DecSm.Atom.Module.GithubWorkflows.Generation.Options;

[PublicAPI]
public enum GithubCustomStepOrder
{
BeforeTarget,
AfterTarget,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace DecSm.Atom.Module.GithubWorkflows.Generation.Options;

[PublicAPI]
public interface IGithubCustomStepOption : IWorkflowOption
{
bool IWorkflowOption.AllowMultiple => true;

GithubCustomStepOrder Order { get; }

int Priority { get; }

void WriteStep(GithubStepWriter writer);
}
2 changes: 2 additions & 0 deletions DecSm.Atom.Module.GithubWorkflows/_usings.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
global using System.Globalization;
global using System.IO.Compression;
global using System.Reflection;
global using System.Text;
global using DecSm.Atom.Artifacts;
global using DecSm.Atom.Build;
Expand All @@ -13,6 +14,7 @@
global using DecSm.Atom.Params;
global using DecSm.Atom.Paths;
global using DecSm.Atom.Reports;
global using DecSm.Atom.Util.Scope;
global using DecSm.Atom.Variables;
global using DecSm.Atom.Workflows.Definition;
global using DecSm.Atom.Workflows.Definition.Options;
Expand Down
2 changes: 1 addition & 1 deletion DecSm.Atom.Tool/DecSm.Atom.Tool.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
</PropertyGroup>

<PropertyGroup Condition="$(TargetFramework) == 'net10.0'">
<RuntimeIdentifiers>win-x64;linux-x64;osx-arm64;any</RuntimeIdentifiers>
<RuntimeIdentifiers>win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers>
<PublishAot>true</PublishAot>

<!-- Size optimisation bits for Native AOT -->
Expand Down
Loading
Loading