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:
@@ -31,10 +31,11 @@ func applicationCommands() []bonfire.Command {
|
||||
}
|
||||
|
||||
// TestCommandsDocumented is the framework's documentation coverage gate
|
||||
// (D-17): every framework command has a description, long help, examples
|
||||
// and described arguments and options, and the test fails on any gap.
|
||||
// (D-17): every command of the summer tool (its delegates included) and of
|
||||
// an application binary has a description, long help, examples and
|
||||
// described arguments and options, and the test fails on any gap.
|
||||
func TestCommandsDocumented(t *testing.T) {
|
||||
commands := applicationCommands()
|
||||
commands := append(toolCommands(), applicationCommands()...)
|
||||
for _, problem := range bonfire.CheckDocs(commands) {
|
||||
t.Error(problem)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
@@ -24,6 +24,17 @@ func docsBuildCommand() bonfire.Command {
|
||||
return bonfire.Command{
|
||||
Name: "docs:build",
|
||||
Description: "Build the documentation site",
|
||||
Long: `Checks every page under docs/ and every module README, then writes the static
|
||||
site to site/ (or --out): HTML pages, a raw .md copy of each page, llms.txt,
|
||||
llms-full.txt and the search index. It fails, writing nothing, when a page
|
||||
names an identifier that does not exist, links to a missing page or anchor,
|
||||
shows a command neither summer nor an application binary has, or has a src=
|
||||
code block that differs from its source. --check only reports. Run it from
|
||||
the framework root.`,
|
||||
Examples: []bonfire.Example{
|
||||
{Command: "docs:build --check", Description: "Check the docs and write nothing"},
|
||||
{Command: "docs:build --base-url /docs/ --out /tmp/site", Description: "Build the site for /docs/ into /tmp/site"},
|
||||
},
|
||||
Flags: []bonfire.Flag{
|
||||
{Name: "root", Description: "Repository root; src= paths and modules/ resolve against it", Default: "."},
|
||||
{Name: "src", Description: "Docs source directory (default <root>/docs)"},
|
||||
@@ -63,6 +74,14 @@ func docsSyncCommand() bonfire.Command {
|
||||
return bonfire.Command{
|
||||
Name: "docs:sync",
|
||||
Description: "Rewrite src= code blocks from their sources",
|
||||
Long: `Rewrites every code block that has a src= reference with the current text of
|
||||
its source (a file, or a function, test or example in it), so the docs show
|
||||
what the code does. Run it after changing code a page shows; docs:build
|
||||
--check fails until you do.`,
|
||||
Examples: []bonfire.Example{
|
||||
{Command: "docs:sync", Description: "Refresh every src= block of the docs in this repository"},
|
||||
{Command: "docs:sync --root ../summercms.go", Description: "Refresh the docs of another checkout"},
|
||||
},
|
||||
Flags: []bonfire.Flag{
|
||||
{Name: "root", Description: "Repository root; src= paths resolve against it", Default: "."},
|
||||
{Name: "src", Description: "Docs source directory (default <root>/docs)"},
|
||||
@@ -174,11 +193,11 @@ func docsCommands() *docsite.Commands {
|
||||
}
|
||||
|
||||
// reportDocsProblems prints one line per problem and a summary, and returns
|
||||
// a short error so the binary exits 1.
|
||||
// a Reported error, so the binary exits 1 without repeating the list.
|
||||
func reportDocsProblems(out bonfire.Output, cmd string, problems []docsite.Problem) error {
|
||||
for _, p := range problems {
|
||||
out.Printf("%s\n", p)
|
||||
}
|
||||
out.Printf("%s: %d problems, nothing written\n", cmd, len(problems))
|
||||
return errors.New(cmd + " failed")
|
||||
return bonfire.Reported(fmt.Errorf("%s failed", cmd))
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
@@ -54,12 +55,39 @@ func buildCommand() bonfire.Command {
|
||||
return bonfire.Command{
|
||||
Name: "build",
|
||||
Description: "Generate plugin imports and build the app binary",
|
||||
Long: `Reads summer.yaml of the application in the current directory (or the nearest
|
||||
one above it), generates plugins.gen.go and main.go from it, and runs go build
|
||||
into bin/<binary>, passing the manifest's build.tags as -tags. On a terminal a
|
||||
spinner runs while go build works. It ends with "Built bin/<binary> in <time>";
|
||||
a compile error is printed and summer exits 1. Do not edit the generated
|
||||
files: every build rewrites them.`,
|
||||
Examples: []bonfire.Example{
|
||||
{Command: "build", Description: "Regenerate main.go and build bin/<binary>"},
|
||||
{Command: "build -q", Description: "Build and print only errors"},
|
||||
},
|
||||
Run: func(ctx context.Context, in bonfire.Input, out bonfire.Output) error {
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return build.App(ctx, dir, out)
|
||||
var log bytes.Buffer
|
||||
run := func() error { return build.App(ctx, dir, &log) }
|
||||
if bonfire.IsTerminal(out) {
|
||||
err = out.Spinner("Building", run)
|
||||
} else {
|
||||
err = run()
|
||||
}
|
||||
for _, line := range strings.SplitAfter(log.String(), "\n") {
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
if rest, ok := strings.CutPrefix(line, "built "); ok && err == nil {
|
||||
out.Success("Built bin/" + strings.TrimSuffix(rest, "\n"))
|
||||
continue
|
||||
}
|
||||
out.Printf("%s", line)
|
||||
}
|
||||
return err
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -68,6 +96,15 @@ func makePluginCommand() bonfire.Command {
|
||||
return bonfire.Command{
|
||||
Name: "make:plugin",
|
||||
Description: "Scaffold a compiling plugin module from a vendor.plugin id",
|
||||
Long: `Creates plugins/<plugin> in the current application: a Go module with plugin.go
|
||||
(the plugin ID, Register and Boot) and go.mod, with a replace for a local
|
||||
framework checkout when the application uses one. Register it with plugin:add
|
||||
so the next summer build compiles it in. Run it inside an application; the
|
||||
directory must not exist yet.`,
|
||||
Examples: []bonfire.Example{
|
||||
{Command: "make:plugin acme.blog", Description: "Create plugins/blog with the ID acme.blog"},
|
||||
{Command: "plugin:add plugins/blog", Description: "Then register it in summer.yaml, go.mod and go.work"},
|
||||
},
|
||||
Args: []bonfire.Arg{{
|
||||
Name: "id",
|
||||
Description: "Plugin ID in vendor.plugin form",
|
||||
@@ -86,7 +123,7 @@ func makePluginCommand() bonfire.Command {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.Printf("created %s\n", created)
|
||||
out.Success("Created " + created)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@@ -96,6 +133,17 @@ func makeModelCommand() bonfire.Command {
|
||||
return bonfire.Command{
|
||||
Name: "make:model",
|
||||
Description: "Generate a GORM model and create-table migration",
|
||||
Long: `Writes models/<name>.go with a GORM model and, unless --no-migration, a
|
||||
create-table migration updates/<timestamp>_create_<table>.go, in the plugin
|
||||
with this ID (or the plugin whose directory you are in, when the ID is left
|
||||
out). The table is the plugin ID and the plural name in snake case, such as
|
||||
acme_blog_posts. The plugin's registry.gen.go lists the new files, so the
|
||||
plugin picks them up on the next build. Existing files and names are never
|
||||
overwritten.`,
|
||||
Examples: []bonfire.Example{
|
||||
{Command: "make:model acme.blog Post", Description: "A Post model and its create-table migration"},
|
||||
{Command: "make:model Post --no-migration", Description: "Inside plugins/blog: only the model"},
|
||||
},
|
||||
Args: makeArtifactArgs(),
|
||||
Flags: []bonfire.Flag{{
|
||||
Name: "no-migration",
|
||||
@@ -124,6 +172,15 @@ func makeMigrationCommand() bonfire.Command {
|
||||
return bonfire.Command{
|
||||
Name: "make:migration",
|
||||
Description: "Generate an ordered gormigrate stub",
|
||||
Long: `Writes updates/<timestamp>_<name>.go with an empty gormigrate migration (up
|
||||
and down steps to fill in) in the plugin with this ID or the plugin whose
|
||||
directory you are in. The 14-digit timestamp keeps migrations in the order you
|
||||
created them. registry.gen.go adds it to the plugin's migrations; run migrate
|
||||
to apply it.`,
|
||||
Examples: []bonfire.Example{
|
||||
{Command: "make:migration acme.blog AddSlugToPosts", Description: "An empty migration in acme.blog"},
|
||||
{Command: "make:migration AddSlugToPosts", Description: "The same from inside plugins/blog"},
|
||||
},
|
||||
Args: makeArtifactArgs(),
|
||||
Run: func(ctx context.Context, in bonfire.Input, out bonfire.Output) error {
|
||||
dir, err := os.Getwd()
|
||||
@@ -147,6 +204,16 @@ func makeCommandCommand() bonfire.Command {
|
||||
return bonfire.Command{
|
||||
Name: "make:command",
|
||||
Description: "Generate a plugin console command",
|
||||
Long: `Writes console/<name>.go with a bonfire.Command named <plugin>:<name> in the
|
||||
plugin with this ID or the plugin whose directory you are in; the generated
|
||||
registry adds it to the commands the plugin returns. The generated function
|
||||
takes no arguments, so a command that needs the database or another service
|
||||
takes a parameter instead and is returned from the plugin's Commands by hand
|
||||
(see the porting guide's blog:publish).`,
|
||||
Examples: []bonfire.Example{
|
||||
{Command: "make:command acme.blog Publish", Description: "A blog:publish command in acme.blog"},
|
||||
{Command: "make:command Publish", Description: "The same from inside plugins/blog"},
|
||||
},
|
||||
Args: makeArtifactArgs(),
|
||||
Run: func(ctx context.Context, in bonfire.Input, out bonfire.Output) error {
|
||||
dir, err := os.Getwd()
|
||||
@@ -170,6 +237,14 @@ func makeJobCommand() bonfire.Command {
|
||||
return bonfire.Command{
|
||||
Name: "make:job",
|
||||
Description: "Generate a plugin job without importing River",
|
||||
Long: `Writes jobs/<name>.go with a queued job built with conga.Job (no River import)
|
||||
in the plugin with this ID or the plugin whose directory you are in; the
|
||||
generated registry adds it to the jobs the plugin returns. Dispatch it with
|
||||
conga.Manager.Dispatch; queue:work or serve runs it.`,
|
||||
Examples: []bonfire.Example{
|
||||
{Command: "make:job acme.blog SendDigest", Description: "A SendDigest job in acme.blog"},
|
||||
{Command: "make:job SendDigest", Description: "The same from inside plugins/blog"},
|
||||
},
|
||||
Args: makeArtifactArgs(),
|
||||
Run: func(ctx context.Context, in bonfire.Input, out bonfire.Output) error {
|
||||
dir, err := os.Getwd()
|
||||
@@ -193,6 +268,16 @@ func makeAdminControllerCommand() bonfire.Command {
|
||||
return bonfire.Command{
|
||||
Name: "make:admin-controller",
|
||||
Description: "Generate an admin controller with fields.yaml and columns.yaml",
|
||||
Long: `Writes controllers/<name>.go, its config_form.yaml and config_list.yaml, and
|
||||
starter fields.yaml and columns.yaml under models/<name>/, in the plugin with
|
||||
this ID or the plugin whose directory you are in. The controller requires the
|
||||
permission <plugin>.access_<name>, which you declare in the plugin's
|
||||
Permissions(), and its NewRecord returns nil until you return your model, so
|
||||
the screens stay closed until you finish it.`,
|
||||
Examples: []bonfire.Example{
|
||||
{Command: "make:admin-controller acme.blog Posts", Description: "A Posts controller in acme.blog"},
|
||||
{Command: "make:admin-controller Posts", Description: "The same from inside plugins/blog"},
|
||||
},
|
||||
Args: makeArtifactArgs(),
|
||||
Run: func(ctx context.Context, in bonfire.Input, out bonfire.Output) error {
|
||||
dir, err := os.Getwd()
|
||||
@@ -246,7 +331,7 @@ func flagTrue(in bonfire.Input, name string) bool {
|
||||
|
||||
func printArtifact(out bonfire.Output, result build.ArtifactResult) error {
|
||||
for _, path := range result.Files {
|
||||
out.Printf("created %s\n", path)
|
||||
out.Success("Created " + path)
|
||||
}
|
||||
if result.Hint != "" {
|
||||
out.Printf("%s\n", result.Hint)
|
||||
@@ -287,6 +372,15 @@ func addPluginCommand() bonfire.Command {
|
||||
return bonfire.Command{
|
||||
Name: "plugin:add",
|
||||
Description: "Register a local plugin module in the app manifest and workspace",
|
||||
Long: `Adds a plugin that already exists on disk (a directory with plugin.go and
|
||||
go.mod, for example one made by make:plugin or a shared plugin checked out as
|
||||
a git submodule) to the application: its ID and module go into summer.yaml, a
|
||||
require and a local replace into the application's go.mod, and the directory
|
||||
into go.work. Run summer build afterwards. A plugin ID or module that is
|
||||
already registered is an error.`,
|
||||
Examples: []bonfire.Example{
|
||||
{Command: "plugin:add plugins/acme/blog", Description: "Register the plugin in plugins/acme/blog"},
|
||||
},
|
||||
Args: []bonfire.Arg{{
|
||||
Name: "dir",
|
||||
Description: "Local plugin directory",
|
||||
@@ -304,7 +398,7 @@ func addPluginCommand() bonfire.Command {
|
||||
if err := build.AddPlugin(ctx, dir, pluginDir); err != nil {
|
||||
return err
|
||||
}
|
||||
out.Printf("added %s\n", pluginDir)
|
||||
out.Success("Added " + pluginDir + " to summer.yaml, go.mod and go.work; run summer build")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
@@ -314,6 +408,16 @@ func newCommand() bonfire.Command {
|
||||
return bonfire.Command{
|
||||
Name: "new",
|
||||
Description: "Create a SummerCMS application in sm-<name>-app",
|
||||
Long: `Creates sm-<name>-app in the current directory: summer.yaml, go.mod and go.work
|
||||
(pointing at a local framework checkout found next to it, or --framework),
|
||||
config/ including config/log.yaml, .env with a fresh app.key and
|
||||
.env.example, compose.yaml for PostgreSQL, a starter plugin (unless
|
||||
--no-plugin) and a git repository (unless --no-git). It prints the next steps:
|
||||
start the database, summer build, summer migrate and summer serve.`,
|
||||
Examples: []bonfire.Example{
|
||||
{Command: "new acme", Description: "Create sm-acme-app with a starter plugin"},
|
||||
{Command: "new acme --no-plugin --no-git", Description: "An empty application without a git repository"},
|
||||
},
|
||||
Args: []bonfire.Arg{{
|
||||
Name: "name",
|
||||
Description: "Application name: lower-case letters and digits, starting with a letter; not a Go keyword, main or internal",
|
||||
|
||||
@@ -18,6 +18,16 @@ func parityProxyCommand() bonfire.Command {
|
||||
return bonfire.Command{
|
||||
Name: "parity:proxy",
|
||||
Description: "Record named HTTP sessions through a loopback reverse proxy",
|
||||
Long: `Runs a recording reverse proxy on a loopback address in front of the reference
|
||||
backend (--upstream). Point a real client (the frontend, an MCP server) at it
|
||||
and use it: each named session, taken from the X-Parity-Session request header
|
||||
or --session, is written as one fixture under --fixtures (nuxt/ or mcp/).
|
||||
Captured secrets go to the --vars file (mode 0600), never into fixtures.
|
||||
Existing fixtures are kept unless --update is true. Ctrl+C stops it.`,
|
||||
Examples: []bonfire.Example{
|
||||
{Command: "parity:proxy --upstream http://127.0.0.1:8000 --fixtures testdata/parity --vars /tmp/parity/vars.yaml", Description: "Record sessions against a reference backend on port 8000"},
|
||||
{Command: "parity:proxy --session checkout --rules testdata/parity/rules.yaml --vars /tmp/parity/vars.yaml", Description: "Record everything as the checkout session with capture rules"},
|
||||
},
|
||||
Flags: []bonfire.Flag{
|
||||
{Name: "listen", Description: "Loopback bind address", Default: tide.DefaultListen},
|
||||
{Name: "upstream", Description: "Fixed loopback HTTP origin", Default: tide.DefaultUpstream},
|
||||
@@ -35,6 +45,16 @@ func parityRecordCommand() bonfire.Command {
|
||||
return bonfire.Command{
|
||||
Name: "parity:record",
|
||||
Description: "Record HTTP responses for a one-flow YAML spec",
|
||||
Long: `Sends the requests of a YAML spec (--spec) to the reference backend (--target)
|
||||
and records the responses as a fixture (--output). With --manifest it instead
|
||||
records the cases of a route manifest that have no fixture yet, at most
|
||||
--next-batch (15) per run; --resume skips valid existing fixtures. Captured
|
||||
secrets go to the --vars file. The true/false flags take the words true or
|
||||
false.`,
|
||||
Examples: []bonfire.Example{
|
||||
{Command: "parity:record --spec testdata/parity/posts.spec.yaml --target http://127.0.0.1:8000 --output testdata/parity/posts.yaml --vars /tmp/parity/vars.yaml", Description: "Record one flow"},
|
||||
{Command: "parity:record --manifest testdata/parity/routes.yaml --fixtures testdata/parity --target http://127.0.0.1:8000 --vars /tmp/parity/vars.yaml --next-batch 10", Description: "Record the next ten unrecorded routes of a manifest"},
|
||||
},
|
||||
Flags: []bonfire.Flag{
|
||||
{Name: "spec", Description: "YAML request spec path"},
|
||||
{Name: "target", Description: "Base URL of the HTTP backend"},
|
||||
@@ -57,6 +77,15 @@ func parityReplayCommand() bonfire.Command {
|
||||
return bonfire.Command{
|
||||
Name: "parity:replay",
|
||||
Description: "Replay recorded fixtures against an HTTP backend",
|
||||
Long: `Replays recorded fixtures (a file or a directory, --fixtures) against a backend,
|
||||
normally the port (--target), and reports each difference after masking IDs
|
||||
and timestamps. --self-check fails on any mismatch; with --manifest and
|
||||
--require-recorded a required case without a fixture fails too. It exits 1
|
||||
when a fixture does not match.`,
|
||||
Examples: []bonfire.Example{
|
||||
{Command: "parity:replay --fixtures testdata/parity --target http://127.0.0.1:8080 --vars /tmp/parity/vars.yaml", Description: "Replay every fixture against the port"},
|
||||
{Command: "parity:replay --fixtures testdata/parity/posts.yaml --target http://127.0.0.1:8000 --self-check true", Description: "Check a fixture against the reference backend itself"},
|
||||
},
|
||||
Flags: []bonfire.Flag{
|
||||
{Name: "fixtures", Description: "Recorded YAML fixture path or directory"},
|
||||
{Name: "target", Description: "Base URL of the HTTP backend"},
|
||||
@@ -73,6 +102,17 @@ func parityBroadcastsCommand() bonfire.Command {
|
||||
return bonfire.Command{
|
||||
Name: "parity:broadcasts",
|
||||
Description: "Record the Centrifugo publications a reference backend sends during a flow into a golden",
|
||||
Long: `Starts a fake Centrifugo server on a loopback address (--listen), runs the flow
|
||||
(--flow) against the reference backend (--target), which must be configured
|
||||
to publish to it, and writes the publications it received into a golden file
|
||||
(--out). --step records only one step, earlier steps running as setup; --ids
|
||||
replaces id:* variables with placeholders; --settle waits for late
|
||||
publications after each step; --pending marks the golden as recorded but not
|
||||
yet asserted.`,
|
||||
Examples: []bonfire.Example{
|
||||
{Command: "parity:broadcasts --flow testdata/parity/chat.flow.yaml --target http://127.0.0.1:8000 --vars /tmp/parity/vars.yaml --out testdata/parity/chat.broadcasts.yaml", Description: "Record the publications of a chat flow"},
|
||||
{Command: "parity:broadcasts --flow testdata/parity/chat.flow.yaml --target http://127.0.0.1:8000 --vars /tmp/parity/vars.yaml --out testdata/parity/send.broadcasts.yaml --step send --ids id:thread", Description: "Record one step with the thread id as a placeholder"},
|
||||
},
|
||||
Flags: []bonfire.Flag{
|
||||
{Name: "flow", Description: "YAML flow spec to run against the target"},
|
||||
{Name: "target", Description: "Loopback base URL of the reference backend"},
|
||||
@@ -95,6 +135,16 @@ func parityUpstreamCommand() bonfire.Command {
|
||||
return bonfire.Command{
|
||||
Name: "parity:upstream",
|
||||
Description: "Record the vendor HTTPS calls a reference backend sends through a loopback recording proxy into an upstream sidecar",
|
||||
Long: `Runs a loopback HTTPS recording proxy (--listen) with a locally generated CA
|
||||
kept in --ca-dir. Configure the reference backend to send its vendor API calls
|
||||
(payments, mail, maps) through it and to trust the CA. In script mode each
|
||||
call is answered from --script; in forward mode it is sent once to the real
|
||||
vendor. When interrupted with Ctrl+C it writes the calls, credentials masked
|
||||
with the --vars values, into the upstream sidecar --out.`,
|
||||
Examples: []bonfire.Example{
|
||||
{Command: "parity:upstream --ca-dir /tmp/parity/ca --out testdata/parity/pay.upstream.yaml --script testdata/parity/pay.script.yaml --vars /tmp/parity/vars.yaml", Description: "Answer vendor calls from a script"},
|
||||
{Command: "parity:upstream --ca-dir /tmp/parity/ca --out testdata/parity/pay.upstream.yaml --mode forward --vars /tmp/parity/vars.yaml", Description: "Forward each call once to the real vendor"},
|
||||
},
|
||||
Flags: []bonfire.Flag{
|
||||
{Name: "listen", Description: "Loopback address of the recording proxy", Default: tide.DefaultUpstreamProxyListen},
|
||||
{Name: "ca-dir", Description: "Directory of the local parity CA, outside the fixtures tree (key kept mode 0600)"},
|
||||
|
||||
@@ -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 + "."
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user