- new.sh gains --update, run inside one application directory (go.mod and summer.yaml required); it changes only that application - the framework checkout is read from the app's go.mod replace (single-line or block form); module-version replaces, missing targets and foreign or dirty checkouts are refused through the shared check_framework_dir - a detached checkout moves fast-forward only to the newest release (or SUMMERCMS_REF) through the shared move_detached; a branch checkout is pulled with git pull --no-rebase --ff-only, refusing a missing upstream or a diverged branch with the checkout unchanged - other sm-*-app directories on the same checkout are named on one "not rebuilt" line and never built or written - summer is reinstalled, a missing SUMMER_ADMIN__JWT__SECRET is appended to this app's .env (openssl, else head and base64; mode kept, never printed), then summer build runs and the script ends with the migrate and restart reminder and the shared path_hint - sunrise.UpdateScript() serves the same script with its last line changed to main "$@" --update, so truncated downloads still run nothing - check-phase14.3.sh --forbidden now allows only the literal SUMMER_ADMIN__JWT__SECRET in new.sh and still fails on any other SUMMER_ name or on a read or export of one; TestUpdateScript and TestUpdateScriptRoute are required tests
13 KiB
title, description, section, order
| title | description | section | order |
|---|---|---|---|
| Installation | Create a SummerCMS application with one command, or install the summer CLI and build, migrate and serve the reference application by hand. | setup | 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
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:
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.
- 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.
- It clones the framework into
./summercmsand checks out the newestvX.Y.Zrelease tag, detached. When./summercmsalready 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; the update script pulls a branch checkout instead. - It installs the
summerCLI from that checkout withgo install ./cmd/summer. When the Go install directory is not on yourPATH, the script reads your login shell fromSHELLand ends withfish_add_path <dir>in fish, which keeps the directory for new sessions, or withexport PATH="<dir>:$PATH", to add to~/.zshrcin zsh,~/.bashrcin bash or your shell profile in any other shell. - It runs
summer new demonext to the checkout, with--frameworkpointing at it, which writessm-demo-appand prints the next steps. The script itself ends with thecdline 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 update script does not use SUMMERCMS_DIR. The sunrise module holds the script itself.
Run the application
The generated application brings its own database and configuration:
compose.yamlruns PostgreSQL 16, published on127.0.0.1:54320only, so it does not clash with a PostgreSQL already on port 5432..envholds the matchingSUMMER_DATABASE__DSN, a freshly generatedSUMMER_APP__KEYand a randomSUMMER_ADMIN__JWT__SECRETfor the admin. It is excluded by.gitignore;.env.examplelists the same variables.config/holdsapp.yaml,database.yaml,http.yamlandstorage.yaml, enough forsummer serveandroute:list.
From sm-demo-app, start the database, build the binary, run the migrations and serve:
docker compose up -d --wait
summer build
summer migrate
summer serve
From another terminal, the starter plugin golem15.demo answers on the root route:
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:
summer admin:create --email you@example.com --superuser
Admin controllers shows how to add a controller, and Users and permissions how administrators sign in.
Updating an application
To move an application to a newer framework, run the update script from inside the application directory:
cd sm-demo-app
curl -fsSL https://summercms.io/update | bash
It works the same from bash, zsh and fish, because the piped script always runs in bash. curl -fsSL https://summercms.io/new | bash -s -- --update is the same command. It updates this application only:
- It checks that the current directory holds a
go.modand asummer.yaml, and reads the framework checkout from thego.modreplace thatsummer newwrote (../summercmsfor a generated application). A replace that names a module version instead of a local path is refused; update that requirement withgo get. - It moves the checkout forward. A detached release checkout is fetched and moved, fast-forward only, to the newest release tag, or to
SUMMERCMS_REFwhen set. A checkout on a branch is pulled from its upstream withgit pull --ff-only. A checkout with changes to tracked files, a branch without an upstream and a branch that has diverged from its upstream stop the script with the checkout unchanged, and nothing is installed or built. - When other
sm-*-appdirectories next to the application or next to the checkout use the same checkout, it names them on onenot rebuiltline. It never builds them or writes their files; each picks up the new framework on its nextsummer build. - It reinstalls the
summerCLI from the checkout. - When the application's
.envhas noSUMMER_ADMIN__JWT__SECRETline, as in applications created before the admin was wired in, it appends one with a random value. The value is never printed, and the file keeps its permissions. - It runs
summer buildin the application and ends with a reminder to migrate and restart.
Then run the new migrations and restart the server:
summer migrate
summer serve
To update by hand, move the checkout yourself, reinstall the tool and rebuild. git -C ../summercms tag -l 'v*' --sort=-v:refname lists the release tags, newest first; for a checkout on a branch, use git -C ../summercms pull --ff-only instead of the first two commands:
git -C ../summercms fetch --tags
git -C ../summercms checkout --detach vX.Y.Z
go -C ../summercms install ./cmd/summer
summer build
summer migrate
An application without SUMMER_ADMIN__JWT__SECRET in its .env needs one line with a long random value, for example from openssl rand -base64 32.
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:
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 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:
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:
export PATH="$(go env GOPATH)/bin:$PATH"
In fish, fish_add_path adds it and keeps it for new sessions without a profile edit:
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:
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 ./...:
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 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:
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:
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 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:
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:
./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 if you are porting a WinterCMS plugin.
- Read the Architecture introduction to see how the pieces fit.
- Read Go modules and workspaces for what
summer newwrites intogo.modandgo.work. - Scaffold your own plugin with
summer make:pluginas described in Plugin registration.