Files
summercms/docs/setup/installation.md
Jakub Zych f70d80ec7e feat(build): make summer new apps admin-ready
- NewOptions.AdminSecret: summer new writes SUMMER_ADMIN__JWT__SECRET
  to .env (still 0600), generated from 32 crypto/rand bytes when empty
  and validated as at least 32 .env-safe characters
- .env.example gets an empty SUMMER_ADMIN__JWT__SECRET= placeholder
  with a comment; the secret never reaches the printed next steps
- The next steps and the generated CLAUDE.md explain the admin at
  /backend: make:admin-controller, build, migrate and
  summer admin:create --email you@example.com --superuser
- Goldens use a fixed, non-secret admin secret fixture
- Scaffolding, installation and users-and-permissions docs updated
2026-10-08 17:20:19 +02:00

208 lines
10 KiB
Markdown

---
title: Installation
description: Create a SummerCMS application with one command, or install the summer CLI and build, migrate and serve the reference application by hand.
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. This page starts with the one-command path that creates a new application, then walks through the manual path: installing the tool from a framework checkout and building the small reference application that ships with it.
## Create an application with one command
```sh
curl -fsSL https://summercms.io/new | bash -s -- demo
```
The script checks for Go 1.27 and Docker, fetches the framework at its latest release tag into `./summercms`, installs the `summer` CLI and runs `summer new demo`, which writes a ready-to-run application to `sm-demo-app`. Start its database, then build, migrate and serve it:
```sh
cd sm-demo-app
docker compose up -d --wait
summer build
summer migrate
summer serve
```
## What the script does
Run the one-liner from the directory that should hold both the framework checkout and the application. `bash -s -- demo` passes the application name to the piped script; the name must be lower-case letters and digits, starting with a letter.
1. It checks the prerequisites. Go older than 1.27 or missing, or a missing git, stops the script with an install hint before anything is cloned. A missing Docker or Docker Compose plugin is only a warning, because the application needs them to run its database, not to be created.
2. It clones the framework into `./summercms` and checks out the newest `vX.Y.Z` release tag, detached. When `./summercms` already exists, the script fetches tags and moves the checkout forward to the newest release only when that is a fast-forward. It refuses, without changing anything, a directory that is not a framework checkout, a checkout with changes to tracked files and a checkout that is on a branch.
3. It installs the `summer` CLI from that checkout with `go install ./cmd/summer`. When the Go install directory is not on your `PATH`, the script reads your login shell from `SHELL` and ends with `fish_add_path <dir>` in fish, which keeps the directory for new sessions, or with `export PATH="<dir>:$PATH"`, to add to `~/.zshrc` in zsh, `~/.bashrc` in bash or your shell profile in any other shell.
4. It runs `summer new demo` next to the checkout, with `--framework` pointing at it, which writes `sm-demo-app` and prints the next steps. The script itself ends with the `cd` line into the new application.
The script stops before cloning when the name is invalid or `sm-demo-app` already exists. Three environment variables change its defaults:
| Variable | Default | Purpose |
|----------|---------|---------|
| `SUMMERCMS_REPO` | `https://git.golem15.com/golem15/summercms` | Repository to clone. |
| `SUMMERCMS_DIR` | `summercms` in the current directory | Framework checkout; the application is created next to it. |
| `SUMMERCMS_REF` | the newest release tag | Tag or commit to check out instead. |
Set them on the `bash` side of the pipe, for example `curl -fsSL https://summercms.io/new | SUMMERCMS_DIR=fw bash -s -- demo`. The [sunrise](../../modules/sunrise/README.md) module holds the script itself.
## Run the application
The generated application brings its own database and configuration:
- `compose.yaml` runs PostgreSQL 16, published on `127.0.0.1:54320` only, so it does not clash with a PostgreSQL already on port 5432.
- `.env` holds the matching `SUMMER_DATABASE__DSN`, a freshly generated `SUMMER_APP__KEY` and a random `SUMMER_ADMIN__JWT__SECRET` for the admin. It is excluded by `.gitignore`; `.env.example` lists the same variables.
- `config/` holds `app.yaml`, `database.yaml`, `http.yaml` and `storage.yaml`, enough for `summer serve` and `route:list`.
From `sm-demo-app`, start the database, build the binary, run the migrations and serve:
```sh
docker compose up -d --wait
summer build
summer migrate
summer serve
```
From another terminal, the starter plugin `golem15.demo` answers on the root route:
```sh
curl http://127.0.0.1:8080/
```
It returns `{"message":"Hello from SummerCMS","plugin":"golem15.demo"}`. Stop the database with `docker compose down`, or `docker compose down -v` to delete its data as well.
`summer serve` prints the URL to open and the admin status. The admin lives at `http://127.0.0.1:8080/backend` once a plugin registers an admin controller; until then the status line reads `admin: off` and names the steps. When the admin is there, create the first administrator:
```sh
summer admin:create --email you@example.com --superuser
```
[Admin controllers](../backend/admin-controllers.md) shows how to add a controller, and [Users and permissions](../backend/users-and-permissions.md) how administrators sign in.
## Create an application without the script
When you already have a framework checkout, or want to read the code before running anything, do the script's steps yourself. Clone the framework into `summercms`, install the tool from it and run `summer new` in the directory next to the checkout:
```sh
git clone https://git.golem15.com/golem15/summercms
go -C summercms install ./cmd/summer
summer new demo
```
`summer new` finds a sibling `summercms` or `summercms.go` checkout by its module path. For a checkout anywhere else, pass it explicitly with `summer new demo --framework ../path/to/summercms`. [Scaffolding](../console/scaffolding.md) lists every flag and the files `summer new` writes.
The rest of this page is the manual path: it installs the tool and builds `examples/hello`, the small reference application in the framework repository.
## Requirements
- Go 1.27.
- PostgreSQL 15 or newer for any application that uses the data layer. A generated application runs PostgreSQL 16 in Docker.
- Docker, for the database of a generated application and for the integration tests that start PostgreSQL or Mailpit containers.
You do not need Node.js to build an application or these docs. It is needed only when you work on the admin SPA itself.
## Install the summer CLI
Clone the framework repository, then install the `summer` tool from its root:
```sh
go install ./cmd/summer
summer --help
```
`go install` writes the binary to `$(go env GOPATH)/bin`, usually `~/go/bin`. If `summer --help` reports `command not found`, that directory is not on your `PATH`. In bash or zsh, add it for the current shell, and to `~/.bashrc` or `~/.zshrc` to keep it:
```sh
export PATH="$(go env GOPATH)/bin:$PATH"
```
In fish, `fish_add_path` adds it and keeps it for new sessions without a profile edit:
```sh
fish_add_path (go env GOPATH)/bin
```
The tool creates, builds and watches applications, scaffolds plugins, models, migrations and admin controllers, and builds this documentation. Check that the framework compiles and its unit tests pass:
```sh
go vet ./...
go test -short ./...
```
`go test -short` skips the tests that need Docker. Run `go test ./...` without `-short` when Docker is available.
## Check your install
Console commands in SummerCMS are plain `bonfire.Command` values: a name in `namespace:verb` form, its arguments and flags, and a run function that reads input and writes output. The `summer` tool and every application binary are built from such values, and `bonfire.Call` runs one in-process, which is how tests call commands. This example comes from the framework's own tests, so it compiles and runs whenever you run `go test ./...`:
```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
```
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 for the example:
```sql
CREATE DATABASE hello;
```
## 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.
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.
## 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.
- Read [Go modules and workspaces](../architecture/go-modules-and-workspaces.md) for what `summer new` writes into `go.mod` and `go.work`.
- Scaffold your own plugin with `summer make:plugin` as described in [Plugin registration](../plugins/registration.md).