feat(bonfire): global flags, Artisan list and exit codes

- Global options -v/-vv/-vvv, -q, --ansi/--no-ansi, -n and --env on every
  command; PeekGlobals reads them before config loads, OnGlobals hooks
  re-apply them after parsing (diary.ApplyGlobals in the generated main)
- Bare binary, --help and list print the Artisan list with the version
  header from BuildVersion and namespace blocks
- Execute runs the root through fang (no version, man page or completions)
  and returns 0, 1 or 2; Fail renders errors before the root exists
- NewRoot rejects duplicate command names (ErrDuplicateCommand) and flags
  that reuse a global option (ErrReservedFlag)
- Generated main and summer use PeekGlobals, compass.Open with --env,
  diary.FromGlobals and bonfire.Execute; docs checker knows the globals
This commit is contained in:
Jakub Zych
2026-10-21 12:13:23 +02:00
parent b7c07591cb
commit 8d4d7ce7a7
25 changed files with 1158 additions and 154 deletions

View File

@@ -15,49 +15,52 @@ WinterCMS has one console entry point, `php artisan`. SummerCMS has two programs
The application binary is what you deploy, so everything that must run in production, such as migrations and workers, is a command of the binary rather than of `summer`.
Run with no arguments, the binary prints its usage and every command it has, the framework's and those its plugins add; `--help` prints the same:
Run with no arguments, the binary prints the list of every command it has, the framework's and those its plugins add, the way `php artisan` does; `--help` and `list` print the same:
```sh
./bin/acme
./bin/acme --help
./bin/acme list
```
```text
SummerCMS acme v1.4.0 (framework v0.3.2)
Usage:
acme [command]
bin/acme <command> [options] [arguments]
Available Commands:
admin:create Create an activated backend administrator
admin:reset-password Reset a backend administrator password and revoke existing tokens
admin:token:create Create a personal API token for a backend administrator
admin:token:list List personal API tokens for a backend administrator (or all)
admin:token:revoke Revoke a personal API token by id
admin:token:scopes List available personal API token scopes
deferred:purge Delete expired deferred bindings with their pending uploads and created records
greeter:hello Print the configured application name
help Help about any command
key:generate Print a fresh 32-byte base64 app.key
migrate Run plugin migrations in dependency order
migrate:rollback Roll back the last migration of a plugin
migrate:status Show per-plugin migration history
queue:clear Clear all queued jobs, by deleting all pending jobs.
queue:work Run background job workers in the foreground
route:list List registered HTTP routes
schedule:run Run the scheduler in the foreground (or once for system cron)
serve Serve the application HTTP API
Options:
-h, --help Display help for the given command
-q, --quiet Do not output any message
-v|vv|vvv Increase verbosity
--ansi|no-ansi Force (or disable) ANSI output
-n, --no-interaction Do not ask any interactive question
--env The environment the command runs under
Flags:
-h, --help help for acme
Use "acme [command] --help" for more information about a command.
Available commands:
help Display help for a command
list List commands
migrate Run plugin migrations in dependency order
serve Serve the application HTTP API
admin
admin:create Create an activated backend administrator
admin:reset-password Reset a backend administrator password and revoke existing tokens
admin:token:create Create a personal API token for a backend administrator
migrate
migrate:rollback Roll back the last migration of a plugin
migrate:status Show per-plugin migration history
queue
queue:work Run background job workers in the foreground
```
The header names the application, its version and the framework version it was built against (`local` when the framework comes from a local checkout). Commands without a namespace come first, then one block per namespace, the part of the name before the first colon. `summer` with no arguments prints its own list in the same layout.
## Getting help
Both programs list their commands with `--help`, and every command accepts `--help` for its arguments and flags:
Both programs list their commands with no arguments, with `--help` and with `list`, and every command accepts `--help` for its arguments and flags:
```sh
summer --help
summer
summer make:model --help
./bin/acme --help
./bin/acme migrate:rollback --help
@@ -65,6 +68,33 @@ summer make:model --help
A command that `summer` delegates to the application binary, such as `summer serve`, answers `--help` with the binary's own help, so run it inside an application directory. Asking the help command of `summer` about a delegated command instead prints a short description and its common flags without running the binary.
### Global options
Every command of both programs accepts these options, before or after the command name:
| Option | Effect |
|--------|--------|
| `-v`, `-vv`, `-vvv` (`--verbose`) | More output: `-v` shows info logs of every component, `-vv` debug logs, `-vvv` also source locations and SQL traces. |
| `-q`, `--quiet` | Shows only warnings and errors in the console log. Implies `-n`. |
| `--ansi`, `--no-ansi` | Forces or turns off colors and styling; the last one given wins. Without either, colors follow the terminal, `NO_COLOR` and `FORCE_COLOR`. |
| `-n`, `--no-interaction` | Marks the run as not interactive. |
| `--env` | The configuration environment, as `SUMMER_ENV` would set it: `--env staging` or `--env=staging`. |
The application binary reads these options before it loads `config/` and starts plugins, so `--env` picks the configuration and `-vv` applies from the first log line:
```sh
./bin/acme -vv migrate
./bin/acme migrate:status --env staging
```
### Exit codes
| Code | Meaning |
|------|---------|
| `0` | The command succeeded. |
| `1` | The command failed; the error is printed on stderr. |
| `2` | The command was called wrongly: an unknown command or flag, or a wrong number of arguments. |
## Commands that summer delegates
During development you often work from the application directory with `summer` alone. These `summer` commands find the application's `summer.yaml`, build `bin/<binary>` when it does not exist yet, and run the same command of the binary with the same arguments:

View File

@@ -48,7 +48,7 @@ if err := catalog.Call(context.Background(), "blog:publish", args, os.Stdout); e
## 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:`.
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 must be unique: when two commands share a name, or a command is named `list` or `help`, `bonfire.NewRoot` fails with `bonfire.ErrDuplicateCommand` naming the command, and the binary does not start. Keep your commands in your plugin's own namespace, such as `blog:`. The global options are reserved as well: a flag named `verbose`, `quiet`, `ansi`, `no-ansi`, `no-interaction`, `env` or `help`, or with the shorthand `v`, `q`, `n` or `h`, makes `bonfire.NewRoot` fail with `bonfire.ErrReservedFlag`.
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`.