Introduction
Invex.StructuredText is a family of strongly-typed C# libraries for generating structured YAML text files — specifically GitHub Actions workflows, Dependabot configurations, and Azure DevOps Pipelines — using a fluent, type-safe API with a unified expression system.
Why generate YAML from C#?
CI/CD configuration files grow quickly. Hand-written YAML suffers from:
- No compile-time validation — typos in keys, indentation mistakes, and invalid values are only discovered when the pipeline runs (or fails to parse).
- No refactoring support — renaming a job, output, or variable means find-and-replace across files and hoping you caught every reference.
- Duplication — YAML has limited reuse mechanisms, so common patterns get copy-pasted between workflows.
- Stringly-typed expressions —
${{ }}and$( )expressions are opaque strings to your editor.
Invex.StructuredText solves these problems by letting you define your pipelines as C# object graphs:
- Full IntelliSense and compile-time validation for every property
- Models closely follow the official schemas (GitHub workflow schema, Dependabot v2 schema, Azure Pipelines YAML schema)
- A single, platform-agnostic expression system that compiles down to GitHub's
${{ }}syntax or Azure DevOps macro/runtime expression syntax - Deterministic, cleanly formatted YAML output that is easy to diff and review
The packages
| Package | What it provides |
|---|---|
Invex.StructuredText |
The core: StructuredTextWriter (indentation-aware text writer) and the TextExpression system |
Invex.StructuredText.GithubActions |
GithubActionWriter, DependabotConfigWriter, GithubExpressionFormatter, and full model types |
Invex.StructuredText.AzureDevopsPipelines |
DevopsPipelineWriter, DevopsExpressionFormatter, and full model types |
Both platform packages depend on the core package, so installing either one gives you everything you need.
A taste
using Invex.StructuredText.GithubActions;
using Invex.StructuredText.GithubActions.GithubActionModel;
var workflow = new GithubAction
{
Name = "CI",
On =
[
new On.Push
{
Branches = ["main"],
BranchesIgnore = null,
Tags = null,
TagsIgnore = null,
Paths = null,
PathsIgnore = null,
},
],
Jobs =
[
new Job
{
Name = new RawExpression("build"),
RunsOn = new() { Labels = [new RawExpression("ubuntu-latest")] },
Steps =
[
new Step.UsesStep { Uses = new RawExpression("actions/checkout@v4") },
new Step.RunStep { Run = [new RawExpression("dotnet build -c Release")] },
],
},
],
};
var writer = new GithubActionWriter();
writer.Write(workflow);
File.WriteAllText(".github/workflows/ci.yml", writer.TextWriter.ToString());
Where to go next
- Getting Started — install the packages and generate your first file
- Expressions — learn the unified expression system
- GitHub Actions — workflow generation in depth
- Dependabot —
dependabot.ymlgeneration - Azure DevOps Pipelines — pipeline generation in depth
- Architecture — how the pieces fit together