SaveApis.Framework

Documentation for SaveApis.Framework

View on GitHub

CLI Framework

SaveApis.Framework.CLI provides a lightweight command-line application host built on Spectre.Console.Cli — with service injection, command discovery via attributes, and branch/sub-command support, all wired through the Installer system.


Table of Contents


Overview

When to use: Use the CLI Framework for command-line tools and utilities where users invoke specific commands with arguments.

What it provides out of the box:

Key differences from Web and Console frameworks:


Entry Point

Start the application from Program.cs:

using SaveApis.Framework;
using SaveApis.Framework.Cli.Extensions;

await SaveApisApplication
    .AsCli(
        args,
        (app, installer) =>
        {
            app.Name = "my-tool";           // used as the CLI application name
            app.Namespace = "My.Org";
        }
    )
    .AddInstaller<MyInstaller>()
    .Build()
    .RunAsync()
    .ConfigureAwait(false);

The configure callback is optional. Unlike Web and Console, SetSoftwareAssembly() is not required since the CLI Framework does not scan assemblies for controllers or validators.


Default Installers

The CLI Framework registers no default installers. Every feature — including services, commands, and any middleware — must be added via custom installers.


Commands

CommandAttribute

Every command class must be decorated with [Command] to be registerable. The attribute declares the command name and optional branch path:

[AttributeUsage(AttributeTargets.Class)]
public class CommandAttribute(string name, params string[] branches) : Attribute
Parameter Description
name The command name as typed by the user (e.g. run)
branches Optional path of branch names leading to this command

Simple Commands

A top-level command without settings:

[Command("greet")]
public class GreetCommand(IGreetingService service) : Command
{
    protected override int Execute(CommandContext context, CancellationToken cancellationToken)
    {
        Console.WriteLine(service.GetGreeting());
        return 0;
    }
}

Register it in an installer:

builder.RegisterCommand<GreetCommand>();

Commands with Settings

A command that accepts arguments or options:

[Command("deploy")]
public class DeployCommand : Command<DeploySettings>
{
    protected override int Execute(CommandContext context, DeploySettings settings, CancellationToken cancellationToken)
    {
        Console.WriteLine($"Deploying to {settings.Environment}...");
        return 0;
    }
}

public class DeploySettings : CommandSettings
{
    [CommandArgument(0, "<environment>")]
    public string Environment { get; set; } = string.Empty;
}

Register it in an installer:

builder.RegisterCommand<DeployCommand, DeploySettings>();

Branched Commands

Commands can be nested under branches by specifying the branch path in [Command]:

// Accessible as: my-tool branch1 branch2 test
[Command("test", "branch1", "branch2")]
public class TestCommand(ITestService service) : Command
{
    protected override int Execute(CommandContext context, CancellationToken cancellationToken)
    {
        Console.WriteLine(service.GetMessage());
        return 0;
    }
}

Branches are created automatically if they do not already exist. Multiple commands can share the same branch path.


Custom Installers

Inherit from BaseCliInstaller to create an installer:

public class MyInstaller : BaseCliInstaller
{
    public override string InstallerKey => "My";

    public override void ConfigureBuilder(
        SaveApisCliApplicationBuilder builder,
        CliInstallerContext context)
    {
        // Register DI services
        builder.Services.AddScoped<ITestService, TestService>();

        // Register commands
        builder.RegisterCommand<GreetCommand>();
        builder.RegisterCommand<DeployCommand, DeploySettings>();
    }
}

From the example project:

public class TestInstaller : BaseCliInstaller
{
    public override string InstallerKey => "Test";

    public override void ConfigureBuilder(
        SaveApisCliApplicationBuilder builder,
        CliInstallerContext context)
    {
        builder.Services.AddScoped<ITestService, TestService>();
        builder.RegisterCommand<TestCommand>();
    }
}

For the full installer API (lifecycle, dependencies, ShouldInstall, etc.) see the Base Framework documentation.

Note: Unlike Web and Console, there are no CliInstallerContext extension methods for enabling/disabling built-in features, since the CLI Framework has no default installers.


Configuration

Configuration is loaded from (in order of precedence):

  1. Environment variables
  2. appsettings.json (optional — no error if absent)
Environment Variable Description Default
CLI_ENVIRONMENT Sets the host environment name Production

Use builder.Configuration inside an installer to access configuration values:

public override void ConfigureBuilder(SaveApisCliApplicationBuilder builder, CliInstallerContext context)
{
    var options = GetOption<MyOptions>(builder); // reads section named by InstallerKey
}