feat(summer): help, examples and hints for every tool command

- build runs go build under a spinner on a terminal and ends with
  'Built bin/<name> in <time>'; make:* print 'Created <path>' per file
- make:*, plugin:add, new, parity:*, docs:build, docs:sync and every
  delegate get long help and examples; delegates note that global options
  pass through to the application binary
- make:* failures (no summer.yaml, invalid plugin ID or name, existing
  artifact, unknown plugin) explain themselves and suggest a command
- docs:build and docs:sync return bonfire.Reported after the problem list
- TestCommandsDocumented covers the tool commands too
- docs: Upgrading to 0.4, delegates and global options, make: results and
  hints, the checklist for a finished command
This commit is contained in:
Jakub Zych
2026-10-21 13:25:53 +02:00
parent 9d06a78f62
commit 33460607db
14 changed files with 385 additions and 50 deletions

View File

@@ -21,29 +21,65 @@ import (
var appRunner = runAppBinary
// delegateCommands returns every command summer runs on the app binary. One
// table is the single source: name, base description and a hint naming the
// common flags or arguments the app binary accepts.
// table is the single source: name, base description, a hint naming the
// common flags or arguments the app binary accepts, and two examples.
func delegateCommands() []bonfire.Command {
rows := []struct{ name, description, hint string }{
{"migrate", "Run plugin migrations in the app binary", ""},
{"migrate:rollback", "Roll back the last migration of a plugin in the app binary", "--plugin <id>"},
{"migrate:status", "Show per-plugin migration history in the app binary", ""},
{"serve", "Run the app HTTP server", "--addr <host:port> (default :8080)"},
{"queue:work", "Run background job workers in the app binary", "--queue <name> (repeatable)"},
{"queue:clear", "Clear pending queued jobs in the app binary", `[queue] argument (default "default")`},
{"schedule:run", "Run the scheduler in the app binary (or once for system cron)", "--once"},
{"route:list", "List the app's routes from the app binary", ""},
{"key:generate", "Print a fresh application key from the app binary", ""},
{"admin:create", "Create a backend administrator in the app binary", "--email <email> (required), --login, --role <code>, --superuser; the password is prompted or read from stdin"},
{"admin:reset-password", "Reset a backend administrator's password in the app binary", "<identifier> (login or email); the password is prompted or read from stdin"},
{"admin:token:create", "Create a personal API token for a backend administrator in the app binary", "<identifier> --name <text> --scope <name> (repeatable) [--expires 30d|90d|365d|never|YYYY-MM-DD]"},
{"admin:token:list", "List personal API tokens in the app binary", "[<identifier>] [--revoked]"},
{"admin:token:revoke", "Revoke a personal API token by id in the app binary", "<id>"},
{"admin:token:scopes", "List available personal API token scopes in the app binary", ""},
ex := func(cmd, desc string) bonfire.Example { return bonfire.Example{Command: cmd, Description: desc} }
rows := []struct {
name, description, hint string
examples []bonfire.Example
}{
{"migrate", "Run plugin migrations in the app binary", "--pretend, --force", []bonfire.Example{
ex("migrate", "Apply every pending migration"),
ex("migrate --pretend", "List the pending migrations without applying them")}},
{"migrate:rollback", "Roll back the last migration of a plugin in the app binary", "--plugin <id>, --pretend, --force", []bonfire.Example{
ex("migrate:rollback --plugin acme.blog", "Roll back the last migration of acme.blog"),
ex("migrate:rollback --plugin acme.blog --pretend", "Name the migration that would roll back")}},
{"migrate:status", "Show per-plugin migration history in the app binary", "--json", []bonfire.Example{
ex("migrate:status", "Show the applied migrations per plugin"),
ex("migrate:status --json", "Machine-readable status")}},
{"serve", "Run the app HTTP server", "--addr <host:port> (default :8080)", []bonfire.Example{
ex("serve", "Serve on port 8080"),
ex("serve --addr 127.0.0.1:8123", "Serve on loopback port 8123")}},
{"queue:work", "Run background job workers in the app binary", "--queue <name> (repeatable)", []bonfire.Example{
ex("queue:work", "Work every known queue"),
ex("queue:work --queue mail", "Work only the mail queue")}},
{"queue:clear", "Clear pending queued jobs in the app binary", `[queue] argument (default "default"), --force`, []bonfire.Example{
ex("queue:clear", "Clear the default queue"),
ex("queue:clear imports --force", "Clear the imports queue without the production question")}},
{"schedule:run", "Run the scheduler in the app binary (or once for system cron)", "--once", []bonfire.Example{
ex("schedule:run", "Run the scheduler in the foreground"),
ex("schedule:run --once", "Run the entries due now and exit")}},
{"route:list", "List the app's routes from the app binary", "--json", []bonfire.Example{
ex("route:list", "Show every route"),
ex("route:list --json", "Machine-readable route list")}},
{"key:generate", "Print a fresh application key from the app binary", "--json", []bonfire.Example{
ex("key:generate", "Print a new key"),
ex("key:generate -q", "Print only the key")}},
{"admin:create", "Create a backend administrator in the app binary", "--email <email> (required), --login, --role <code>, --superuser; the password is prompted or read from stdin", []bonfire.Example{
ex("admin:create --email admin@example.com --superuser", "Create a superuser; the password is asked for"),
ex("admin:create --email editor@example.com --role editor", "Create an administrator with a role")}},
{"admin:reset-password", "Reset a backend administrator's password in the app binary", "<identifier> (login or email); the password is prompted or read from stdin", []bonfire.Example{
ex("admin:reset-password admin@example.com", "Reset a password; it is asked for"),
ex("admin:reset-password admin", "Reset by login")}},
{"admin:token:create", "Create a personal API token for a backend administrator in the app binary", "<identifier> --name <text> --scope <name> (repeatable) [--expires 30d|90d|365d|never|YYYY-MM-DD] [--json]", []bonfire.Example{
ex("admin:token:create jane --name agent --scope journal.posts", "A token limited to one scope"),
ex("admin:token:create jane --scope journal.posts --json", "The token and its details as JSON")}},
{"admin:token:list", "List personal API tokens in the app binary", "[<identifier>] [--revoked] [--json]", []bonfire.Example{
ex("admin:token:list", "Every active token"),
ex("admin:token:list jane --revoked", "Every token of one administrator")}},
{"admin:token:revoke", "Revoke a personal API token by id in the app binary", "<id>", []bonfire.Example{
ex("admin:token:revoke 12", "Revoke token 12"),
ex("admin:token:revoke 7 -q", "Revoke token 7 and print nothing but errors")}},
{"admin:token:scopes", "List available personal API token scopes in the app binary", "--json", []bonfire.Example{
ex("admin:token:scopes", "Show every scope"),
ex("admin:token:scopes --json", "Machine-readable scope list")}},
}
cmds := make([]bonfire.Command, 0, len(rows))
for _, r := range rows {
cmds = append(cmds, delegateCommand(r.name, r.description, r.hint))
c := delegateCommand(r.name, r.description, r.hint)
c.Examples = r.examples
cmds = append(cmds, c)
}
return cmds
}
@@ -53,7 +89,8 @@ func delegateCommands() []bonfire.Command {
func delegateCommand(name, description, hint string) bonfire.Command {
long := description + ".\n\nsummer runs bin/<binary> " + name +
" in the application directory, building the binary first when it is missing. Every flag and argument after " +
name + ", including --help, is passed to the app binary unchanged."
name + ", including --help, is passed to the app binary unchanged, and so are the global options (-v, -q, --ansi, --no-ansi, -n, --env). " +
"summer fails when the app binary fails."
if hint != "" {
long += "\n\nCommon flags: " + hint + "."
}