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:
@@ -134,6 +134,13 @@ summer admin:create --email admin@example.com --superuser
|
||||
|
||||
One leading `--` is ignored, so `summer serve -- --addr 127.0.0.1:8123` behaves the same as `summer serve --addr 127.0.0.1:8123`.
|
||||
|
||||
The global options go to the binary too, wherever you put them: `summer -v migrate --pretend` runs `./bin/acme -v migrate --pretend`, and `summer -q key:generate` prints only the key. `summer help migrate` shows each delegate's examples; the binary's own help, with every flag, is `summer migrate --help`:
|
||||
|
||||
```sh
|
||||
summer -v migrate --pretend
|
||||
summer admin:token:create jane --scope journal.posts --json
|
||||
```
|
||||
|
||||
When `summer` receives SIGINT or SIGTERM while the binary runs, it passes the signal on and waits for the binary to shut down, so stopping `summer serve` with Ctrl+C or `kill` frees its port.
|
||||
|
||||
`summer` does not rebuild an existing binary before it delegates. Run `summer build` after you change code, or keep `summer dev` running. Every other runtime command, such as `deferred:purge` or the commands your plugins add, is run on the binary directly.
|
||||
|
||||
@@ -12,7 +12,7 @@ The `summer` tool creates and builds applications and generates the files a plug
|
||||
|
||||
| Command | Purpose |
|
||||
|---------|---------|
|
||||
| `summer build` | Reads `summer.yaml`, generates `plugins.gen.go` and `main.go`, and builds the binary into `bin/`, passing the manifest's `build.tags` to `go build` as `-tags`. Run it from the application directory or any directory below it. |
|
||||
| `summer build` | Reads `summer.yaml`, generates `plugins.gen.go` and `main.go`, and builds the binary into `bin/`, passing the manifest's `build.tags` to `go build` as `-tags`. On a terminal a spinner runs while `go build` works; it ends with `Built bin/<binary> in <time>`. Run it from the application directory or any directory below it. |
|
||||
| `summer dev` | Builds the application as `summer build` does (build tags included), runs `bin/<binary> serve`, and rebuilds and restarts it whenever a Go or YAML source, `go.mod`, `go.work`, `.env` or `summer.yaml` changes. A failed build keeps the running server. |
|
||||
|
||||
```sh
|
||||
@@ -119,4 +119,17 @@ File names are the snake-case form of the name: `AddPublishedAt` becomes `add_pu
|
||||
|
||||
After writing the files, every `make:` command regenerates the plugin's `registry.gen.go`, which lists the plugin's models, migrations, commands, jobs and admin controllers, and runs `go mod tidy` in the plugin. The capability methods of a scaffolded `plugin.go` return those generated lists. If your `plugin.go` was written by hand and does not call them, the command prints a note naming the accessors to add.
|
||||
|
||||
A command refuses to overwrite an existing file or to declare a name the package already has.
|
||||
Each command prints `Created <path>` for every file it writes. A command refuses to overwrite an existing file or to declare a name the package already has.
|
||||
|
||||
When a `make:` command cannot do its work, the error says why and what to run instead. Outside an application (no `summer.yaml` in the directory or above it) it reads:
|
||||
|
||||
```text
|
||||
ERROR build: no summer.yaml found from /home/jane
|
||||
|
||||
summer make:model runs inside an application: a directory with summer.yaml, or one below it.
|
||||
|
||||
Try:
|
||||
cd <app>; summer make:model acme.blog Post
|
||||
```
|
||||
|
||||
An invalid plugin ID (`Acme.Blog`), a name that is not an exported Go identifier (`post`), an artifact that already exists and a plugin that `summer.yaml` does not list get the same kind of explanation, with a valid command form to try.
|
||||
|
||||
@@ -101,7 +101,7 @@ SummerCMS acme v1.4.0 (framework v0.4.0)
|
||||
|
||||
The database line shows the host, port and database name, never the user or password. Pending migrations read `3 pending — run bin/acme migrate`. The admin line shows the admin URL, or `off` with the steps to add an admin controller; a warning naming `summer admin:create` follows when the `backend_users` table is empty. `-v` adds one line per plugin with the plugins it requires and its pending migrations, and lets every component's info logs through; `-q` prints no summary, only warnings and errors.
|
||||
|
||||
Off a terminal (systemd, Docker, a redirected log) nothing is printed on stdout: the same facts are logged as one `startup summary` record and one record whose message is exactly `listening on <addr>`, both from the `serve` component, so journald and the daily files keep them and scripts can wait for the second one. On a terminal the two records still reach the daily files, but the console does not repeat them.
|
||||
Off a terminal (systemd, Docker, a redirected log) nothing is printed on stdout: the same facts are logged as one `startup summary` record and one record whose message is exactly `listening on <addr>`, both from the `serve` component, so journald and the daily files keep them and scripts can wait for the second one. On a terminal the console does not repeat the two records after the block; a file channel whose level lets Info through still gets them (the default daily channel keeps only warnings and errors).
|
||||
|
||||
### Shutting down
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ When you port an existing backend to SummerCMS, its real responses are the contr
|
||||
| `summer parity:broadcasts` | `--flow`, `--target`, `--vars`, `--listen` (default `127.0.0.1:8424`), `--out`, `--name`, `--step`, `--ids`, `--rules`, `--api-key`, `--settle` (default `500ms`), `--pending` | Runs a flow against the reference backend with a fake Centrifugo server and records the realtime publications it sends into a golden file. |
|
||||
| `summer parity:upstream` | `--listen` (default `127.0.0.1:8425`), `--ca-dir`, `--out`, `--mode` (default `script`; or `forward`), `--script`, `--vars` | Runs a loopback HTTPS recording proxy with a locally generated CA. The reference backend sends its vendor API calls through it; each call is answered from the `--script` file (or forwarded once to the vendor in `forward` mode) and written, with credentials masked, into an upstream sidecar (`<fixture>.upstream.yaml`) when the command is interrupted. |
|
||||
|
||||
A typical port records once against the reference backend and replays against the Go backend on every change:
|
||||
Each command's help (`summer help parity:record`) explains its place in this workflow and shows examples. A typical port records once against the reference backend and replays against the Go backend on every change:
|
||||
|
||||
```sh
|
||||
summer 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
|
||||
@@ -45,6 +45,6 @@ summer docs:sync
|
||||
summer docs:serve
|
||||
```
|
||||
|
||||
`docs:build` fails, and writes nothing, when a page names an identifier that does not exist, links to a missing page or anchor, shows a command that neither `summer` nor an application binary has, or has a `src=` code block that differs from its source. It also fails on a Go code block (including `golang`) without a `src=` reference, a `src=` code block inside a callout, blockquote or list item (`src=` blocks must be top-level), and a `src=` code block in a module README. A `src=` target must be code `go test ./...` compiles and runs, that is a file in the default build, inside a `Test` function or an `Example` with an `// Output:` comment, or a function one of them calls. After you change code that a page shows, run `summer docs:sync` to refresh the copies.
|
||||
`summer help docs:build` and `summer help docs:sync` show examples. `docs:build` fails, and writes nothing, when a page names an identifier that does not exist, links to a missing page or anchor, shows a command that neither `summer` nor an application binary has, or has a `src=` code block that differs from its source. It also fails on a Go code block (including `golang`) without a `src=` reference, a `src=` code block inside a callout, blockquote or list item (`src=` blocks must be top-level), and a `src=` code block in a module README. A `src=` target must be code `go test ./...` compiles and runs, that is a file in the default build, inside a `Test` function or an `Example` with an `// Output:` comment, or a function one of them calls. After you change code that a page shows, run `summer docs:sync` to refresh the copies.
|
||||
|
||||
`docs:serve` listens only on a loopback address unless you pass `--allow-remote`. Use it to preview search, which browsers block when you open the built files directly from disk.
|
||||
|
||||
@@ -138,6 +138,18 @@ bonfire.Execute(context.Background(), root, []string{"help", "blog:export"})
|
||||
|
||||
`list --format=json` prints every command with its arguments, options (with defaults) and examples as a `bonfire.Listing`, for documentation checks, scripts and agents. `bonfire.Describe` builds the same listing from a command slice.
|
||||
|
||||
### What a finished command has
|
||||
|
||||
The framework's own commands follow these rules, and so should a plugin's:
|
||||
|
||||
- **Description:** one line in the imperative, sentence case, without a trailing period: "Publish a blog post by its slug".
|
||||
- **Long help:** what the command does, when to run it (by hand, from cron, from the scheduler), what each flag changes, and its exit codes.
|
||||
- **Two or three examples:** the common call first, then the flags people reach for.
|
||||
- **A result line:** a one-shot command ends with a line that says what it did, such as `Published hello-world` or `Imported 120 posts (3 skipped)`, and returns an error when it failed, so the exit code is 1.
|
||||
- **Data on stdout:** what a script reads (a token, a key, `--json` output) goes through `Println` or `Printf`, which `-q` does not hide; information and success lines go through `Info` and `Success`, which it does.
|
||||
- **Flags with a meaning people know:** `--json` for output scripts read, `--pretend` or `--dry-run` for a run that changes nothing, `--force` to skip a confirmation. A destructive step asks through `bonfire.ConfirmProceed`, which never asks off an interactive terminal; never give an existing flag a new meaning.
|
||||
- **A named logger:** log through `diary.For("<plugin code>")` from a package variable, not `slog.Default`, so `log.components` can raise or silence the plugin. See [Logging](../services/logging.md).
|
||||
|
||||
## Errors and hints
|
||||
|
||||
When `Run` returns an error, the binary prints a block on stderr: the ERROR badge and the message, then "Run with -v for details.". With `-v` the block lists the wrapped errors under "Caused by:" instead. Messages pass through `sunscreen.Scrub`, so a token inside an error text is not printed.
|
||||
|
||||
Reference in New Issue
Block a user