Files
summercms/docs/console/setup-and-maintenance.md
Jakub Zych 9d06a78f62 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
2026-10-21 13:16:21 +02:00

12 KiB

title, description, section, order
title description section order
Setup and maintenance Every runtime command of an application binary, with its flags and purpose, from migrations and the server to workers, admin accounts and realtime checks. console 20

Setup and maintenance

Every application binary carries the framework's runtime commands. The examples use a binary named acme; yours is named by binary: in summer.yaml.

Migrations

Command Flags Purpose
migrate --pretend, --force Runs the framework migrations, then every plugin's migrations in dependency order, printing how many each set applied and Applied N migrations (or Nothing to migrate). --pretend lists the pending migrations as <plugin>: <id> and applies nothing.
migrate:rollback --plugin <id>, --pretend, --force Rolls back the last migration of the plugin; without the flag, of the last activated plugin that has migrations. Prints Rolled back <id> of <plugin>; --pretend only names it.
migrate:status --json Prints a table of plugin, history table and applied migration IDs. --json prints every declared migration of those plugins with plugin, table, id and applied.
./bin/acme migrate --pretend
./bin/acme migrate
./bin/acme migrate:status --json
./bin/acme migrate:rollback --plugin acme.blog

Each plugin keeps its own migration history table, so rolling back one plugin never touches another. The migrations are listed in lagoon.

Confirmation in production

migrate, migrate:rollback, deferred:purge and queue:clear change or delete data. When the environment is production and the command runs on an interactive terminal, they ask before doing it; answering no prints a cancellation and exits 1. --force skips the question. Off a terminal (a deploy over SSH, a container entrypoint, the scheduler) and with -n they never ask and proceed as before, so deploy scripts need no change.

./bin/acme migrate --force

When the database cannot be reached, or database.dsn is empty, the error names the fix (docker compose up -d --wait, cp .env.example .env). Run from a directory below the application root, an application binary finds no config/ and adds a hint to its error with the command to run instead, for example cd ../..; bin/acme migrate.

An admin form holds the files uploaded and the related records created for a record that is not saved yet as deferred bindings. Saving the record commits them; abandoned ones are removed by deferred:purge:

Command Flags Purpose
deferred:purge --days <n>, --pretend, --force Deletes deferred bindings older than n days, the unattached uploads they hold and the records created under deferral, and prints the counts. Records that were only linked are kept. Without the flag, database.deferred_bindings.purge_days applies, else 5 days. --pretend counts the bindings that would go and deletes nothing.
./bin/acme deferred:purge --pretend
./bin/acme deferred:purge --days=1

The scheduler already runs it every day at 03:00; see Task scheduling.

Application key

Command Flags Purpose
key:generate --json Prints a fresh base64 32-byte key for app.key. It writes nothing and needs no database. With -q it prints only the key; --json prints {"key": "..."}.
./bin/acme key:generate
KEY=$(./bin/acme key:generate -q)

Copy the printed value into SUMMER_APP__KEY. When you rotate the key, keep the old one in app.previous_keys so existing encrypted columns can still be read.

The HTTP server

Command Flags Purpose
serve --addr (default :8080) Opens the database and uploads bucket, builds the router, binds the address, starts the in-process job worker and serves HTTP until SIGINT or SIGTERM. Once it listens it prints a startup summary; on a signal it drains requests and the worker within 10 seconds and prints a shutdown report.
route:list --json Builds the router the way serve does, without opening the database or listening, and prints every route with its method, pattern, plugin, middleware and raw flag, followed by the admin status line. --json prints the routes as a JSON array.
./bin/acme route:list
./bin/acme serve --addr 127.0.0.1:8080

The default --addr listens on every interface. Pass a loopback address during development, and put a reverse proxy in front of the binary in production. The routing options are in surf.

From the application directory, summer serve --addr 127.0.0.1:8123 passes the flag through to the built binary.

Startup summary

On a terminal serve prints a block once the port is bound:

SummerCMS acme v1.4.0 (framework v0.4.0)
  Environment  production
  URL          http://127.0.0.1:8080/
  Admin        http://127.0.0.1:8080/backend
  Database     db.internal:5432/acme
  Migrations   up to date
  Plugins      3 (acme.user, acme.blog, acme.shop)
  Queue        default, mail, scheduled (worker in serve)
  Scheduler    in serve
  Mail         smtp
  Cache        redis
  Realtime     null
  Search       null

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.

Shutting down

Ctrl+C or SIGTERM drains the requests in flight for up to 10 seconds, stops the in-process worker and prints a report (on a terminal) and a shutdown report record:

