Console
Writing commands
Add console commands to a plugin with bonfire.Command values, arguments, flags, styled output and prompts, and run commands in-process.
On this page
A plugin adds console commands to the application binary the way a WinterCMS plugin calls registerConsoleCommand. In SummerCMS a command is a plain bonfire.Command value, and the plugin returns its commands from pact.HasCommands. summer make:command acme.blog Publish generates a starting point in console/publish.go.
Defining a command#
A bonfire.Command has a name, a description, its positional arguments and flags, and a run function:
- The name is in
namespace:verbform, such asblog:publish. Plugin commands must use this form; only a few framework commands have bare names. - Each
bonfire.Argis a positional argument with a name, a description and aRequiredmarker. The usage line shows required arguments as<name>and optional ones as[name]. - Each
bonfire.Flagis a string flag. Setbonfire.Flag.Barefor a switch such as--dry-runthat storestruewhen given alone, andbonfire.Flag.Repeatablefor a flag that can be given several times. bonfire.Command.Runreceives the context, abonfire.Inputand abonfire.Output.
Read arguments with bonfire.Input.Argument, scalar and bare flags with bonfire.Input.Flag, and repeatable flags with bonfire.Input.Flags, which returns the values in the order given:
publish := bonfire.Command{
Name: "blog:publish",
Description: "Publish a post",
Args: []bonfire.Arg{{Name: "slug", Description: "Post slug", Required: true}},
Flags: []bonfire.Flag{
{Name: "dry-run", Description: "Report without writing", Bare: true},
{Name: "tag", Description: "Tag to add (repeatable)", Repeatable: true},
},
Run: func(ctx context.Context, in bonfire.Input, out bonfire.Output) error {
slug, _ := in.Argument("slug")
dryRun, _ := in.Flag("dry-run")
out.Printf("publish %s, tags %v, dry run %s\n", slug, in.Flags("tag"), dryRun)
return nil
},
}
// The generated main publishes a catalog of every command on the app.
catalog := bonfire.NewCatalog([]bonfire.Command{publish})
args := []string{"hello-world", "--tag", "news", "--tag", "go", "--dry-run"}
if err := catalog.Call(context.Background(), "blog:publish", args, os.Stdout); err != nil {
fmt.Println(err)
}
// Output: publish hello-world, tags [news go], dry run true
Registering commands#
Return the commands from the plugin's Commands method, which implements pact.HasCommands. The generated main appends every plugin's commands after the framework's runtime commands. Command names are not checked for duplicates, so keep your commands in your plugin's own namespace, such as blog:.
A scaffolded plugin's Commands method returns the generated list of everything in console/, so commands created with summer make:command are registered without editing plugin.go.
Output#
bonfire.Output is the console your command writes to. Besides bonfire.Output.Printf and bonfire.Output.Println, it provides:
- status lines:
bonfire.Output.Info,bonfire.Output.Success,bonfire.Output.Warningandbonfire.Output.Error(the last one writes to the error stream); - widgets:
bonfire.Output.Table,bonfire.Output.Spinneraround a function andbonfire.Output.Progressfor a progress bar, which fall back to plain lines when the output is not a terminal; - prompts:
bonfire.Output.Ask,bonfire.Output.Confirm,bonfire.Output.Choiceandbonfire.Output.Secret. Prompts return their defaults when input ends, so a command run from cron or a script never hangs.
Return an error from Run to fail the command. The binary prints it and exits with status 1.
Calling commands in-process#
bonfire.Call runs one command of a slice by name with its arguments and writes the output to any writer, like Artisan::call in Laravel. Tests use it to exercise a command without building a binary:
commands := []bonfire.Command{{
Name: "acme:greet",
Description: "Greet someone by name",
Args: []bonfire.Arg{{Name: "name", Required: true}},
Run: func(ctx context.Context, in bonfire.Input, out bonfire.Output) error {
name, _ := in.Argument("name")
out.Printf("Hello, %s\n", name)
return nil
},
}}
if err := bonfire.Call(context.Background(), commands, "acme:greet", []string{"blog"}, os.Stdout); err != nil {
fmt.Println(err)
}
// Output: Hello, blog
The generated main also publishes the application's complete command list as a bonfire.Catalog on the container. Code outside the command line, such as the scheduler, looks it up and calls commands through bonfire.Catalog.Call, and checks for one with bonfire.Catalog.Has.
Running your command#
After summer build, your command is part of the binary:
./bin/hello greeter:hello
That command comes from the greeter plugin of examples/hello, whose Commands method returns one bonfire.Command.