![]() |
VOOZH | about |
dotnet add package ModularPipelines.Ftp --version 3.2.8
NuGet\Install-Package ModularPipelines.Ftp -Version 3.2.8
<PackageReference Include="ModularPipelines.Ftp" Version="3.2.8" />
<PackageVersion Include="ModularPipelines.Ftp" Version="3.2.8" />Directory.Packages.props
<PackageReference Include="ModularPipelines.Ftp" />Project file
paket add ModularPipelines.Ftp --version 3.2.8
#r "nuget: ModularPipelines.Ftp, 3.2.8"
#:package ModularPipelines.Ftp@3.2.8
#addin nuget:?package=ModularPipelines.Ftp&version=3.2.8Install as a Cake Addin
#tool nuget:?package=ModularPipelines.Ftp&version=3.2.8Install as a Cake Tool
Write CI/CD pipelines in C#. Debug them locally. Ship with confidence.
👁 Nuget
👁 GitHub Workflow Status (with event)
👁 GitHub last commit (branch)
👁 Codacy Badge
👁 CodeFactor
👁 License
👁 Codacy Badge
👁 codecov
You know the drill. You write some YAML, push it, wait for CI to start, watch it fail on a typo, fix it, push again, wait again. Repeat until you lose the will to live.
YAML pipelines are:
ModularPipelines lets you write your CI/CD pipelines as regular C# code. That means:
Set a breakpoint. Step through your pipeline. Fix it before you push.
[DependsOn<BuildModule>]
[DependsOn<TestModule>]
public class PublishModule : Module<CommandResult>
{
protected override async Task<CommandResult?> ExecuteAsync(IPipelineContext context, CancellationToken cancellationToken)
{
// This is real C#. Set a breakpoint. Inspect variables. Debug locally.
return await context.DotNet().Publish(new DotNetPublishOptions
{
Project = "src/MyApp/MyApp.csproj",
Configuration = Configuration.Release,
Output = "publish/"
}, cancellationToken);
}
}
Intellisense, refactoring, compile-time errors. Your pipeline code gets the same treatment as your application code. Rename a module? Your IDE updates all the references. Typo in an option? Red squiggle before you even save.
Test your entire pipeline on your machine before pushing. No more "let me push and see if it works" commits. Debug failures in your IDE instead of reading logs from a build agent.
Modules declare their dependencies with attributes. ModularPipelines figures out what can run in parallel and maximizes throughput. No more manually orchestrating parallel jobs.
Your pipeline logic lives in C#, not in vendor-specific YAML. Moving from GitHub Actions to Azure Pipelines to TeamCity? Change one line - your modules stay the same.
Inject services, configuration, and secrets the same way you do in ASP.NET Core. Mock dependencies for testing. No more environment variable gymnastics.
Secrets are automatically obfuscated in logs. No more accidentally exposing API keys in build output.
Modules return strongly-typed results that other modules can consume. No shared mutable state - just clean data flow.
// BuildModule returns version info
public class BuildModule : Module<BuildInfo>
{
protected override async Task<BuildInfo?> ExecuteAsync(IModuleContext context, CancellationToken cancellationToken)
{
await context.DotNet().Build(new DotNetBuildOptions { Project = "MyApp.csproj" }, cancellationToken);
return new BuildInfo { Version = "1.0.0", OutputPath = "bin/Release" };
}
}
// PublishModule retrieves and uses it
[DependsOn<BuildModule>]
public class PublishModule : Module
{
protected override async Task ExecuteAsync(IModuleContext context, CancellationToken cancellationToken)
{
var buildResult = await GetModule<BuildModule>();
var outputPath = buildResult.Value!.OutputPath; // Strongly-typed, compile-time checked
// Publish using the build output...
}
}
Built-in Roslyn analyzers catch common mistakes before you even run:
[DependsOn] when calling GetModule<T>()await module resultsConsole.Write instead of the logging systemdotnet new console -n MyPipeline
cd MyPipeline
dotnet add package ModularPipelines
// Program.cs
await PipelineHostBuilder.Create()
.AddModule<BuildModule>()
.AddModule<TestModule>()
.AddModule<PublishModule>()
.ExecutePipelineAsync();
// BuildModule.cs
public class BuildModule : Module<CommandResult>
{
protected override async Task<CommandResult?> ExecuteAsync(IPipelineContext context, CancellationToken cancellationToken)
{
return await context.DotNet().Build(new DotNetBuildOptions
{
Project = "MySolution.sln",
Configuration = Configuration.Release
}, cancellationToken);
}
}
// TestModule.cs
[DependsOn<BuildModule>]
public class TestModule : Module<CommandResult>
{
protected override async Task<CommandResult?> ExecuteAsync(IPipelineContext context, CancellationToken cancellationToken)
{
return await context.DotNet().Test(new DotNetTestOptions
{
Project = "MySolution.sln",
Configuration = Configuration.Release
}, cancellationToken);
}
}
Run it:
dotnet run
That's it. No YAML. No waiting for CI. Just dotnet run and watch your pipeline execute.
See exactly what's happening as your pipeline runs:
Get a clear summary when your pipeline completes:
<img width="444" alt="image" src="https://github.com/thomhurst/ModularPipelines/assets/30480171/8963e891-2c29-4382-9a3e-6ced4daf4d4b">
ModularPipelines has strongly-typed wrappers for the tools you already use:
| Package | Description | Version |
|---|---|---|
| ModularPipelines | Write your pipelines in C#! | 👁 nuget |
| ModularPipelines.AmazonWebServices | Helpers for interacting with Amazon Web Services. | 👁 nuget |
| ModularPipelines.Azure | Helpers for interacting with Azure. | 👁 nuget |
| ModularPipelines.Azure.Pipelines | Helpers for interacting with Azure Pipeline agents. | 👁 nuget |
| ModularPipelines.Chocolatey | Helpers for interacting with the Chocolatey CLI. | 👁 nuget |
| ModularPipelines.Cmd | Helpers for interacting with the Windows cmd process. | 👁 nuget |
| ModularPipelines.Docker | Helpers for interacting with the Docker CLI. | 👁 nuget |
| ModularPipelines.DotNet | Helpers for interacting with dotnet CLI. | 👁 nuget |
| ModularPipelines.Email | Helpers for sending emails. | 👁 nuget |
| ModularPipelines.Ftp | Helpers for downloading and uploading via FTP. | 👁 nuget |
| ModularPipelines.Yarn | Helpers for interacting with Yarn CLI. | 👁 nuget |
| ModularPipelines.Node | Helpers for interacting with node / npm CLI. | 👁 nuget |
| ModularPipelines.Git | Helpers for interacting with git. | 👁 nuget |
| ModularPipelines.GitHub | Helpers for interacting with GitHub Actions build agents. | 👁 nuget |
| ModularPipelines.Google | Helpers for interacting with the Google gcloud CLI. | 👁 nuget |
| ModularPipelines.Helm | Helpers for interacting with Helm CLI. | 👁 nuget |
| ModularPipelines.Kubernetes | Helpers for interacting with kubectl CLI. | 👁 nuget |
| ModularPipelines.MicrosoftTeams | Helpers for sending Microsoft Teams cards. | 👁 nuget |
| ModularPipelines.Slack | Helpers for sending Slack cards. | 👁 nuget |
| ModularPipelines.TeamCity | Helpers for interacting with TeamCity build agents. | 👁 nuget |
| ModularPipelines.Terraform | Helpers for interacting with Terraform CLI. | 👁 nuget |
| ModularPipelines.WinGet | Helpers for interacting with the Windows Package Manager. | 👁 nuget |
| ModularPipelines | Cake | Nuke | |
|---|---|---|---|
| Language | Real C# | C# DSL (scripted) | Real C# |
| Parallelization | Automatic based on dependencies | Manual | Manual |
| Architecture | Separate module classes (SRP) | Single build script | Single build class |
| Dependency Injection | Full Microsoft.Extensions.DI | Limited | Built-in but different |
| Setup | dotnet run |
Requires bootstrapper | Requires global tool |
| Module Communication | Strongly-typed return values | Shared state | Parameters |
ModularPipelines takes a different approach: each unit of work is a self-contained module class. This keeps code organized, makes merge conflicts rare, and lets you test modules in isolation.
DependsOnIf<T>() for dynamic dependency graphsWhile I aim to maintain stability, minor versions may include breaking changes. These will always be documented in release notes.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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. |
This package is not used by any NuGet packages.
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 3.2.8 | 120 | 4/17/2026 |
| 3.1.90 | 138 | 2/7/2026 |
| 3.1.6 | 135 | 1/19/2026 |
| 3.1.5 | 134 | 1/19/2026 |
| 3.1.0 | 138 | 1/18/2026 |
| 3.0.124 | 132 | 1/18/2026 |
| 3.0.86 | 127 | 1/18/2026 |
| 3.0.1 | 140 | 1/17/2026 |
| 3.0.0 | 129 | 1/16/2026 |
| 2.48.531-alpha0001 | 109 | 1/16/2026 |
| 2.48.528-alpha0001 | 117 | 1/16/2026 |
| 2.48.517-alpha0003 | 107 | 1/16/2026 |
| 2.48.499-alpha0001 | 109 | 1/16/2026 |
| 2.48.496-alpha0001 | 112 | 1/16/2026 |
| 2.48.30 | 183 | 11/2/2025 |
| 2.48.29 | 176 | 11/2/2025 |
| 2.48.8 | 237 | 10/30/2025 |
| 2.47.8 | 232 | 8/10/2025 |
| 2.47.0 | 202 | 8/9/2025 |
| 2.46.1 | 229 | 8/8/2025 |