Atom is an opinionated task and build automation framework, written in C#.
Inspired by the likes of NUKE, Atom aims to provide a flexible, extensible framework for defining and executing build tasks. It leverages .NET and provides a comprehensive set of features for automating your development workflow with automatic CI/CD pipeline generation e.g. for GitHub Actions and Azure DevOps.
- π― Type-Safe Build Definitions: Define build targets using strongly-typed C# interfaces and classes
- π Automatic CI/CD Generation: Generate GitHub Actions and Azure DevOps pipelines from your build definitions
- π¦ Artifact Management: Built-in support for artifact storage and retrieval with CI/CD host or a custom provider
- π Secret Management: Secure secret handling with .NET secrets or custom providers
- π§© Modular Architecture: Extensible module system for different platforms and tools
- βοΈ Parameter Management: Flexible parameter system with validation and optional interactive prompting
- π Reporting: Comprehensive build reporting with CI/CD platform integration
- π¨ .NET Tooling: Built-in support for .NET operations (build, test, pack, publish)
- π Source Generation: Automatic code generation for build definitions
Create a new console application and add the Atom package:
dotnet new console -n MyBuild
cd MyBuild
dotnet add package DecSm.Atom
Create a Build.cs
file:
using DecSm.Atom.Build.Definition;
using DecSm.Atom.Hosting;
namespace MyBuild;
[BuildDefinition]
[GenerateEntryPoint]
internal partial class Build : BuildDefinition
{
private Target HelloWorld =>
t => t
.DescribedAs("Prints a hello world message")
.Executes(() => Logger.LogInformation("Hello, World!"));
}
Targets can also be async:
private Target HelloWorldAsync =>
t => t
.DescribedAs("Prints a hello world message asynchronously")
.Executes(async () =>
{
await Task.Delay(1000); // Simulate async work
Logger.LogInformation("Hello, World!");
});
private Target HelloWorldAsyncWithCancel =>
t => t
.DescribedAs("Prints a hello world message asynchronously")
.Executes(async cancellationToken =>
{
await Task.Delay(1000, cancellationToken); // Simulate async work with cancellation
Logger.LogInformation("Hello, World!");
});
dotnet run -- HelloWorld
Build definitions are classes that inherit from BuildDefinition
and define targets (build steps) as properties:
[BuildDefinition]
internal partial class Build : BuildDefinition
{
private Target Compile => t => t
.DescribedAs("Compiles the project")
.Executes(() =>
{
// Your build logic here
});
}
Define and use parameters in your builds:
[ParamDefinition("my-name", "Name to greet")]
private string? MyName => GetParam(() => MyName);
private Target Hello => t => t
.RequiresParam(nameof(MyName))
.Executes(() =>
{
Logger.LogInformation("Hello, {Name}!", MyName);
});
Define dependencies between targets:
private Target Test => t => t
.DependsOn(Compile)
.Executes(() =>
{
// Run tests after compilation
});
You can also define targets using interfaces for better organization:
using DecSm.Atom.Build.Definition;
[BuildDefinition]
internal partial class Build : BuildDefinition, ICompile, ITest;
public interface ICompile
{
Target Compile => t => t
.DescribedAs("Compiles the project")
.Executes(() =>
{
// Your build logic here
});
}
public interface ITest
{
Target Test => t => t
.DependsOn(Compile)
.Executes(() =>
{
// Run tests after compilation
});
}
You can access various build services like logging, parameters, and secrets:
public interface ICompile : IBuildAccessor
{
Target Compile => t => t
.DescribedAs("Compiles the project")
.Executes(() =>
{
Logger.LogInformation("Compiling project...");
FileSystem.File.Create("output.txt");
Services.Get<IService>().DoSomething();
});
}
Atom can automatically generate CI/CD pipelines for your builds:
Install the required modules for your CI/CD platform (GitHub Actions, Azure DevOps, etc.):
dotnet add package DecSm.Atom.Module.GithubWorkflows
[BuildDefinition]
public partial class Build : BuildDefinition, IGithubWorkflows
{
public override IReadOnlyList<WorkflowDefinition> Workflows =>
[
new("ci")
{
Triggers = [new GitPullRequestTrigger { IncludedBranches = ["main"] }],
StepDefinitions = [Targets.Test],
WorkflowTypes = [new GithubWorkflowType()]
}
];
}
Install the required modules for your CI/CD platform (GitHub Actions, Azure DevOps, etc.):
dotnet add package DecSm.Atom.Module.DevopsWorkflows
[BuildDefinition]
public partial class Build : BuildDefinition, IDevopsWorkflows
{
public override IReadOnlyList<WorkflowDefinition> Workflows =>
[
new("ci")
{
Triggers = [new GitPullRequestTrigger { IncludedBranches = ["main"] }],
StepDefinitions = [Targets.Test],
WorkflowTypes = [new DevopsWorkflowType()]
}
];
}
Atom provides several modules for different functionalities:
- DecSm.Atom: Core framework
- DecSm.Atom.Module.Dotnet: .NET tooling support
- DecSm.Atom.Module.GitVersion: GitVersion integration
- DecSm.Atom.Module.GithubWorkflows: GitHub Actions support
- DecSm.Atom.Module.DevopsWorkflows: Azure DevOps support
- DecSm.Atom.Module.AzureKeyVault: Azure Key Vault integration
- DecSm.Atom.Module.AzureStorage: Azure Blob Storage for artifacts
Add modules to your build definition:
[BuildDefinition]
public partial class Build : BuildDefinition,
IGithubWorkflows,
IDotnetPackHelper,
IAzureKeyVault
{
// Your build targets here
}
Atom supports artifact management with automatic upload/download in CI/CD pipelines:
private Target Package => t => t
.ProducesArtifact("MyPackage") // Workflows automatically upload this artifact
.Executes(() => {
// Create your package
return Task.CompletedTask;
});
private Target Deploy => t => t
.ConsumesArtifact(nameof(Package), "MyPackage") // Workflows automatically download this artifact
.Executes(() =>
{
// Deploy the package
});
Atom supports variable management for build parameters and secrets:
[ParamDefinition("my-name", "Name to greet")]
private string? MyName => GetParam(() => MyName);
private Target Info => t => t
.ProducesVariable("MyPackage")
.Executes(() =>
{
// Variable writing is done manually
Services.GetRequiredService<IVariablesHelper>().WriteVariable(nameof(MyName), "Declan");
});
private Target Print => t => t
.ConsumesVariable(nameof(Info), "MyPackage") // Workflows automatically inject this variable
.Executes(() =>
{
// Using the variable
Logger.LogInformation("Hello, {Name}!", MyName);
// output: Hello, Declan!
});
Run builds across multiple configurations:
public override IReadOnlyList<WorkflowDefinition> Workflows =>
[
new("build")
{
StepDefinitions =
[
Targets.Test.WithMatrixDimensions(
new MatrixDimension("os", ["ubuntu-latest", "windows-latest", "macos-latest"])),
],
}
];
Use custom artifact storage backends:
public override IReadOnlyList<WorkflowDefinition> Workflows =>
[
new("build")
{
Options = [UseCustomArtifactProvider.Enabled],
// ... other configuration
}
];
Integrate with Azure Key Vault:
Install the required module for Azure Key Vault:
dotnet add package DecSm.Atom.Module.AzureKeyVault
[BuildDefinition]
public partial class Build : BuildDefinition, IAzureKeyVault
{
[SecretDefinition("my-secret", "Description of the secret")]
private string MySecret => GetSecret(() => MySecret);
}
Check out the sample projects:
- Sample_01_HelloWorld: Basic hello world example
- Sample_02_Params: Parameter usage and configuration
- .NET 8.0 or 9.0
- Visual Studio or VS Code or Rider or any other C# IDE
Atom can build itself!
git clone https://github.com/DecSmith42/atom.git
cd atom
dotnet run --project _atom -- PackAtom
Even faster if you've installed the Atom tool globally:
atom PackAtom
Or the old-fashioned way:
dotnet build _atom/Atom.csproj
dotnet run --project _atom -- TestAtom
Contributions are welcome! Please feel free to submit a Pull Request.
Atom is released under the MIT License. See the LICENSE file for details.