feat(11.1-03): add the Setup and Console docs sections
- setup: introduction, installation rewritten from install to serve, configuration with the keys an application sets - console: introduction, setup and maintenance, scaffolding, writing commands, utilities; every command name is checker-verified - bonfire ExampleCatalog shows arguments, bare and repeatable flags - index links the section introductions; TestDocsRequiredPages lists the seven new pages
This commit is contained in:
50
docs/console/introduction.md
Normal file
50
docs/console/introduction.md
Normal file
@@ -0,0 +1,50 @@
|
||||
---
|
||||
title: Console introduction
|
||||
description: The two command-line programs of SummerCMS, the summer developer tool and the application binary, and how summer delegates runtime commands.
|
||||
section: console
|
||||
order: 10
|
||||
---
|
||||
# Console introduction
|
||||
|
||||
WinterCMS has one console entry point, `php artisan`. SummerCMS has two programs, because the developer tooling and the running application are separate binaries:
|
||||
|
||||
| Program | Where it comes from | What it does |
|
||||
|---------|---------------------|--------------|
|
||||
| `summer` | Installed once from the framework with `go install ./cmd/summer`. | Builds and watches applications, scaffolds plugins and their parts, records API parity fixtures and builds these docs. |
|
||||
| The application binary, for example `./bin/acme` | Written by `summer build` into the application's `bin/` directory. | Runs the application: the HTTP server, migrations, workers, the scheduler, admin accounts and every command its plugins add. |
|
||||
|
||||
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`.
|
||||
|
||||
## Getting help
|
||||
|
||||
Both programs list their commands with `--help`, and every command accepts `--help` for its arguments and flags:
|
||||
|
||||
```sh
|
||||
summer --help
|
||||
summer make:model --help
|
||||
./bin/acme --help
|
||||
./bin/acme migrate:rollback --help
|
||||
```
|
||||
|
||||
## 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:
|
||||
|
||||
| summer command | Runs |
|
||||
|----------------|------|
|
||||
| `summer migrate` | `./bin/acme migrate` |
|
||||
| `summer migrate:rollback` | `./bin/acme migrate:rollback` |
|
||||
| `summer migrate:status` | `./bin/acme migrate:status` |
|
||||
| `summer serve` | `./bin/acme serve` |
|
||||
| `summer queue:work` | `./bin/acme queue:work` |
|
||||
| `summer queue:clear` | `./bin/acme queue:clear` |
|
||||
| `summer schedule:run` | `./bin/acme schedule:run` |
|
||||
|
||||
`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 `route:list`, `key:generate` or `admin:create`, is run on the binary directly.
|
||||
|
||||
## The sections of this chapter
|
||||
|
||||
- [Setup and maintenance](setup-and-maintenance.md) lists every command of the application binary.
|
||||
- [Scaffolding](scaffolding.md) covers `summer build`, `summer dev` and the `make:` commands.
|
||||
- [Writing commands](writing-commands.md) shows how a plugin adds its own commands.
|
||||
- [Utilities](utilities.md) covers the parity and documentation commands of `summer`.
|
||||
67
docs/console/scaffolding.md
Normal file
67
docs/console/scaffolding.md
Normal file
@@ -0,0 +1,67 @@
|
||||
---
|
||||
title: Scaffolding
|
||||
description: Build and watch an application, and generate plugins, models, migrations, console commands, jobs and admin controllers with the summer make commands.
|
||||
section: console
|
||||
order: 30
|
||||
---
|
||||
# Scaffolding
|
||||
|
||||
The `summer` tool builds applications and generates the files a plugin is made of, the way `create:plugin`, `create:model` and the other `create:` commands do in WinterCMS. Every generated file compiles as written, so you can build straight after running a command.
|
||||
|
||||
## Building and watching
|
||||
|
||||
| Command | Purpose |
|
||||
|---------|---------|
|
||||
| `summer build` | Reads `summer.yaml`, generates `plugins.gen.go` and `main.go`, and builds the binary into `bin/`. Run it from the application directory or any directory below it. |
|
||||
| `summer dev` | Builds the application, starts the binary, and rebuilds and restarts it whenever a Go or YAML source, `go.mod`, `go.work`, `.env` or `summer.yaml` changes. |
|
||||
|
||||
```sh
|
||||
summer build
|
||||
summer dev
|
||||
```
|
||||
|
||||
Do not edit `main.go` or `plugins.gen.go`: `summer build` rewrites them from the manifest.
|
||||
|
||||
## Creating a plugin
|
||||
|
||||
`summer make:plugin` takes a plugin ID in `vendor.plugin` form and creates the plugin module in `plugins/<plugin>` of the current application, with the directory layout described in [Plugin registration](../plugins/registration.md):
|
||||
|
||||
```sh
|
||||
summer make:plugin acme.blog
|
||||
summer plugin:add plugins/blog
|
||||
```
|
||||
|
||||
`summer plugin:add` then registers the local module: it adds the plugin to `summer.yaml`, adds a `require` and a local `replace` to the application's `go.mod`, and adds the directory to `go.work`. The next `summer build` compiles the plugin in.
|
||||
|
||||
## Generating plugin parts
|
||||
|
||||
The other `make:` commands add one artifact to an existing plugin. Each takes the plugin ID and an exported Go name:
|
||||
|
||||
```sh
|
||||
summer make:model acme.blog Post
|
||||
summer make:migration acme.blog AddPublishedAt
|
||||
summer make:command acme.blog Publish
|
||||
summer make:job acme.blog ImportPosts
|
||||
summer make:admin-controller acme.blog Posts
|
||||
```
|
||||
|
||||
When you run a command inside a plugin directory, leave the ID out and pass only the name. The tool finds the plugin from the nearest `plugin.go` above the current directory:
|
||||
|
||||
```sh
|
||||
cd plugins/blog
|
||||
summer make:model Comment
|
||||
```
|
||||
|
||||
| Command | Writes | Notes |
|
||||
|---------|--------|-------|
|
||||
| `summer make:model` | `models/<name>.go` and `updates/<timestamp>_create_<table>.go` | The table name is the plugin ID and the plural name in snake case, such as `acme_blog_posts`. `--no-migration` skips the migration. |
|
||||
| `summer make:migration` | `updates/<timestamp>_<name>.go` | An empty gormigrate migration with up and down steps to fill in. |
|
||||
| `summer make:command` | `console/<name>.go` | A `bonfire.Command` named `<plugin>:<name>`, such as `blog:publish`. |
|
||||
| `summer make:job` | `jobs/<name>.go` | A typed job built with `conga.Job`; the plugin never imports the queue library. |
|
||||
| `summer make:admin-controller` | `controllers/<name>.go`, `controllers/<name>/config_form.yaml`, `controllers/<name>/config_list.yaml`, `models/<name>/fields.yaml`, `models/<name>/columns.yaml` | A `pact.AdminController` with WinterCMS-shaped form and list configuration. |
|
||||
|
||||
File names are the snake-case form of the name: `AddPublishedAt` becomes `add_published_at`. Migration file names start with a 14-digit timestamp, so they sort in the order you created them; migrations created in the same second get consecutive timestamps.
|
||||
|
||||
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.
|
||||
99
docs/console/setup-and-maintenance.md
Normal file
99
docs/console/setup-and-maintenance.md
Normal file
@@ -0,0 +1,99 @@
|
||||
---
|
||||
title: Setup and maintenance
|
||||
description: Every runtime command of an application binary, with its flags and purpose, from migrations and the server to workers, admin accounts and realtime checks.
|
||||
section: console
|
||||
order: 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` | none | Runs the framework migrations, then every plugin's migrations in dependency order. |
|
||||
| `migrate:rollback` | `--plugin <id>` | Rolls back the last migration of the plugin; without the flag, of the last activated plugin that has migrations. |
|
||||
| `migrate:status` | none | Prints a table of plugin, history table and applied migration IDs. |
|
||||
|
||||
```sh
|
||||
./bin/acme migrate
|
||||
./bin/acme migrate:status
|
||||
./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](../../modules/lagoon/README.md).
|
||||
|
||||
## Application key
|
||||
|
||||
| Command | Flags | Purpose |
|
||||
|---------|-------|---------|
|
||||
| `key:generate` | none | Prints a fresh base64 32-byte key for `app.key`. It writes nothing and needs no database. |
|
||||
|
||||
```sh
|
||||
./bin/acme key:generate
|
||||
```
|
||||
|
||||
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, starts the in-process job worker and serves HTTP until SIGINT or SIGTERM, then shuts down within 10 seconds. |
|
||||
| `route:list` | none | 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. |
|
||||
|
||||
```sh
|
||||
./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](../../modules/surf/README.md).
|
||||
|
||||
## Admin accounts
|
||||
|
||||
| Command | Arguments and flags | Purpose |
|
||||
|---------|---------------------|---------|
|
||||
| `admin:create` | `--email`, `--password` (both required), `--login`, `--role <code>`, `--superuser` | Creates an activated backend administrator. `--login` defaults to the lower-cased email. |
|
||||
| `admin:reset-password` | `<identifier>` (login or email), `--password` | Sets a new password and revokes every token issued before the reset. |
|
||||
|
||||
```sh
|
||||
./bin/acme admin:create --email admin@example.com --password '<secret>' --superuser
|
||||
./bin/acme admin:reset-password admin@example.com --password '<secret>'
|
||||
```
|
||||
|
||||
Passwords passed as flags end up in your shell history. Prefer reading them from a secrets manager into a variable. The admin is described in [cabana](../../modules/cabana/README.md).
|
||||
|
||||
## 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. An unknown queue is an error that lists the known ones. |
|
||||
| `queue:clear` | `[queue]` (default `default`) | Deletes the waiting, scheduled and retryable jobs of one queue and prints how many it cleared. Running jobs are never touched. |
|
||||
| `schedule:run` | `--once` | Without `--once`, runs a scheduler-only worker until stopped. With `--once`, runs the entries due in the current minute and exits, for system cron. |
|
||||
|
||||
```sh
|
||||
./bin/acme queue:work --queue default --queue imports
|
||||
./bin/acme queue:clear imports
|
||||
./bin/acme schedule:run --once
|
||||
```
|
||||
|
||||
`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](../plugins/scheduling.md) explains the scheduler, and [conga](../../modules/conga/README.md) 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` | none | 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. |
|
||||
| `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, sends one encrypted test notification to each. |
|
||||
|
||||
```sh
|
||||
./bin/acme websockets:health
|
||||
./bin/acme websockets:generate-vapid-keys --show-current
|
||||
./bin/acme websockets:test-push 1
|
||||
```
|
||||
|
||||
The settings are in [lighthouse](../../modules/lighthouse/README.md) and [flare](../../modules/flare/README.md).
|
||||
47
docs/console/utilities.md
Normal file
47
docs/console/utilities.md
Normal file
@@ -0,0 +1,47 @@
|
||||
---
|
||||
title: Utilities
|
||||
description: The summer commands for API parity testing and for building, syncing and previewing this documentation, with their flags.
|
||||
section: console
|
||||
order: 50
|
||||
---
|
||||
# Utilities
|
||||
|
||||
Besides building and scaffolding, the `summer` tool carries two groups of utility commands: API parity testing, used when you port an existing backend, and the documentation build.
|
||||
|
||||
## API parity commands
|
||||
|
||||
When you port an existing backend to SummerCMS, its real responses are the contract your port must meet. The parity commands wrap [tide](../../modules/tide/README.md): they record fixtures from the reference backend and replay them against the port. Every address they listen on or connect to must be a loopback address, and captured secrets go to a variables file with mode 0600, outside the committed fixtures.
|
||||
|
||||
| Command | Flags | Purpose |
|
||||
|---------|-------|---------|
|
||||
| `summer parity:proxy` | `--listen` (default `127.0.0.1:8422`), `--upstream` (default `http://127.0.0.1:8423`), `--session`, `--rules`, `--vars`, `--fixtures`, `--update` | Runs a recording reverse proxy in front of the reference backend. Point a real client at it; each named session (from the `X-Parity-Session` header, or `--session`) is written as one fixture. |
|
||||
| `summer parity:record` | `--spec`, `--target`, `--output`, `--rules`, `--vars`, `--update`; `--manifest`, `--fixtures`, `--next-batch`, `--resume`, `--allow-incomplete`, `--require-recorded` | Sends the requests of a YAML spec to a target and records the responses as a fixture, or records the missing cases of a route manifest in batches of at most 15. |
|
||||
| `summer parity:replay` | `--fixtures`, `--target`, `--vars`, `--manifest`, `--self-check`, `--require-recorded` | Replays recorded fixtures against a backend and reports the differences after masking IDs and timestamps. |
|
||||
| `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. |
|
||||
|
||||
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
|
||||
summer parity:replay --fixtures testdata/parity --target http://127.0.0.1:8080 --vars /tmp/parity/vars.yaml
|
||||
```
|
||||
|
||||
## Documentation commands
|
||||
|
||||
These docs are Markdown files under `docs/`, plus every module README, built into a static site by `summer`. Run the commands from the framework root.
|
||||
|
||||
| Command | Flags | Purpose |
|
||||
|---------|-------|---------|
|
||||
| `summer docs:build` | `--root` (default `.`), `--src`, `--out`, `--base-url`, `--check` | Checks every page and writes the site to `site/` (or `--out`): HTML pages, a raw `.md` copy of each page, `llms.txt`, `llms-full.txt` and the search index. With `--check` it only reports problems and writes nothing. |
|
||||
| `summer docs:sync` | `--root` (default `.`), `--src` | Rewrites every code block that has a `src=` reference from its source file. |
|
||||
| `summer docs:serve` | `--root` (default `.`), `--src`, `--base-url`, `--addr` (default `127.0.0.1:8088`), `--allow-remote` | Builds the site into a temporary directory, serves it and rebuilds when a page, a module or a referenced source changes. A failed rebuild prints its problems and keeps serving the last good build. |
|
||||
|
||||
```sh
|
||||
summer docs:build --check
|
||||
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. 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.
|
||||
96
docs/console/writing-commands.md
Normal file
96
docs/console/writing-commands.md
Normal file
@@ -0,0 +1,96 @@
|
||||
---
|
||||
title: Writing commands
|
||||
description: Add console commands to a plugin with bonfire.Command values, arguments, flags, styled output and prompts, and run commands in-process.
|
||||
section: console
|
||||
order: 40
|
||||
---
|
||||
# Writing commands
|
||||
|
||||
A plugin adds console commands to the application binary the way a WinterCMS plugin calls `registerConsoleCommand`. In SummerCMS a command is a plain `bonfire.Command` value, and the plugin returns its commands from `pact.HasCommands`. `summer make:command acme.blog Publish` generates a starting point in `console/publish.go`.
|
||||
|
||||
## Defining a command
|
||||
|
||||
A `bonfire.Command` has a name, a description, its positional arguments and flags, and a run function:
|
||||
|
||||
- The name is in `namespace:verb` form, such as `blog:publish`. Plugin commands must use this form; only a few framework commands have bare names.
|
||||
- Each `bonfire.Arg` is a positional argument with a name, a description and a `Required` marker. The usage line shows required arguments as `<name>` and optional ones as `[name]`.
|
||||
- Each `bonfire.Flag` is a string flag. Set `bonfire.Flag.Bare` for a switch such as `--dry-run` that stores `true` when given alone, and `bonfire.Flag.Repeatable` for a flag that can be given several times.
|
||||
- `bonfire.Command.Run` receives the context, a `bonfire.Input` and a `bonfire.Output`.
|
||||
|
||||
Read arguments with `bonfire.Input.Argument`, scalar and bare flags with `bonfire.Input.Flag`, and repeatable flags with `bonfire.Input.Flags`, which returns the values in the order given:
|
||||
|
||||
```go src=modules/bonfire/example_test.go#ExampleCatalog
|
||||
publish := bonfire.Command{
|
||||
Name: "blog:publish",
|
||||
Description: "Publish a post",
|
||||
Args: []bonfire.Arg{{Name: "slug", Description: "Post slug", Required: true}},
|
||||
Flags: []bonfire.Flag{
|
||||
{Name: "dry-run", Description: "Report without writing", Bare: true},
|
||||
{Name: "tag", Description: "Tag to add (repeatable)", Repeatable: true},
|
||||
},
|
||||
Run: func(ctx context.Context, in bonfire.Input, out bonfire.Output) error {
|
||||
slug, _ := in.Argument("slug")
|
||||
dryRun, _ := in.Flag("dry-run")
|
||||
out.Printf("publish %s, tags %v, dry run %s\n", slug, in.Flags("tag"), dryRun)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
// The generated main publishes a catalog of every command on the app.
|
||||
catalog := bonfire.NewCatalog([]bonfire.Command{publish})
|
||||
args := []string{"hello-world", "--tag", "news", "--tag", "go", "--dry-run"}
|
||||
if err := catalog.Call(context.Background(), "blog:publish", args, os.Stdout); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
// Output: publish hello-world, tags [news go], dry run true
|
||||
```
|
||||
|
||||
## 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:`.
|
||||
|
||||
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`.
|
||||
|
||||
## Output
|
||||
|
||||
`bonfire.Output` is the console your command writes to. Besides `bonfire.Output.Printf` and `bonfire.Output.Println`, it provides:
|
||||
|
||||
- status lines: `bonfire.Output.Info`, `bonfire.Output.Success`, `bonfire.Output.Warning` and `bonfire.Output.Error` (the last one writes to the error stream);
|
||||
- widgets: `bonfire.Output.Table`, `bonfire.Output.Spinner` around a function and `bonfire.Output.Progress` for a progress bar, which fall back to plain lines when the output is not a terminal;
|
||||
- prompts: `bonfire.Output.Ask`, `bonfire.Output.Confirm`, `bonfire.Output.Choice` and `bonfire.Output.Secret`. Prompts return their defaults when input ends, so a command run from cron or a script never hangs.
|
||||
|
||||
Return an error from `Run` to fail the command. The binary prints it and exits with status 1.
|
||||
|
||||
## Calling commands in-process
|
||||
|
||||
`bonfire.Call` runs one command of a slice by name with its arguments and writes the output to any writer, like `Artisan::call` in Laravel. Tests use it to exercise a command without building a binary:
|
||||
|
||||
```go src=modules/bonfire/example_test.go#ExampleCall
|
||||
commands := []bonfire.Command{{
|
||||
Name: "acme:greet",
|
||||
Description: "Greet someone by name",
|
||||
Args: []bonfire.Arg{{Name: "name", Required: true}},
|
||||
Run: func(ctx context.Context, in bonfire.Input, out bonfire.Output) error {
|
||||
name, _ := in.Argument("name")
|
||||
out.Printf("Hello, %s\n", name)
|
||||
return nil
|
||||
},
|
||||
}}
|
||||
|
||||
if err := bonfire.Call(context.Background(), commands, "acme:greet", []string{"blog"}, os.Stdout); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
// Output: Hello, blog
|
||||
```
|
||||
|
||||
The generated `main` also publishes the application's complete command list as a `bonfire.Catalog` on the container. Code outside the command line, such as the scheduler, looks it up and calls commands through `bonfire.Catalog.Call`, and checks for one with `bonfire.Catalog.Has`.
|
||||
|
||||
## Running your command
|
||||
|
||||
After `summer build`, your command is part of the binary:
|
||||
|
||||
```sh
|
||||
./bin/hello greeter:hello
|
||||
```
|
||||
|
||||
That command comes from the greeter plugin of `examples/hello`, whose `Commands` method returns one `bonfire.Command`.
|
||||
@@ -9,3 +9,10 @@ order: 0
|
||||
SummerCMS keeps what makes WinterCMS productive (plugins that extend each other, YAML-driven admin forms and lists, console scaffolding) and compiles an application into a single Go binary. The framework modules, the application's plugins, the embedded admin SPA and the console commands all ship as one executable.
|
||||
|
||||
Start with [Installation](setup/installation.md) to set up the toolchain and the `summer` CLI. If you know WinterCMS, read [Coming from WinterCMS](setup/coming-from-wintercms.md) for a map of its concepts to SummerCMS. The API reference section has one page per framework module.
|
||||
|
||||
## Where to start
|
||||
|
||||
- [Setup](setup/introduction.md): what SummerCMS is, installation, configuration and the move from WinterCMS.
|
||||
- [Architecture](architecture/introduction.md): the single binary, Go modules, the application lifecycle and the request lifecycle.
|
||||
- [Plugins](plugins/registration.md): registering a plugin, scheduling, extending other plugins and testing.
|
||||
- [Console](console/introduction.md): the `summer` tool, the application binary's commands and writing your own.
|
||||
|
||||
70
docs/setup/configuration.md
Normal file
70
docs/setup/configuration.md
Normal file
@@ -0,0 +1,70 @@
|
||||
---
|
||||
title: Configuration
|
||||
description: Configure an application with YAML files, per-environment directories, plugin defaults and SUMMER_ environment variables, and set the keys it needs.
|
||||
section: setup
|
||||
order: 30
|
||||
---
|
||||
# Configuration
|
||||
|
||||
WinterCMS keeps configuration in `config/*.php` files, per-environment directories and `.env`. SummerCMS keeps the same shape with YAML files, merged by [compass](../../modules/compass/README.md) into one tree that you read with dot paths such as `app.name`.
|
||||
|
||||
## The config directory
|
||||
|
||||
Each YAML file in the application's `config/` directory is a section named after the file, so `config/app.yaml` provides the `app.*` keys and `config/http.yaml` provides `http.*`:
|
||||
|
||||
```yaml
|
||||
# config/app.yaml
|
||||
name: Acme
|
||||
url: http://127.0.0.1:8080
|
||||
timezone: Europe/Warsaw
|
||||
```
|
||||
|
||||
Files for one environment go in `config/env/<environment>/` with the same naming, and override the base files. The environment comes from `SUMMER_ENV` and defaults to `production`, so a development setup sets `SUMMER_ENV=development` and keeps its overrides in `config/env/development/`.
|
||||
|
||||
## Plugin defaults
|
||||
|
||||
A plugin ships its own defaults embedded in the binary through `pact.HasConfig`. Its `config/config.yaml` becomes `<plugin id>.<key>`, so the `posts_per_page` key of `acme.blog` is read as `acme.blog.posts_per_page`. WinterCMS writes the same key as `acme.blog::posts_per_page`. The application overrides a plugin default like any other key: put `posts_per_page` under `blog:` in `config/acme.yaml`.
|
||||
|
||||
## Environment variables
|
||||
|
||||
Any key can be overridden with an environment variable. Take the dot path, replace each dot with a double underscore, upper-case it and prefix `SUMMER_`:
|
||||
|
||||
| Key | Variable |
|
||||
|-----|----------|
|
||||
| `database.dsn` | `SUMMER_DATABASE__DSN` |
|
||||
| `app.key` | `SUMMER_APP__KEY` |
|
||||
| `admin.jwt.secret` | `SUMMER_ADMIN__JWT__SECRET` |
|
||||
|
||||
A `.env` file next to the `config/` directory supplies `KEY=VALUE` lines for variables that are not already set in the real environment. Keep secrets in the environment or in `.env`, never in committed YAML.
|
||||
|
||||
Environment values are strings. Keys that must be numbers, such as `http.body_limits.default_bytes`, belong in a YAML file.
|
||||
|
||||
The layers merge in this order, each overriding the ones before it: plugin defaults, `config/*.yaml`, `config/env/<environment>/*.yaml`, `SUMMER_` variables, then runtime overrides that a command saved to `config/env/<environment>/overrides.yaml`.
|
||||
|
||||
## Keys an application sets
|
||||
|
||||
These are the keys most applications set. Each module's reference page lists all of its keys and their defaults.
|
||||
|
||||
| Key | Purpose | Reference |
|
||||
|-----|---------|-----------|
|
||||
| `database.dsn` | PostgreSQL connection string. Required by every command that opens the database. | [lagoon](../../modules/lagoon/README.md) |
|
||||
| `app.key` | Base64 encoding of 32 random bytes, used to encrypt columns. Generate it with `key:generate`. | [lagoon](../../modules/lagoon/README.md) |
|
||||
| `app.timezone` | Timezone of scheduled commands. Defaults to UTC. | [conga](../../modules/conga/README.md) |
|
||||
| `app.locale`, `app.fallback_locale` | Default and fallback translation locales. | [phrasebook](../../modules/phrasebook/README.md) |
|
||||
| `http.body_limits.default_bytes`, `http.body_limits.upload_bytes` | Request body limits. Required by `serve` and `route:list`. | [surf](../../modules/surf/README.md) |
|
||||
| `http.cors.*`, `http.trusted_proxies` | CORS and the proxies whose forwarded client IP is trusted. | [surf](../../modules/surf/README.md) |
|
||||
| `storage.uploads.bucket_url` | Uploads bucket, `file://` or `mem://`. Required by `serve`. | [lagoon](../../modules/lagoon/README.md) |
|
||||
| `admin.jwt.secret` | Secret for admin tokens. Required as soon as a plugin registers an admin controller. | [cabana](../../modules/cabana/README.md) |
|
||||
| `backend.uri` | Path the admin is served under. Defaults to `/backend`. | [cabana](../../modules/cabana/README.md) |
|
||||
| `mail.driver`, `mail.from`, `mail.smtp.*` | Mail delivery: `memory`, `log` or `smtp`. | [postcard](../../modules/postcard/README.md) |
|
||||
| `queue.work_in_serve`, `queue.queues.*` | Whether `serve` runs the job worker, and workers per queue. | [conga](../../modules/conga/README.md) |
|
||||
| `realtime.driver`, `realtime.centrifugo.*` | The realtime driver and its Centrifugo settings. | [lighthouse](../../modules/lighthouse/README.md) |
|
||||
| `search.driver`, `search.typesense.*` | The search engine and its Typesense settings. | [beachcomber](../../modules/beachcomber/README.md) |
|
||||
| `push.enabled`, `push.public_key`, `push.private_key` | Web Push with VAPID keys. | [flare](../../modules/flare/README.md) |
|
||||
|
||||
> [!NOTE]
|
||||
> The documentation checks every identifier, link and command name it shows against the code, but not configuration keys. The keys on this page are reviewed by hand; the module reference pages are the authority.
|
||||
|
||||
## Reading configuration in a plugin
|
||||
|
||||
Plugins read configuration from `backpack.App.Config`, a `compass.Config`. Use `compass.Config.String`, `compass.Config.Int` and `compass.Config.Bool` for single values, which return the zero value for a missing key, `compass.Config.Has` to tell a missing key from a zero value, and `compass.Config.LoadSection` to decode a whole section into a struct.
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
title: Installation
|
||||
description: Install the Go toolchain, PostgreSQL and the summer CLI you need to build a SummerCMS application.
|
||||
description: Install the Go toolchain, PostgreSQL and the summer CLI, then build, configure, migrate and serve your first SummerCMS application.
|
||||
section: setup
|
||||
order: 20
|
||||
---
|
||||
# Installation
|
||||
|
||||
SummerCMS is a Go module. An application requires it, lists its plugins in a `summer.yaml` manifest and builds everything into one binary with the `summer` CLI.
|
||||
SummerCMS is a Go module. An application requires it, lists its plugins in a `summer.yaml` manifest and builds everything into one binary with the `summer` CLI. This page installs the tool, then builds and runs `examples/hello`, the small reference application in the framework repository.
|
||||
|
||||
## Requirements
|
||||
|
||||
@@ -57,3 +57,71 @@ if err := bonfire.Call(context.Background(), commands, "acme:greet", []string{"b
|
||||
```
|
||||
|
||||
If the tests above pass, this example ran and printed `Hello, blog`. See the [bonfire](../../modules/bonfire/README.md) reference for flags, prompts and styled output.
|
||||
|
||||
## Build the example application
|
||||
|
||||
`examples/hello` has three compiled plugins. From the framework root, build it with `summer build`, which generates `plugins.gen.go` and `main.go` from `summer.yaml` and writes the binary to `bin/hello`:
|
||||
|
||||
```sh
|
||||
cd examples/hello
|
||||
summer build
|
||||
./bin/hello --help
|
||||
./bin/hello greeter:hello
|
||||
./bin/hello key:generate
|
||||
```
|
||||
|
||||
`greeter:hello` prints the example's layered configuration and `key:generate` prints a fresh application key. Neither needs a database.
|
||||
|
||||
## Create the database
|
||||
|
||||
The commands that touch data open PostgreSQL. Create a database with the locale lagoon checks for:
|
||||
|
||||
```sql
|
||||
CREATE DATABASE hello TEMPLATE template0 ENCODING 'UTF8' LOCALE_PROVIDER icu ICU_LOCALE 'pl-PL';
|
||||
```
|
||||
|
||||
## Configure the application
|
||||
|
||||
Configuration comes from YAML files in `config/`. Any key can be overridden with a `SUMMER_` environment variable, in which a double underscore separates path segments: `SUMMER_DATABASE__DSN` sets `database.dsn`. [Configuration](configuration.md) explains the layers.
|
||||
|
||||
`serve` and `route:list` need request body limits, and surf reads them as numbers, which the string-valued environment overlay cannot supply. Create `config/http.yaml` in `examples/hello`:
|
||||
|
||||
```yaml
|
||||
body_limits:
|
||||
default_bytes: 1048576
|
||||
upload_bytes: 10485760
|
||||
```
|
||||
|
||||
Then point the binary at the database, give it the application key and an uploads bucket. Replace the `<secret>` markers with your own values, and never commit them:
|
||||
|
||||
```sh
|
||||
export SUMMER_DATABASE__DSN='postgres://acme:<secret>@127.0.0.1:5432/hello?sslmode=disable'
|
||||
export SUMMER_APP__KEY='<value printed by key:generate>'
|
||||
export SUMMER_STORAGE__UPLOADS__BUCKET_URL='mem://'
|
||||
```
|
||||
|
||||
## Migrate and serve
|
||||
|
||||
Run the migrations, list the routes and start the server on the loopback address:
|
||||
|
||||
```sh
|
||||
./bin/hello migrate
|
||||
./bin/hello route:list
|
||||
./bin/hello serve --addr 127.0.0.1:8080
|
||||
curl http://127.0.0.1:8080/items/1
|
||||
```
|
||||
|
||||
From the application directory, `summer migrate`, `summer migrate:status` and `summer serve` run the same commands through the built binary, building it first when it is missing.
|
||||
|
||||
> [!WARNING]
|
||||
> Known issues in the current framework:
|
||||
>
|
||||
> - `examples/hello` ships no `http.body_limits` configuration, so `serve` and `route:list` fail with `surf: config http.body_limits.default_bytes is required` until you add `config/http.yaml` as shown above. The same gap makes the example's `TestTypedItemRoute` fail.
|
||||
> - lagoon refuses any database whose default locale is not ICU `pl-PL`.
|
||||
> - The committed `examples/hello/main.go` is older than what `summer build` generates now, so building the example leaves that file modified. Restore it with `git checkout -- examples/hello/main.go` if you do not intend to commit it.
|
||||
|
||||
## Next steps
|
||||
|
||||
- Read [Coming from WinterCMS](coming-from-wintercms.md) if you are porting a WinterCMS plugin.
|
||||
- Read the [Architecture introduction](../architecture/introduction.md) to see how the pieces fit.
|
||||
- Scaffold your own plugin with `summer make:plugin` as described in [Plugin registration](../plugins/registration.md).
|
||||
|
||||
39
docs/setup/introduction.md
Normal file
39
docs/setup/introduction.md
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
title: Introduction
|
||||
description: What SummerCMS is, who it is for, how its headless model works and how this documentation is organised.
|
||||
section: setup
|
||||
order: 10
|
||||
---
|
||||
# Introduction
|
||||
|
||||
SummerCMS is a content management framework for Go, inspired by WinterCMS. It keeps what makes WinterCMS productive (plugins that extend each other, backend lists and forms described in YAML, models, migrations and console scaffolding) and compiles an application into a single binary.
|
||||
|
||||
## Who it is for
|
||||
|
||||
SummerCMS is for developers who build content-driven applications and APIs the WinterCMS way and want a compiled, typed backend. If you already know WinterCMS, most concepts carry over: a plugin still has an ID, requires other plugins, registers and boots, ships migrations and admin YAML, and adds console commands. Start with [Coming from WinterCMS](coming-from-wintercms.md) for a concept-by-concept map.
|
||||
|
||||
## The headless model
|
||||
|
||||
SummerCMS is headless. An application serves:
|
||||
|
||||
- a JSON API, declared route by route by its plugins;
|
||||
- an admin area, a compiled single-page application embedded in the binary, driven by each plugin's `fields.yaml` and `columns.yaml`;
|
||||
- console commands for migrations, workers, the scheduler and your own tasks.
|
||||
|
||||
It does not render public pages. There are no themes, CMS pages, layouts, components or AJAX framework. Build the public site as a separate frontend application that calls the JSON API and, for live updates, subscribes to realtime channels.
|
||||
|
||||
## One binary
|
||||
|
||||
Plugins are Go packages compiled into the application at build time. The `summer` tool reads the application's `summer.yaml` manifest, generates the plugin imports and builds one executable. Nothing is loaded or installed at runtime, so what you tested is exactly what you deploy.
|
||||
|
||||
The data layer supports PostgreSQL only, and SummerCMS targets Go 1.27.
|
||||
|
||||
## How these docs are organised
|
||||
|
||||
- **Setup** covers installation, configuration and the move from WinterCMS.
|
||||
- **Architecture** explains the single binary, Go modules, the application lifecycle and how a request reaches your code.
|
||||
- **Plugins** covers registering a plugin, scheduling, extending other plugins and testing.
|
||||
- **Console** lists the `summer` tool's commands and the commands of every application binary, and shows how to write your own.
|
||||
- **API reference** has one page per framework module.
|
||||
|
||||
Continue with [Installation](installation.md).
|
||||
@@ -17,5 +17,7 @@ sections:
|
||||
title: Architecture
|
||||
- name: plugins
|
||||
title: Plugins
|
||||
- name: console
|
||||
title: Console
|
||||
- name: api
|
||||
title: API reference
|
||||
|
||||
Reference in New Issue
Block a user