-
-
Notifications
You must be signed in to change notification settings - Fork 16
Add a typed step-injection pipeline to the GitHub Actions generator #475
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
avidenic
merged 14 commits into
Fallout-build:main
from
avidenic:features/456-github-actions-step-injection
Jul 13, 2026
Merged
Changes from 7 commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
219d884
Add typed step-injection types for the GitHub Actions generator
avidenic d9babf1
Inject custom steps into generated GitHub Actions jobs
avidenic bd59153
Move custom-step render helpers below the emit sequence
avidenic 4cfba2c
Merge branch 'main' into features/456-github-actions-step-injection
avidenic 9c7f3a7
Assert custom-step rendering with raw string literals
avidenic 4d11e18
Trim the ShouldRenderAs comment to the why
avidenic 2c7d1e0
Trim the GitHubActionsCustomStepSpecs class comment
avidenic cf18d3f
Harden custom-step rendering and injection against review feedback
avidenic 21d9def
Escape custom-step names as valid YAML and guard a vacuous test
avidenic 5b81959
Promote YAML single-quoting to a shared StringExtensions helper
avidenic d4d114c
Merge branch 'main' into features/456-github-actions-step-injection
avidenic 9839102
Merge branch 'main' into features/456-github-actions-step-injection
avidenic 3acf6fd
Snapshot custom-step rendering with Verify instead of manual literals
avidenic 3790d1f
Merge branch 'main' into features/456-github-actions-step-injection
avidenic File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
80 changes: 80 additions & 0 deletions
80
src/Fallout.Common/CI/GitHubActions/Configuration/GitHubActionsCustomStep.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| using System.Collections.Generic; | ||
| using System.Globalization; | ||
| using Fallout.Common.Utilities; | ||
| using Fallout.Common.Utilities.Collections; | ||
|
|
||
| namespace Fallout.Common.CI.GitHubActions.Configuration; | ||
|
|
||
| /// <summary> | ||
| /// A user-constructed workflow step injected via <see cref="IConfigureGitHubActions"/>. A non-empty | ||
| /// <see cref="Uses"/> renders a marketplace/action step; a non-empty <see cref="Run"/> renders a shell | ||
| /// step (a single entry as <c>run: x</c>, multiple as a <c>run: |</c> block scalar). Exactly one of the | ||
| /// two must be set — enforced at generation time. | ||
| /// </summary> | ||
| public class GitHubActionsCustomStep : GitHubActionsStep | ||
| { | ||
| public string Name { get; set; } | ||
| public string Uses { get; set; } | ||
| public Dictionary<string, string> With { get; set; } = new Dictionary<string, string>(); | ||
| public Dictionary<string, string> Env { get; set; } = new Dictionary<string, string>(); | ||
| public string If { get; set; } | ||
| public string Shell { get; set; } | ||
| public string[] Run { get; set; } = new string[0]; | ||
| public bool? ContinueOnError { get; set; } | ||
| public int? TimeoutMinutes { get; set; } | ||
| public string Id { get; set; } | ||
|
|
||
| public override void Write(CustomFileWriter writer) | ||
| { | ||
| // The first emitted key carries the '- ' list marker; every later key is a ' ' continuation. | ||
| var written = false; | ||
|
|
||
| if (!Name.IsNullOrWhiteSpace()) | ||
| Scalar("name", Name); | ||
| if (!Id.IsNullOrWhiteSpace()) | ||
| Scalar("id", Id); | ||
| if (!Uses.IsNullOrWhiteSpace()) | ||
| Scalar("uses", Uses); | ||
| if (With.Count > 0) | ||
| MapBlock("with", With); | ||
| if (Env.Count > 0) | ||
| MapBlock("env", Env); | ||
|
|
||
| if (Run.Length == 1) | ||
| { | ||
| Scalar("run", Run[0]); | ||
| } | ||
| else if (Run.Length > 1) | ||
| { | ||
| writer.WriteLine((written ? " " : "- ") + "run: |"); | ||
| written = true; | ||
| using (writer.Indent()) | ||
| Run.ForEach(x => writer.WriteLine($" {x}")); | ||
| } | ||
|
avidenic marked this conversation as resolved.
|
||
|
|
||
| if (!Shell.IsNullOrWhiteSpace()) | ||
| Scalar("shell", Shell); | ||
| if (!If.IsNullOrWhiteSpace()) | ||
| Scalar("if", If); | ||
| if (ContinueOnError.HasValue) | ||
| Scalar("continue-on-error", ContinueOnError.Value ? "true" : "false"); | ||
| if (TimeoutMinutes.HasValue) | ||
| Scalar("timeout-minutes", TimeoutMinutes.Value.ToString(CultureInfo.InvariantCulture)); | ||
|
|
||
| return; | ||
|
|
||
| void Scalar(string key, string value) | ||
| { | ||
| writer.WriteLine((written ? " " : "- ") + $"{key}: {value}"); | ||
|
avidenic marked this conversation as resolved.
|
||
| written = true; | ||
| } | ||
|
|
||
| void MapBlock(string key, Dictionary<string, string> map) | ||
| { | ||
| writer.WriteLine((written ? " " : "- ") + $"{key}:"); | ||
| written = true; | ||
| using (writer.Indent()) | ||
| map.ForEach(x => writer.WriteLine($" {x.Key}: {x.Value}")); | ||
| } | ||
| } | ||
| } | ||
51 changes: 51 additions & 0 deletions
51
src/Fallout.Common/CI/GitHubActions/Configuration/GitHubActionsStepPipeline.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
|
|
||
| namespace Fallout.Common.CI.GitHubActions.Configuration; | ||
|
|
||
| /// <summary> | ||
| /// The per-job insertion surface handed to <see cref="IConfigureGitHubActions.ConfigureSteps"/>. Carries | ||
| /// the job's identity (<see cref="WorkflowName"/>, <see cref="Image"/>) and a read-only view of the job's | ||
| /// built-in steps, and collects the caller's insertions. Generator-constructed; not user-instantiable. | ||
| /// </summary> | ||
| public class GitHubActionsStepPipeline | ||
| { | ||
| private readonly Dictionary<GitHubActionsStepPosition, List<GitHubActionsCustomStep>> _inserts = | ||
| new Dictionary<GitHubActionsStepPosition, List<GitHubActionsCustomStep>>(); | ||
|
|
||
| internal GitHubActionsStepPipeline(string workflowName, GitHubActionsImage image, IReadOnlyList<GitHubActionsStep> builtInSteps) | ||
| { | ||
| WorkflowName = workflowName; | ||
| Image = image; | ||
| BuiltInSteps = builtInSteps; | ||
| } | ||
|
|
||
| /// <summary>The name of the workflow this job belongs to (normalized, spaces-to-underscores).</summary> | ||
| public string WorkflowName { get; } | ||
|
|
||
| /// <summary>The runner image of this job.</summary> | ||
| public GitHubActionsImage Image { get; } | ||
|
|
||
| /// <summary>A read-only view of the built-in steps already assembled for this job.</summary> | ||
| public IReadOnlyList<GitHubActionsStep> BuiltInSteps { get; } | ||
|
|
||
| /// <summary>Insert one custom step at <paramref name="position"/>. Multiple inserts at one position render in call order.</summary> | ||
| public void Insert(GitHubActionsStepPosition position, GitHubActionsCustomStep step) | ||
|
avidenic marked this conversation as resolved.
|
||
| { | ||
| if (!_inserts.TryGetValue(position, out var list)) | ||
| _inserts[position] = list = new List<GitHubActionsCustomStep>(); | ||
| list.Add(step); | ||
| } | ||
|
|
||
| /// <summary>Insert several custom steps at <paramref name="position"/>, in enumeration order.</summary> | ||
| public void Insert(GitHubActionsStepPosition position, IEnumerable<GitHubActionsCustomStep> steps) | ||
| { | ||
| foreach (var step in steps) | ||
| Insert(position, step); | ||
| } | ||
|
avidenic marked this conversation as resolved.
|
||
|
|
||
| internal IReadOnlyList<GitHubActionsCustomStep> GetInserts(GitHubActionsStepPosition position) | ||
| => _inserts.TryGetValue(position, out var list) ? list : (IReadOnlyList<GitHubActionsCustomStep>)new GitHubActionsCustomStep[0]; | ||
|
avidenic marked this conversation as resolved.
|
||
|
|
||
| internal IEnumerable<GitHubActionsCustomStep> AllInserts => _inserts.Values.SelectMany(x => x); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
20 changes: 20 additions & 0 deletions
20
src/Fallout.Common/CI/GitHubActions/GitHubActionsStepPosition.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| namespace Fallout.Common.CI.GitHubActions; | ||
|
|
||
| /// <summary> | ||
| /// Named insertion points for custom steps, anchored to the always-present checkout and run block so | ||
| /// they stay well-defined when the optional cache / artifact steps are absent. | ||
| /// </summary> | ||
| public enum GitHubActionsStepPosition | ||
| { | ||
| /// <summary>After checkout, before the cache step (if any).</summary> | ||
| PostCheckout, | ||
|
|
||
| /// <summary>After the cache step (if any), before the setup-dotnet / restore / <c>dotnet fallout</c> block.</summary> | ||
| PreRun, | ||
|
|
||
| /// <summary>After the run block, before the built-in artifact upload (if any).</summary> | ||
| PostRun, | ||
|
|
||
| /// <summary>After the built-in artifact upload — the end of the job.</summary> | ||
| JobEnd, | ||
| } |
15 changes: 15 additions & 0 deletions
15
src/Fallout.Common/CI/GitHubActions/IConfigureGitHubActions.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| using Fallout.Common.CI.GitHubActions.Configuration; | ||
|
|
||
| namespace Fallout.Common.CI.GitHubActions; | ||
|
|
||
| /// <summary> | ||
| /// Implemented by a build to inject custom steps into generated GitHub Actions jobs. The generator calls | ||
| /// <see cref="ConfigureSteps"/> once per generated job, with a pipeline scoped to that job — so steps are | ||
| /// scoped to a workflow or runner by ordinary branching on <see cref="GitHubActionsStepPipeline.WorkflowName"/> | ||
| /// / <see cref="GitHubActionsStepPipeline.Image"/>, with no per-step scoping arrays. The generator stays in | ||
| /// sole control of the base step sequence; implementations only insert. | ||
| /// </summary> | ||
| public interface IConfigureGitHubActions | ||
| { | ||
| void ConfigureSteps(GitHubActionsStepPipeline pipeline); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.