![]() |
VOOZH | about |
dotnet add package Invex.Atom.Module.GithubWorkflows --version 3.1.0
NuGet\Install-Package Invex.Atom.Module.GithubWorkflows -Version 3.1.0
<PackageReference Include="Invex.Atom.Module.GithubWorkflows" Version="3.1.0" />
<PackageVersion Include="Invex.Atom.Module.GithubWorkflows" Version="3.1.0" />Directory.Packages.props
<PackageReference Include="Invex.Atom.Module.GithubWorkflows" />Project file
paket add Invex.Atom.Module.GithubWorkflows --version 3.1.0
#r "nuget: Invex.Atom.Module.GithubWorkflows, 3.1.0"
#:package Invex.Atom.Module.GithubWorkflows@3.1.0
#addin nuget:?package=Invex.Atom.Module.GithubWorkflows&version=3.1.0Install as a Cake Addin
#tool nuget:?package=Invex.Atom.Module.GithubWorkflows&version=3.1.0Install as a Cake Tool
👁 Validate
👁 Build
👁 Dependabot Updates
Atom is an opinionated, type-safe build automation framework for .NET. It enables you to define your build logic in C#, debug it like standard code, and automatically generate CI/CD configuration files for GitHub Actions and Azure DevOps.
Write build logic in C# alongside your application code.
Step through your build process using your IDE.
Define logic once; Atom generates the YAML for GitHub and Azure DevOps.
Pull in capabilities via NuGet packages (GitVersion, Azure KeyVault, etc.).
Reduces boilerplate by automatically discovering targets and parameters.
It is recommended to use the atom dotnet tool to invoke atom projects:
dotnet tool install -g Invex.Atom.Tool
atom ...
However, the dotnet cli can also be used directly:
dotnet run -- ...
Create a .NET 10 project
dotnet new console -n _atom
Update the _atom.csproj file:
<Project Sdk="Microsoft.NET.Sdk.Worker">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<RootNamespace>Atom</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Invex.Atom.Build" Version="3.*" />
</ItemGroup>
</Project>
Replace the Program.cs file with IBuild.cs:
using Invex.Atom.Build.Definition;
using Invex.Atom.Build.Hosting;
namespace Atom;
[BuildDefinition]
[GenerateEntryPoint]
internal interface IBuild : IBuildDefinition
{
Target HelloWorld =>
t => t
.DescribedAs("Prints a hello world message to the console")
.Executes(() => Logger.LogInformation("Hello, World!"));
}
Execute atom HelloWorld
26-06-06 +10:00 Invex.Atom.Build.BuildExecutor:
01:16:47.552 INF Executing build
HelloWorld
Prints a hello world message to the console
26-06-06 +10:00 HelloWorld | Atom.Build:
01:16:47.616 INF Hello, World!
Build Summary
HelloWorld │ Succeeded │ <0.01s
Add a parameter to the IBuild interface:
.RequiresParam(nameof(...)) is used to ensure the parameter is provided before the target is executed.
.UsesParam(nameof(...)) is used to use the parameter if it is provided, but not fail the build if it is not.
using Invex.Atom.Build.Definition;
using Invex.Atom.Build.Hosting;
using Invex.Atom.Build.Params;
namespace Atom;
[BuildDefinition]
[GenerateEntryPoint]
internal interface IBuild : IBuildDefinition
{
[ParamDefinition("my-name", "My name")]
string? MyName => GetParam(() => MyName);
Target HelloWorld =>
t => t
.DescribedAs("Prints a hello world message to the console")
.RequiresParam(nameof(MyName))
.Executes(() => Logger.LogInformation("Hello, World! I am {MyName}.", MyName));
}
Execute `atom HelloWorld --my-name Frodo
26-06-06 +10:00 Invex.Atom.Build.BuildExecutor:
01:23:25.405 INF Executing build
HelloWorld
Prints a hello world message to the console
26-06-06 +10:00 HelloWorld | Atom.Build:
01:23:25.467 INF Hello, World! I am Frodo.
Build Summary
HelloWorld │ Succeeded │ <0.01s
Add a secret parameter to the IBuild interface:
using Invex.Atom.Build.Definition;
using Invex.Atom.Build.Hosting;
using Invex.Atom.Build.Params;
namespace Atom;
[BuildDefinition]
[GenerateEntryPoint]
internal interface IBuild : IBuildDefinition
{
[ParamDefinition("my-name", "My name")]
string? MyName => GetParam(() => MyName);
[SecretDefinition("my-secret", "My secret")]
string? MySecret => GetParam(() => MySecret);
Target HelloWorld =>
t => t
.DescribedAs("Prints a hello world message to the console")
.RequiresParam(nameof(MyName))
.RequiresParam(nameof(MySecret))
.Executes(() =>
Logger.LogInformation("Hello, World! I am {MyName} and my secret is {MySecret}.",
MyName,
MySecret));
}
Execute atom HelloWorld --my-name Frodo --my-secret TheOneRing
The secret is masked in the output.
26-06-06 +10:00 Invex.Atom.Build.BuildExecutor:
01:27:08.482 INF Executing build
HelloWorld
Prints a hello world message to the console
26-06-06 +10:00 HelloWorld | Atom.Build:
01:27:08.544 INF Hello, World! I am Frodo and my secret is *****.
Build Summary
HelloWorld │ Succeeded │ <0.01s
Update the IBuild interface:
using Invex.Atom.Build.Definition;
using Invex.Atom.Build.Hosting;
using Invex.Atom.Build.Params;
namespace Atom;
[BuildDefinition]
[GenerateEntryPoint]
internal interface IBuild : IBuildDefinition
{
[ParamDefinition("my-name", "My name")]
string? MyName => GetParam(() => MyName);
[SecretDefinition("my-secret", "My secret")]
string? MySecret => GetParam(() => MySecret);
Target HelloWorld =>
t => t
.DescribedAs("Prints a hello world message to the console")
.RequiresParam(nameof(MyName))
.RequiresParam(nameof(MySecret))
.Executes(() =>
Logger.LogInformation("Hello, World! I am {MyName} and my secret is {MySecret}.", MyName, MySecret));
Target Goodbye =>
t => t
.DescribedAs("Prints a goodbye message to the console")
.DependsOn(nameof(HelloWorld))
.Executes(() => Logger.LogInformation("Goodbye!"));
}
Execute atom Goodbye --my-name Frodo --my-secret TheOneRing
The Goodbye target depends on the HelloWorld target, so both will be executed in the correct order, and the
parameters only need to be provided once.
26-06-06 +10:00 Invex.Atom.Build.BuildExecutor:
01:30:12.175 INF Executing build
HelloWorld
Prints a hello world message to the console
26-06-06 +10:00 HelloWorld | Atom.Build:
01:30:12.235 INF Hello, World! I am Frodo and my secret is *****.
Goodbye
Prints a goodbye message to the console
26-06-06 +10:00 Goodbye | Atom.Build:
01:30:12.240 INF Goodbye!
Build Summary
HelloWorld │ Succeeded │ <0.01s
Goodbye │ Succeeded │ <0.01s
Update the _atom.csproj file:
<Project Sdk="Microsoft.NET.Sdk.Worker">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<RootNamespace>Atom</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Invex.Atom.Module.GithubWorkflows" Version="3.*" />
</ItemGroup>
</Project>
Update the IBuild interface:
using Invex.Atom.Build.BuildOptions;
using Invex.Atom.Build.Definition;
using Invex.Atom.Build.Hosting;
using Invex.Atom.Build.Params;
using Invex.Atom.Module.GithubWorkflows.Extensions;
using Invex.Atom.Module.GithubWorkflows.Helpers;
using Invex.Atom.Workflows;
using Invex.Atom.Workflows.Definition;
using Invex.Atom.Workflows.Definition.Triggers;
using Invex.Atom.Workflows.Options;
using Invex.StructuredText.Expressions;
namespace Atom;
[BuildDefinition]
[GenerateEntryPoint]
internal interface IBuild : IWorkflowBuildDefinition, IGithubWorkflows
{
[ParamDefinition("my-name", "My name")]
string? MyName => GetParam(() => MyName);
[SecretDefinition("my-secret", "My secret")]
string? MySecret => GetParam(() => MySecret);
Target HelloWorld =>
t => t
.DescribedAs("Prints a hello world message to the console")
.RequiresParam(nameof(MyName))
.RequiresParam(nameof(MySecret))
.Executes(() =>
Logger.LogInformation("Hello, World! I am {MyName} and my secret is {MySecret}.", MyName, MySecret));
Target Goodbye =>
t => t
.DescribedAs("Prints a goodbye message to the console")
.DependsOn(nameof(HelloWorld))
.Executes(() => Logger.LogInformation("Goodbye!"));
IReadOnlyList<WorkflowDefinition> IWorkflowBuildDefinition.Workflows =>
[
new("Hello")
{
Triggers = [WorkflowTriggers.PushToMain],
Targets =
[
new(nameof(HelloWorld))
{
Options =
[
BuildOptions.Inject.Param(nameof(MyName), TextExpressions.Github.GithubRepositoryOwner),
BuildOptions.Inject.Secret(nameof(MySecret)),
],
},
new(nameof(Goodbye)),
],
Types = [WorkflowTypes.Github.Action],
},
];
}
Execute atom gen
26-06-06 +10:00 Invex.Atom.Module.GithubWorkflows.GithubActions.GithubWorkflowFileWriter:
01:38:57.388 INF Writing new workflow file:
L:\Repos\Invex-Games\atom\.github\workflows\Hello.yml
26-06-06 +10:00 Invex.Atom.Build.BuildExecutor:
01:38:57.472 INF Executing build
Gen
Generates workflow files
Build Summary
Gen │ Succeeded │ <0.01s
.github/workflows/Hello.yml
name: Hello
on:
push:
branches: [ main ]
permissions: { }
jobs:
HelloWorld:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: HelloWorld
id: HelloWorld
run: dotnet run --project _atom/_atom.csproj -- HelloWorld --skip --headless
env:
my-secret: ${{ secrets.MY_SECRET }}
my-name: ${{ github.repository_owner }}
Goodbye:
needs: [ HelloWorld ]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Goodbye
id: Goodbye
run: dotnet run --project _atom/_atom.csproj -- Goodbye --skip --headless
Atom can also be used as a file-based app:
Atom.cs
#:sdk Microsoft.NET.Sdk.Worker
#:package Invex.Atom.Build@3.*
using Invex.Atom.Build.Definition;
using Invex.Atom.Build.Hosting;
namespace Atom;
[BuildDefinition]
[GenerateEntryPoint]
internal interface IBuild : IBuildDefinition
{
Target HelloWorld =>
t => t
.DescribedAs("Prints a hello world message to the console")
.Executes(() => Logger.LogInformation("Hello, World!"));
}
BuildDefinition vs
WorkflowBuildDefinition[BuildDefinition] attribute and source generatorsTargetDefinition APIISecretsProviderIRootedFileSystem, RootedPath, and path providersIBuildOptionAtomHost, [GenerateEntryPoint], and host configurationIAtomLifecycleHook for pre/post-build logicTransformFileScopeWorkflowDefinition, targets, and typesInvex.Atom.Module.DotnetInvex.Atom.Module.GithubWorkflowsInvex.Atom.Module.DevopsWorkflowsInvex.Atom.Module.AzureKeyVaultInvex.Atom.Module.AzureStorageInvex.Atom.Module.GitVersionIArtifactProvider, ISecretsProvider,
and moreInvex.Atom.TestUtilsThe Atom libraries are human-made, however GitHub Copilot completions have been used on a superficial level.
Generative AI was also used to assist in writing tests and documentation.
Atom is released under the .
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 net8.0 is compatible. net8.0-android net8.0-android was computed. net8.0-browser net8.0-browser was computed. net8.0-ios net8.0-ios was computed. net8.0-maccatalyst net8.0-maccatalyst was computed. net8.0-macos net8.0-macos was computed. net8.0-tvos net8.0-tvos was computed. net8.0-windows net8.0-windows was computed. net9.0 net9.0 is compatible. net9.0-android net9.0-android was computed. net9.0-browser net9.0-browser was computed. net9.0-ios net9.0-ios was computed. net9.0-maccatalyst net9.0-maccatalyst was computed. net9.0-macos net9.0-macos was computed. net9.0-tvos net9.0-tvos was computed. net9.0-windows net9.0-windows was computed. net10.0 net10.0 is compatible. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
Showing the top 1 NuGet packages that depend on Invex.Atom.Module.GithubWorkflows:
| Package | Downloads |
|---|---|
|
Invex.RepoUtils.Atom.Module
A collection of .NET utilities for building and maintaining .NET repositories |
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 3.2.0-rc.4 | 65 | 6/17/2026 |
| 3.2.0-rc.2 | 43 | 6/16/2026 |
| 3.1.0 | 502 | 6/13/2026 |
| 3.1.0-rc.15 | 49 | 6/12/2026 |
| 3.1.0-rc.10 | 44 | 6/12/2026 |
| 3.1.0-rc.8 | 43 | 6/11/2026 |
| 3.1.0-rc.4 | 48 | 6/10/2026 |
| 3.0.0 | 1,442 | 6/9/2026 |
| 3.0.0-rc.201 | 53 | 6/8/2026 |
| 3.0.0-beta.feature-v-next.200 | 52 | 6/8/2026 |
| 3.0.0-beta.feature-v-next.194 | 51 | 6/8/2026 |
| 3.0.0-beta.feature-v-next.193 | 41 | 6/8/2026 |
| 3.0.0-beta.feature-v-next.191 | 47 | 6/8/2026 |