feat: Artisan checklist for application commands, hints and coverage gate

- migrate, migrate:rollback and deferred:purge gain --pretend and --force;
  they and queue:clear confirm only in production on an interactive
  terminal, so deploys over SSH proceed as before
- result lines: Applied N migrations, Rolled back <id> of <plugin>,
  Cleared N jobs from <queue>
- --json for migrate:status, key:generate, route:list, admin:token:create,
  admin:token:list, admin:token:scopes and websockets:health; -q keeps the
  key and the token
- hints for an empty DSN, an unreachable database, an invalid app.key, a
  missing uploads bucket and an application binary run below its root
- flare and websockets:health return bonfire.Reported after printing
- long help and examples for every application command, greeter:hello and
  blog:publish; cmd/summer TestCommandsDocumented fails on any gap
This commit is contained in:
Jakub Zych
2026-10-21 13:16:21 +02:00
parent 168d65cd1b
commit 9d06a78f62
29 changed files with 831 additions and 77 deletions

View File

@@ -337,3 +337,28 @@ func generatedHash(t *testing.T) [32]byte {
}
return sha256.Sum256(buf.Bytes())
}
// TestGreeterDocumented: the greeter plugin's commands carry a description,
// long help, examples and flag descriptions (bonfire.CheckDocs).
func TestGreeterDocumented(t *testing.T) {
cfg, err := compass.Load("config")
if err != nil {
t.Fatalf("Load: %v", err)
}
plugins, err := party.Activate(backpack.New(cfg), []string{"golem15.hello", "golem15.greeter"})
if err != nil {
t.Fatalf("Activate: %v", err)
}
var commands []bonfire.Command
for _, p := range plugins {
if hc, ok := p.(pact.HasCommands); ok && p.ID() == "golem15.greeter" {
commands = append(commands, hc.Commands()...)
}
}
if len(commands) == 0 {
t.Fatal("the greeter plugin has no commands")
}
for _, problem := range bonfire.CheckDocs(commands) {
t.Error(problem)
}
}

View File

@@ -79,6 +79,14 @@ func (p *Plugin) Commands() []bonfire.Command {
return []bonfire.Command{{
Name: "greeter:hello",
Description: "Print the configured application name",
Long: `Prints app.name, golem15.hello.posts_per_page and app.debug from the layered
configuration, then fires, collects and hands a HelloEvent to the event bus
and reports what came back. The example application's tests use it to check
configuration layering and events end to end.`,
Examples: []bonfire.Example{
{Command: "greeter:hello", Description: "Print the configured values"},
{Command: "greeter:hello --env production", Description: "Print the values of the production configuration"},
},
Run: func(ctx context.Context, in bonfire.Input, out bonfire.Output) error {
name := ""
posts := 0