Architecture and configuration
Architecture and configuration
Section titled “Architecture and configuration”Decision
Section titled “Decision”The shared artifact is a .NET tool NuGet package, not a reusable workflow and not an MSBuild SDK. Modular Pipelines is an executable orchestration system, so a tool is its natural package boundary. A tool manifest gives each consumer deterministic version pinning and Renovate/Dependabot-compatible upgrades. It also keeps GitHub Actions as a thin host; the same command runs locally, in GitHub Actions, or in another CI service.
The implementation is the generalized PipelineCLI that originated in sourcegeneratorframework (its most advanced version, including pack validation). It supersedes the earlier Purview.Build modules.
The repository additionally exposes:
- a composite action (
.github/actions/purview-build) that installs a pinnedPurview.Buildversion and runs it, for repositories embedding the build in their own jobs, and - two reusable workflows (
purview-build.yml,purview-release.yml) that wrap that logic with structured inputs/secrets, reducing a consumer to one reusable-workflow job pluspurview-build.json.
An MSBuild SDK remains a possible future companion for shared compile-time properties, analyzers, or package metadata. It should not own CI orchestration.
Ownership boundary
Section titled “Ownership boundary”The package owns module implementation, dependency ordering, safe defaults, secret lookup, NuGet/GitHub integration, and diagnostics. Each repository owns its tool-version pin, paths and discovery patterns, feature switches, and release-mode selection. A project needing truly custom behavior can invoke its own command before/after the shared tool; a generally useful variation should be added as a typed option here.
Configuration reference
Section titled “Configuration reference”| Key | Default | Purpose |
|---|---|---|
LogLevel |
Warning |
Trace/Debug/Information/Warning/Error/Critical/None; used by the pipeline logger |
Solution |
src/Product.slnx |
Solution, project, or directory passed to restore/build/pack |
Configuration |
Release |
.NET configuration |
ArtifactsFolder |
artifacts |
Package output directory |
RunTests |
true |
Enable discovered tests |
TestRoot |
src/tests |
Test discovery root (relative to the repository root) |
TestPatterns |
*Tests.csproj |
Comma-separated project search patterns applied under TestRoot |
TestProjects |
* |
Comma-separated project names/globs to run; * runs all discovered |
TestFramework |
TUnit |
TUnit (tree-node filter) or xUnit (VSTest filter) |
TestFilter |
/*/*/*/*/ |
TUnit tree-node filter or xUnit --filter; empty disables it |
RunLint |
true |
Restore local tools and run CSharpier check |
RunPack |
true |
Enable packing |
ValidatePack |
true |
Enable pack validation |
PackValidation
Section titled “PackValidation”| Key | Default | Purpose |
|---|---|---|
RequireSymbolPackage |
true |
Every .nupkg must have a matching .snupkg and vice versa |
RequireSymbolFiles |
true |
Every .snupkg must contain at least one .pdb |
RequireSourceLink |
false |
Every .dll/.exe must have a matching portable PDB containing a Source Link record |
RequireDeterministic |
false |
Every .dll/.exe must be built deterministically (the PE carries the Reproducible debug directory entry) |
RequiredCompilerFlags |
[] |
Compiler-flag key=value entries that must appear in each assembly’s PDB compiler-flags record (e.g. optimization=release) |
RequiredContent |
{} |
Package-id glob → entry-path globs that must be present in the .nupkg ("*" matches every package) |
ForbiddenContent |
{} |
Package-id glob → entry-path globs that must not be present in the .nupkg ("*" matches every package) |
Content entry paths and package-id keys are matched as globs (case-insensitive), e.g. tools/**/Foo.dll or **/*.pdb. Required content is satisfied when any package entry matches; forbidden content fails when any entry matches. The assembly checks (RequireSourceLink, RequireDeterministic, RequiredCompilerFlags) inspect each .dll/.exe in the .nupkg (PE header) and its sibling portable PDB in the .snupkg (custom debug info records); they only apply to assemblies the package ships symbols for. Determinism is detected via the PE’s Reproducible debug directory entry, source link via the PDB’s Source Link record, and compiler flags via the PDB’s key/value compiler-flags record (matched case-insensitively, e.g. optimization=release).
| Key | Default | Purpose |
|---|---|---|
FeedUrl |
nuget.org v3 | Remote package source |
TrustedPublishing |
false |
Push without an API key (NuGet Trusted Publishing / OIDC) |
APIKey |
unset | Secret; use NUGET_APIKEY or NuGet__ApiKey |
EnvAPIKey |
unset | Binds NuGet__NUGET_APIKEY; also falls back to process env NUGET_APIKEY/NUGET_API_KEY |
PublishLocalNuGet
Section titled “PublishLocalNuGet”| Key | Default | Purpose |
|---|---|---|
LocalFeedPath |
unset | Absolute local package source |
EnvLocalFeedPath |
unset | Binds PublishLocalNuGet__LOCAL_NUGET_FEED_PATH; also falls back to process env LOCAL_NUGET_FEED_PATH |
OverwriteExistingPackages |
true |
Overwrite packages already in the local feed |
ShutdownDotnetBuilderServer |
true |
Shut down the dotnet build server after publishing |
ClearPackageCache |
true |
Clear the local NuGet package caches for the published packages |
GitHub
Section titled “GitHub”| Key | Default | Purpose |
|---|---|---|
AccessToken |
unset | Secret; use GITHUB_TOKEN |
EnvAccessToken |
unset | Binds GitHub__GITHUB_TOKEN; also falls back to process env GITHUB_TOKEN |
ProductHeader |
Purview.Build.Pipeline |
GitHub API product header |
Release
Section titled “Release”| Key | Default | Purpose |
|---|---|---|
Mode |
None |
None, LocalNuGet, NuGet, or GitHubRelease |
UploadArtifacts |
false |
Upload every file in Build:ArtifactsFolder as GitHub release assets |
Project, testing, and release support
Section titled “Project, testing, and release support”- Project types: the pipeline is dotnet-first (libraries, source generators, analyzers, MSBuild SDKs, Aspire hosting extensions). Non-dotnet project types (
Webfor full-stack apps,WebExtensionfor JS/Azure DevOps extensions) are designed as future module additions gated by configuration. - Testing types: TUnit on Microsoft.Testing.Platform (default) and xUnit, both configurable via
TestFramework/TestFilter. Non-dotnet runners (Vitest, Playwright, Jest, Astro) are future modules. - Release types: nuget.org (API key or Trusted Publishing), GitHub Packages internal feed, local NuGet feed, GitHub release (optionally with package/vsix assets), and future Aspire-deploy / Azure DevOps marketplace publishing.
Release behavior
Section titled “Release behavior”None: build/test/pack may run, but nothing publishes.LocalNuGet: pushes packages to the resolved local feed for developer testing. Only honoured when the tool runs locally; it is ignored in CI (Modular Pipelines detects a non-CI environment), so it cannot be driven through the reusable workflows.NuGet: pushes packages to the configured feed and, by default, creates a GitHub release.GitHubRelease: creates a GitHub release (optionally uploadingArtifactsFolderassets) without publishing NuGet packages.
The workflow decides whether a version is eligible to release (for example, only an untagged version on main or release) and sets Release__Mode. Credentials remain CI secrets.
Repository root resolution
Section titled “Repository root resolution”The tool locates the repository root by walking up from the current working directory to the nearest package.json; Environment.CurrentDirectory is set to that root before modules run. MODULAR_PIPELINES_DIRECTORY can override the directory containing appsettings.json when the defaults do not apply.
Command-line overrides use configuration syntax, for example:
dotnet purview-build --Build:TestPatterns=*IntegrationTests.csproj --Build:RunPack=false