- 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
121 lines
7.9 KiB
Markdown
121 lines
7.9 KiB
Markdown
---
|
|
title: Scaffolding
|
|
description: Create an application with summer new, build and watch it, and generate plugins, models, migrations, commands, jobs and admin controllers.
|
|
section: console
|
|
order: 30
|
|
---
|
|
# Scaffolding
|
|
|
|
The `summer` tool creates and 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 an application
|
|
|
|
`summer new` creates a new application in the current directory, the way `composer create-project` starts a WinterCMS project. It takes the application name and writes `sm-<name>-app`:
|
|
|
|
```sh
|
|
summer new acme
|
|
```
|
|
|
|
The name must be lower-case letters and digits, starting with a letter. Go keywords such as `go` or `type`, and the names `main` and `internal`, are refused because the starter plugin package would not compile or could not be imported. `summer new` also refuses any existing `sm-<name>-app`, even an empty directory, and never merges into one. When a step fails after it created the directory, it removes the directory again, so you can rerun the same command.
|
|
|
|
| Flag | Effect |
|
|
|------|--------|
|
|
| `--framework <path>` | The framework checkout to build against. Without it, `summer new` looks for a sibling `summercms` or `summercms.go` checkout, then the source tree the `summer` binary was built from. [Go modules and workspaces](../architecture/go-modules-and-workspaces.md) describes the detection. |
|
|
| `--no-plugin` | Skip the starter plugin; `summer.yaml` then lists no plugins, and the application still builds and serves. |
|
|
| `--no-git` | Skip `git init` and the first commit. |
|
|
|
|
The application gets these files:
|
|
|
|
| File | Contents |
|
|
|------|----------|
|
|
| `go.mod` | Module `git.golem15.com/golem15/sm-<name>-app`, requiring the framework through a relative `replace`. |
|
|
| `go.work` | The workspace with the application and its plugin modules, as relative paths. It is committed. |
|
|
| `summer.yaml` | The manifest: module path, binary name and plugin list. |
|
|
| `config/` | `app.yaml`, `database.yaml`, `http.yaml` and `storage.yaml`, enough for `serve` and `route:list`. |
|
|
| `.env`, `.env.example` | The database DSN, a freshly generated application key and a random admin token secret (`SUMMER_ADMIN__JWT__SECRET`). `.env` is readable by its owner only and never committed; `.env.example` lists the variables without the key and the secret. |
|
|
| `compose.yaml` | PostgreSQL 16 on `127.0.0.1:54320`, with credentials matching `.env`. |
|
|
| `.gitignore` | `bin/`, `tmp/`, `storage/`, `.env` and `go.work.sum`. |
|
|
| `CLAUDE.md`, `AGENTS.md` | Instructions for AI coding agents: the commands, the layout, the admin, the links to the documentation for agents and the naming convention. `AGENTS.md` is a symlink to `CLAUDE.md`. |
|
|
| `plugins/<name>` | The starter plugin `golem15.<name>`, created by the same code as `summer make:plugin` and registered by the same code as `summer plugin:add`. Its `routes.go` answers `GET /` with a JSON greeting. |
|
|
| `main.go`, `plugins.gen.go` | Generated from `summer.yaml`, as `summer build` does. |
|
|
|
|
Unless you pass `--no-git`, `summer new` initialises a git repository on `main` and commits everything except `.env`. When git has no user name or email configured, that first commit uses the author `SummerCMS <summer@localhost>` and prints a warning; your git configuration is not changed. Finally it prints the next steps to run inside `sm-acme-app`:
|
|
|
|
```sh
|
|
docker compose up -d --wait
|
|
summer build
|
|
summer migrate
|
|
summer serve
|
|
```
|
|
|
|
The next steps also explain the admin. It opens at `/backend` once a plugin registers an admin controller; until then `summer serve` prints `admin: off` with these steps:
|
|
|
|
```sh
|
|
summer make:admin-controller golem15.acme Posts
|
|
summer build
|
|
summer migrate
|
|
summer admin:create --email you@example.com --superuser
|
|
```
|
|
|
|
[Admin controllers](../backend/admin-controllers.md) explains the permission and model the generated controller needs.
|
|
|
|
Golem15 names repositories by what they hold: `sm-<name>-app` for an application such as the one `summer new` writes, `sm-<name>-plugin` for a standalone plugin module and `sm-<name>-theme` for a Nuxt or Vue frontend.
|
|
|
|
## 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. It already requires the permission `<plugin>.access_<name>`, which you declare in the plugin's `Permissions()`, and its `NewRecord` returns `nil` until you return your model, so the screens stay closed until you finish it. |
|
|
|
|
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. A second migration with the same name in the same second gets the next second's timestamp, but migrations with different names created in the same second share one timestamp and sort by name; check the order in `updates/`, as [Porting a plugin](../setup/porting-a-plugin.md) describes.
|
|
|
|
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.
|