Shutdown report
  Requests  1520 served, 2 drained, 0 aborted
  Worker    84 done, 1 failed, 0 handed back to the queue
  Uptime    3h12m40s
  Errors    none

Jobs still running at the deadline are cancelled; River records the cancelled attempt and retries the job later, which the report counts as handed back to the queue. serve exits 0 after a clean stop and 1 when draining fails (requests still running at the deadline, or the worker failing to stop).

Admin accounts

Command Arguments and flags Purpose
admin:create --email (required), --login, --role <code>, --superuser, --password (deprecated) Creates an activated backend administrator. --login defaults to the lower-cased email. The password is read from a hidden prompt, or from stdin when the input is not a terminal.
admin:reset-password <identifier> (login or email), --password (deprecated) Sets a new password, read like the one of admin:create, and revokes every JWT issued before the reset. Personal API tokens are unchanged.
admin:token:create <identifier>, --name, --scope (repeatable), --expires (30d, 90d, 365d, never, or YYYY-MM-DD), --json Creates a personal API token for the named administrator and prints exactly the plaintext and a newline on stdout, also under -q. --json prints id, name, token, scopes and expires_at.
admin:token:list [<identifier>], --revoked, --json Lists tokens (hint only, never the hash or plaintext); --json as a JSON array.
admin:token:revoke <id> Soft-revokes any user's active token by id.
admin:token:scopes --json Lists framework and plugin scopes available for tokens; --json as a JSON array.
./bin/acme admin:create --email admin@example.com --superuser
./bin/acme admin:reset-password admin@example.com
TOKEN=$(./bin/acme admin:token:create admin@example.com --name agent --scope blog)

admin:create and admin:reset-password prompt for the password without echoing it. In a script, pipe it on stdin, for example printf '%s\n' "$ADMIN_PASSWORD" | ./bin/acme admin:create --email admin@example.com --superuser. The --password flag still works but is deprecated and prints a warning, because the value stays in your shell history and is visible in the process list. The admin and personal API tokens are described in cabana and Users and permissions.

From the application directory, summer admin:create --email admin@example.com --superuser runs the same command on the built binary, prompt included. The four admin:token:* commands have matching summer delegates.

When the admin is mounted and the backend_users table is empty, serve prints a warning after its startup summary naming summer admin:create.

Queues and the scheduler

Command Arguments and flags Purpose
queue:work --queue <name>, repeatable Runs a job worker in the foreground on the named queues (default: every known queue) until SIGINT or SIGTERM, with a startup summary and a shutdown report. An unknown queue is an error that lists the known ones.
queue:clear [queue] (default default), --force Deletes the waiting, scheduled and retryable jobs of one queue and prints Cleared N jobs from <queue>. Running jobs are never touched.
schedule:run --once Without --once, runs a scheduler-only worker until stopped, with a startup summary and a shutdown report. With --once, runs the entries due in the current minute, prints Ran N due entries (M failed) and exits, for system cron.
./bin/acme queue:work --queue default --queue imports
./bin/acme queue:clear imports
./bin/acme schedule:run --once

queue:work prints a startup summary on a terminal: the environment, each queue with its concurrency, the database host and whether the scheduled queue runs in this worker. schedule:run prints the number of schedule entries and the next one due. Off a terminal they log the same facts as a startup summary record (components queue and schedule). On Ctrl+C or SIGTERM both stop within 10 seconds and print a shutdown report: jobs done, failed and handed back to the queue (jobs still running at the deadline are cancelled and River retries them), the uptime and any stop error. They exit 0 after a clean stop and 1 when the worker fails to stop.

serve runs a job worker in the same process unless queue.work_in_serve is false; set it to false when you run queue:work separately. Task scheduling explains the scheduler, and conga the queue settings.

Realtime and push

These commands are not added by the generated main. An application that uses Centrifugo or Web Push appends them to the list one of its plugins returns from pact.HasCommands: centrifugo.Commands returns websockets:health, and flare.Commands returns the two push commands.

Command Arguments and flags Purpose
websockets:health --json Calls the Centrifugo info API and prints the configuration. Exits 1 when the API key is missing or the call fails. The key itself is never printed. --json prints healthy, api_url, enabled, api_key_set and error for monitoring.
websockets:generate-vapid-keys --update, --show-current Shows the configured VAPID keys, truncated, then generates a new pair. With --update it saves them to the environment's overrides.yaml; without it, it prints the variables to set by hand.
websockets:test-push <user_id>, --show-config Lists a user's push subscriptions and, after confirmation (yes by default, so -n sends without asking), sends one encrypted test notification to each.
./bin/acme websockets:health
./bin/acme websockets:generate-vapid-keys --show-current
./bin/acme websockets:test-push 1

The settings are in lighthouse and flare.