Skip to main content
Version: 3.x

Generate a private CLI integration

ModularPipelines.OptionsGenerator is a .NET tool that generates the same strongly typed options and services used by first-party ModularPipelines CLI packages. It accepts a versioned JSON definition, so private and experimental tools do not need changes in the ModularPipelines repository.

Install and pin the generator

Use a local tool manifest so every developer and CI runner uses the same generator version:

dotnet new tool-manifest
dotnet tool install ModularPipelines.OptionsGenerator --version <version>

Commit .config/dotnet-tools.json. Update the pinned package version deliberately, review the generated diff, and regenerate before merging the update.

The NuGet package follows the ModularPipelines semantic version. The input format is versioned independently by schemaVersion. This release supports schema version 1 and rejects unknown versions rather than interpreting them differently.

Define the private tool

Create tools/private-widget.json:

{
"schemaVersion": 1,
"tool": {
"ownershipId": "private-widget-integration",
"toolName": "private-widget",
"namespacePrefix": "PrivateWidget",
"targetNamespace": "Example.Build.PrivateWidget",
"outputDirectory": "src/Example.Build.PrivateWidget",
"documentationOutputDirectory": null,
"executablePrerequisiteMetadataExemption": "Installation is controlled by the private repository.",
"commands": [
{
"fullCommand": "private-widget deploy",
"commandParts": ["deploy"],
"className": "PrivateWidgetDeployOptions",
"parentClassName": "PrivateWidgetOptions",
"toolNamespacePrefix": "PrivateWidget",
"description": "Deploys a private widget.",
"options": [
{
"switchName": "--environment",
"propertyName": "Environment",
"cSharpType": "string?",
"description": "Deployment environment."
},
{
"switchName": "--dry-run",
"propertyName": "DryRun",
"cSharpType": "bool?",
"isFlag": true,
"valueArity": "none"
}
]
}
]
}
}

ownershipId is an immutable identifier for the integration. Keep it unchanged when renaming the tool, namespace prefix, target namespace, or output directories so the generator can reconcile files it previously owned. Use a different value for every independent definition.

outputDirectory and documentationOutputDirectory are relative to --output-dir. Absolute paths, paths that escape that root, and paths traversing symbolic links or filesystem reparse points are rejected. Generated namespaces, types, properties, methods, and enum names must be valid C# syntax. Set documentationOutputDirectory to null when the integration repository does not want a generated Markdown reference.

The integration project needs a package reference to ModularPipelines:

<ItemGroup>
<PackageReference Include="ModularPipelines" Version="..." />
</ItemGroup>

Generate

Run the tool from any directory:

dotnet tool restore
dotnet tool run modular-pipelines-options -- \
--input tools/private-widget.json \
--output-dir .

The command is deterministic: identical metadata and generator versions produce identical files. Generated C# files, command-coverage manifests, and optional documentation are owned by the JSON definition and generator version. The generator records this set in .modular-pipelines-options/<namespacePrefix>.files, allowing later runs to remove stale generated documentation when its directory or tool name changes. Commit that manifest and the generated files, but do not edit either manually. Change the JSON and rerun instead.

For CI, restore the manifest and run the same command, then fail when git diff --exit-code finds uncommitted output:

- uses: actions/setup-dotnet@v6.0.0
with:
dotnet-version: 10.0.x
- run: dotnet tool restore
- run: >-
dotnet tool run modular-pipelines-options --
--input tools/private-widget.json
--output-dir .
- run: git diff --exit-code

Use --change-manifest <path> when automation needs the exact generated and deleted paths. The existing --tools mode remains reserved for first-party scrapers; it cannot be combined with --input.