-
Notifications
You must be signed in to change notification settings - Fork 0
CLI Reference
flagctl is the SharpNinja Feature Flags command-line tool. It validates feature flag manifests at build time and CI time, checking structure, CEL rule syntax, Ed25519 signature material, product identity, schema version compatibility, and generated accessor/gate bindings.
Install as a global .NET tool:
dotnet tool install --global SharpNinja.FeatureFlags.CliInstall as a local tool in a repository:
dotnet tool install SharpNinja.FeatureFlags.CliAfter installing locally, invoke via:
dotnet flagctl validate flags/flags.jsonVerify the installation:
flagctl --helpflagctl validate <manifest-path> [options]
Validates one JSON feature flag manifest file. The manifest path must point to an existing, readable file (not a directory).
Full usage:
Usage: flagctl validate <manifest-path> [options]
Commands:
validate <manifest-path> Validate one JSON feature-flag manifest.
Options:
--product-id <id> Require the manifest productId to match the build ProductId.
--release-id <id> Require the manifest releaseId to match the build ReleaseId.
--schema-version <value> Supported manifest schema version. Defaults to 1.
--public-key <path> Validate embedded Ed25519 public-key material.
--generated-bindings <path>
Validate generated accessor/gate binding diagnostics input.
-h, --help Show usage.
| Option | Argument | Description |
|---|---|---|
--product-id |
<id> |
When provided, the manifest's productId field must exactly match this value. Emits FFMANIFEST_PRODUCT_MISMATCH on mismatch. |
--release-id |
<id> |
When provided, the manifest's releaseId field must exactly match this value. Emits FFMANIFEST_RELEASE_MISMATCH on mismatch. |
--schema-version |
<integer> |
The highest manifest schema version this tooling version supports. Defaults to 1. Emits FFMANIFEST_SCHEMA_COMPATIBILITY when the manifest requires a higher version. |
--public-key |
<path> |
Path to a file containing a raw 32-byte Ed25519 public key, or a base64-encoded 32-byte Ed25519 public key. Validates the format of the key file. |
--generated-bindings |
<path> |
Path to a generated bindings JSON file (output of the SDK source generator). May be specified multiple times to validate multiple binding files. Emits SNFF0001, SNFF0002, and SNFF0003 diagnostics. |
-h, --help
|
(none) | Print usage and exit with code 0. |
| Code | Meaning |
|---|---|
0 |
Validation passed; the manifest is valid. |
1 |
Validation failed; one or more diagnostic errors were emitted to stderr. |
2 |
Usage error; the command was invoked incorrectly (unknown command, missing manifest path, unreadable file, unknown option). |
| Code | Description | Fix |
|---|---|---|
SNFF0001 |
A generated binding references a flag key that does not exist in the manifest's flags array. |
Add the missing flag to the manifest, or remove the stale binding from the generated bindings file. |
SNFF0002 |
A generated accessor's CLR type or a generated gate's kind does not match the manifest flag type. For example, a gate binding requires a boolean flag but the manifest declares the flag as string. |
Align the flag type in the manifest with the CLR type used in the generated binding, or change the binding kind. |
SNFF0003 |
A generated binding's productId is not listed in the flag's productScope in the manifest. |
Add the product to the flag's productScope in the manifest, or correct the productId in the generated binding. |
| Code | Description | Fix |
|---|---|---|
FFMANIFEST_CEL_SYNTAX |
A targeting rule's when expression is not valid CEL syntax. |
Correct the CEL expression in the manifest. |
FFMANIFEST_PRODUCT_MISMATCH |
The manifest productId does not match --product-id. |
Ensure the manifest is for the correct product, or update --product-id to match. |
FFMANIFEST_RELEASE_MISMATCH |
The manifest releaseId does not match --release-id. |
Regenerate the manifest with the correct release identifier. |
FFMANIFEST_SCHEMA_COMPATIBILITY |
The manifest schemaVersion or compatibility.minimumReaderSchemaVersion exceeds --schema-version. |
Upgrade flagctl to a version that supports the required schema version. |
FFMANIFEST_SIGNATURE_REQUIRED |
The manifest does not contain a signature object. |
Add a signature block (generated by the signing step of the publish workflow). |
FFMANIFEST_SIGNATURE_OBJECT |
The signature field is present but is not a JSON object. |
Fix the manifest structure so signature is an object. |
FFMANIFEST_SIGNATURE_ALGORITHM |
signature.algorithm is not Ed25519. |
Only Ed25519 signatures are supported in v1. |
FFMANIFEST_SIGNATURE_KEYID |
signature.keyId is missing or blank. |
Populate signature.keyId with the identifier of the signing key. |
FFMANIFEST_SIGNATURE_VALUE |
signature.value is not a valid base64-encoded 64-byte Ed25519 signature. |
Re-sign the manifest with a valid Ed25519 private key. |
FFMANIFEST_PUBLIC_KEY_READ |
The file supplied via --public-key could not be read. |
Verify the path and file permissions. |
FFMANIFEST_PUBLIC_KEY_FORMAT |
The public key file does not contain a valid raw or base64-encoded 32-byte Ed25519 key. | Export the Ed25519 public key as raw 32 bytes or as a base64-encoded string. |
FFMANIFEST_FILE_READ |
The manifest file could not be read (permission denied or I/O error). | Verify the path and file permissions. Exits with code 2. |
SNFF_BINDING_JSON_INVALID |
A generated bindings file is not valid JSON. | Regenerate the bindings file, or check for corruption. |
SNFF_BINDING_SCHEMA |
A generated bindings file is missing the required bindings array, or the file could not be read. |
Regenerate the bindings file from the SDK source generator. |
$ flagctl validate flags/flags.json \
--product-id TruckMate \
--release-id 1.2.0 \
--public-key flags/public-key.ed25519
valid: flags/flags.json
Exit code: 0
$ flagctl validate flags/flags.json \
--product-id TruckMate \
--release-id 1.2.0 \
--generated-bindings obj/SharpNinjaFeatureFlagsBindings.json
error FFMANIFEST_SIGNATURE_REQUIRED $.signature: manifest must include a signature object for v1 bundled defaults.
error SNFF0001 obj/SharpNinjaFeatureFlagsBindings.json:$.bindings[2].flagKey: generated binding references unknown flag key 'enable-old-feature'.
error SNFF0003 obj/SharpNinjaFeatureFlagsBindings.json:$.bindings[0].productId: generated binding productId 'DriverMate' is not in productScope for flag 'enable-dark-mode'.
Exit code: 1
$ flagctl
Usage: flagctl validate <manifest-path> [options]
Commands:
validate <manifest-path> Validate one JSON feature-flag manifest.
...
Exit code: 2
$ flagctl validate missing/flags.json
error: manifest file not found: missing/flags.json
Exit code: 2
Run flagctl as a step in your CI pipeline. Exit code 1 causes the pipeline step to fail.
- name: Install flagctl
run: dotnet tool install --global SharpNinja.FeatureFlags.Cli
- name: Validate feature flag manifest
run: |
flagctl validate flags/flags.json \
--product-id "${{ env.PRODUCT_ID }}" \
--release-id "${{ env.RELEASE_ID }}" \
--public-key flags/public-key.ed25519 \
--schema-version 1- script: dotnet tool install --global SharpNinja.FeatureFlags.Cli
displayName: Install flagctl
- script: |
flagctl validate flags/flags.json \
--product-id "$(ProductId)" \
--release-id "$(ReleaseId)" \
--public-key flags/public-key.ed25519
displayName: Validate feature flag manifest
failOnStderr: false # flagctl writes errors to stderr; rely on exit code
flagctlwrites diagnostic errors tostderrand thevalid:confirmation tostdout. SetfailOnStderr: falseand let the exit code govern the step result.
When a project references the SharpNinja.FeatureFlags.Build NuGet package, flagctl is invoked automatically during dotnet build as part of the ValidateSharpNinjaFeatureFlagManifest target. No manual CI step is needed as long as flagctl is on the PATH.
The buildTransitive/SharpNinja.FeatureFlags.Build.targets file shipped with the Build package injects two targets before CoreCompile:
-
ValidateSharpNinjaFeatureFlagBuildProperties- Fails the build ifProductIdorReleaseIdMSBuild properties are missing when a manifest file is present. -
ValidateSharpNinjaFeatureFlagManifest- Callsflagctl validatewith arguments derived from MSBuild properties.
| Property | Default | Description |
|---|---|---|
SharpNinjaFeatureFlagsManifest |
$(MSBuildProjectDirectory)\flags\flags.json |
Path to the manifest file |
SharpNinjaFeatureFlagsPublicKey |
$(MSBuildProjectDirectory)\flags\public-key.ed25519 |
Path to the Ed25519 public key |
SharpNinjaFeatureFlagsValidateOnBuild |
true |
Set to false to disable auto-validation |
SharpNinjaFeatureFlagsCliCommand |
flagctl |
Command used to invoke the CLI |
SharpNinjaFeatureFlagsSchemaVersion |
1 |
Passed to --schema-version
|
SharpNinjaFeatureFlagsRequirePublicKey |
true |
Fails the build if the public key file is missing |
SharpNinjaFeatureFlagsEmitBuildIdentity |
true |
Stamps ProductId and ReleaseId as AssemblyMetadata attributes |
The effective MSBuild invocation is equivalent to:
flagctl validate "flags/flags.json" \
--product-id "$(ProductId)" \
--release-id "$(ReleaseId)" \
--schema-version "1" \
--public-key "flags/public-key.ed25519" \
--generated-bindings "path/to/binding1.json" \
--generated-bindings "path/to/binding2.json"Generated binding files are included by adding items to the SharpNinjaFeatureFlagBinding item group:
<ItemGroup>
<SharpNinjaFeatureFlagBinding Include="$(IntermediateOutputPath)FeatureFlagBindings.json" />
</ItemGroup><PropertyGroup>
<SharpNinjaFeatureFlagsValidateOnBuild>false</SharpNinjaFeatureFlagsValidateOnBuild>
</PropertyGroup>Or on the command line:
dotnet build -p:SharpNinjaFeatureFlagsValidateOnBuild=false