test(14.3-04): pin every file summer new writes with golden files

- new_golden_test.go: -update flag, fixed 0x00..0x1f key, sibling summercms symlink
- testdata/new (16 files + next steps) and testdata/new-no-plugin (6)
- plugin go.mod replace and absent root go.sum asserted instead of pinned
This commit is contained in:
Jakub Zych
2026-10-08 12:12:34 +02:00
parent 9f56dc7eff
commit eed24b8ad1
24 changed files with 526 additions and 0 deletions

View File

@@ -0,0 +1,44 @@
# CLAUDE.md
Guidance for AI coding agents working in this repository. `AGENTS.md` is a symlink to this file.
## What this is
`sm-demo-app` is a SummerCMS application: a Go program built on the SummerCMS framework, module `git.golem15.com/golem15/summercms`. `go.mod` replaces the framework with the local checkout at `../summercms`. That checkout is shared with every other application next to it: do not edit the framework from here. Change this application, or report the framework problem.
Framework documentation for agents:
- https://summercms.io/docs/llms.txt (index)
- https://summercms.io/docs/llms-full.txt (full text in one file)
## Commands
Run them from this directory.
```sh
docker compose up -d --wait # start PostgreSQL 16 on 127.0.0.1:54320
summer build # generate main.go and plugins.gen.go, build bin/demo
summer dev # watch sources and rebuild
summer migrate # run plugin migrations
summer serve # start the HTTP server
summer make:plugin golem15.<name> # scaffold a plugin module under plugins/<name>
summer plugin:add plugins/<name> # register it in summer.yaml, go.mod and go.work
```
## Layout
- `summer.yaml`: the application manifest (module path, binary name, ordered plugin list).
- `plugins/`: local plugin modules.
- `config/`: YAML configuration (`app.yaml`, `database.yaml`, `http.yaml`, `storage.yaml`). Any key can be overridden with a `SUMMER_` environment variable, a double underscore separating path segments (`SUMMER_DATABASE__DSN` sets `database.dsn`).
- `.env`: local secrets (database DSN, application key). Never commit it; `.gitignore` excludes it. `.env.example` lists the variables.
- `compose.yaml`: the local PostgreSQL 16.
- `main.go`, `plugins.gen.go`: generated by `summer build` from `summer.yaml`. Do not edit them by hand; they are committed.
- `go.work`: the workspace that joins the application and its plugin modules.
Plugins are Go modules compiled into the binary at build time; there is no runtime plugin loading. Add a plugin with `summer make:plugin` and `summer plugin:add`, then run `summer build`.
## Naming convention
- `sm-<name>-app`: an application, like this repository.
- `sm-<name>-plugin`: a standalone plugin module.
- `sm-<name>-theme`: a Nuxt/Vue frontend.

View File

@@ -0,0 +1,9 @@
module git.golem15.com/golem15/sm-demo-app
go 1.27.0
toolchain go1.27.0
require git.golem15.com/golem15/summercms v0.0.0
replace git.golem15.com/golem15/summercms => ../summercms

View File

@@ -0,0 +1,7 @@
go 1.27.0
toolchain go1.27.0
use (
.
)

View File

@@ -0,0 +1,10 @@
created <parent>/sm-demo-app
Next steps, inside sm-demo-app:
docker compose up -d --wait
summer build
summer migrate
summer serve
The app has no plugins yet. Add one with summer make:plugin golem15.<name>
and summer plugin:add plugins/<name>, then run summer build again.

View File

@@ -0,0 +1,6 @@
// Code generated by summer build. DO NOT EDIT.
package main
// PluginIDs is the ordered plugin list from summer.yaml.
var PluginIDs = []string{}

View File

@@ -0,0 +1,3 @@
module: git.golem15.com/golem15/sm-demo-app
binary: demo
plugins:

View File

@@ -0,0 +1,44 @@
# CLAUDE.md
Guidance for AI coding agents working in this repository. `AGENTS.md` is a symlink to this file.
## What this is
`sm-demo-app` is a SummerCMS application: a Go program built on the SummerCMS framework, module `git.golem15.com/golem15/summercms`. `go.mod` replaces the framework with the local checkout at `../summercms`. That checkout is shared with every other application next to it: do not edit the framework from here. Change this application, or report the framework problem.
Framework documentation for agents:
- https://summercms.io/docs/llms.txt (index)
- https://summercms.io/docs/llms-full.txt (full text in one file)
## Commands
Run them from this directory.
```sh
docker compose up -d --wait # start PostgreSQL 16 on 127.0.0.1:54320
summer build # generate main.go and plugins.gen.go, build bin/demo
summer dev # watch sources and rebuild
summer migrate # run plugin migrations
summer serve # start the HTTP server
summer make:plugin golem15.<name> # scaffold a plugin module under plugins/<name>
summer plugin:add plugins/<name> # register it in summer.yaml, go.mod and go.work
```
## Layout
- `summer.yaml`: the application manifest (module path, binary name, ordered plugin list).
- `plugins/`: local plugin modules. `plugins/demo` is the starter plugin `golem15.demo`; its `routes.go` answers `GET /` with JSON.
- `config/`: YAML configuration (`app.yaml`, `database.yaml`, `http.yaml`, `storage.yaml`). Any key can be overridden with a `SUMMER_` environment variable, a double underscore separating path segments (`SUMMER_DATABASE__DSN` sets `database.dsn`).
- `.env`: local secrets (database DSN, application key). Never commit it; `.gitignore` excludes it. `.env.example` lists the variables.
- `compose.yaml`: the local PostgreSQL 16.
- `main.go`, `plugins.gen.go`: generated by `summer build` from `summer.yaml`. Do not edit them by hand; they are committed.
- `go.work`: the workspace that joins the application and its plugin modules.
Plugins are Go modules compiled into the binary at build time; there is no runtime plugin loading. Add a plugin with `summer make:plugin` and `summer plugin:add`, then run `summer build`.
## Naming convention
- `sm-<name>-app`: an application, like this repository.
- `sm-<name>-plugin`: a standalone plugin module.
- `sm-<name>-theme`: a Nuxt/Vue frontend.

View File

@@ -0,0 +1,22 @@
# Local development database: docker compose up -d --wait
# PostgreSQL is published on the loopback address only, and the credentials
# match SUMMER_DATABASE__DSN in .env.
services:
postgres:
image: postgres:16
environment:
POSTGRES_USER: summer
POSTGRES_PASSWORD: summer
POSTGRES_DB: demo
ports:
- "127.0.0.1:54320:5432"
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U summer -d demo"]
interval: 2s
timeout: 3s
retries: 30
volumes:
pgdata:

View File

@@ -0,0 +1,5 @@
name: demo
debug: false
# The application key encrypts data at rest. Set SUMMER_APP__KEY in .env (a
# 32-byte base64 value); keep it out of this file and out of git.
key: ""

View File

@@ -0,0 +1,2 @@
# Set SUMMER_DATABASE__DSN in .env; compose.yaml runs the matching PostgreSQL.
dsn: ""

View File

@@ -0,0 +1,5 @@
body_limits:
# Request body limits in bytes. Keep them YAML numbers: values from the
# environment are strings and are rejected at boot.
default_bytes: 1048576
upload_bytes: 10485760

View File

@@ -0,0 +1,5 @@
# Uploads bucket, required by summer serve. A file:// URL is a local
# directory under the application working directory.
uploads:
bucket_url: "file://./storage/app/uploads"
public_path_prefix: "/storage/uploads"

View File

@@ -0,0 +1,10 @@
# The variables .env sets. summer new wrote a .env with a fresh key; copy this
# file to .env only when you need to start over.
SUMMER_DATABASE__DSN=postgres://summer:summer@127.0.0.1:54320/demo?sslmode=disable
# A 32-byte base64 application key. Generate one with ./bin/demo key:generate
# and paste only the key, without the leading check mark.
SUMMER_APP__KEY=
# Uploads bucket; config/storage.yaml already sets this default.
# SUMMER_STORAGE__UPLOADS__BUCKET_URL=file://./storage/app/uploads
# Configuration environment; selects the config overlay (default production).
# SUMMER_ENV=development

View File

@@ -0,0 +1,5 @@
# Local secrets for this application: the database DSN and the application
# key. .gitignore excludes this file; never commit it. Real environment
# variables override the values here.
SUMMER_DATABASE__DSN=postgres://summer:summer@127.0.0.1:54320/demo?sslmode=disable
SUMMER_APP__KEY=AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=

View File

@@ -0,0 +1,6 @@
/bin/
/tmp/
/storage/
.env
go.work.sum
*.exe

View File

@@ -0,0 +1,14 @@
module git.golem15.com/golem15/sm-demo-app
go 1.27.0
toolchain go1.27.0
require (
git.golem15.com/golem15/sm-demo-app/plugins/demo v0.0.0
git.golem15.com/golem15/summercms v0.0.0
)
replace git.golem15.com/golem15/summercms => ../summercms
replace git.golem15.com/golem15/sm-demo-app/plugins/demo => ./plugins/demo

View File

@@ -0,0 +1,8 @@
go 1.27.0
toolchain go1.27.0
use (
.
./plugins/demo
)

View File

@@ -0,0 +1,59 @@
// Code generated by summer build. DO NOT EDIT.
package main
import (
"fmt"
"io"
"os"
"git.golem15.com/golem15/summercms/modules/backpack"
"git.golem15.com/golem15/summercms/modules/bonfire"
"git.golem15.com/golem15/summercms/modules/cabana"
"git.golem15.com/golem15/summercms/modules/compass"
"git.golem15.com/golem15/summercms/modules/conga"
"git.golem15.com/golem15/summercms/modules/lagoon"
"git.golem15.com/golem15/summercms/modules/pact"
"git.golem15.com/golem15/summercms/modules/party"
"git.golem15.com/golem15/summercms/modules/sunscreen"
"git.golem15.com/golem15/summercms/modules/surf"
)
func main() {
if err := run(os.Args[1:], os.Stdout); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func run(args []string, out io.Writer) error {
sunscreen.InstallDefault(os.Stderr)
cfg, err := compass.Load("config")
if err != nil {
return err
}
app := backpack.New(cfg)
plugins, err := party.Activate(app, PluginIDs)
if err != nil {
return err
}
commands := lagoon.RuntimeCommands(app, plugins)
commands = append(commands, conga.RuntimeCommands(app, plugins)...)
commands = append(commands, surf.ServeCommand(app, plugins))
commands = append(commands, surf.RouteListCommand(app, plugins))
commands = append(commands, cabana.RuntimeCommands(app)...)
for _, plugin := range plugins {
if hasCommands, ok := plugin.(pact.HasCommands); ok {
commands = append(commands, hasCommands.Commands()...)
}
}
if err := app.Publish(bonfire.NewCatalog(commands)); err != nil {
return err
}
root, err := bonfire.NewRoot("demo", commands, out)
if err != nil {
return err
}
root.SetArgs(args)
return root.Execute()
}

View File

@@ -0,0 +1,10 @@
created <parent>/sm-demo-app
Next steps, inside sm-demo-app:
docker compose up -d --wait
summer build
summer migrate
summer serve
Then, from another terminal:
curl http://127.0.0.1:8080/

View File

@@ -0,0 +1,12 @@
// Code generated by summer build. DO NOT EDIT.
package main
import (
_ "git.golem15.com/golem15/sm-demo-app/plugins/demo"
)
// PluginIDs is the ordered plugin list from summer.yaml.
var PluginIDs = []string{
"golem15.demo",
}

View File

@@ -0,0 +1,60 @@
package demo
import (
"embed"
"io/fs"
"git.golem15.com/golem15/summercms/modules/backpack"
"git.golem15.com/golem15/summercms/modules/bonfire"
"git.golem15.com/golem15/summercms/modules/pact"
"git.golem15.com/golem15/summercms/modules/party"
"github.com/go-gormigrate/gormigrate/v2"
)
var (
_ pact.HasConfig = (*Plugin)(nil)
_ pact.HasLang = (*Plugin)(nil)
_ pact.HasMailTemplates = (*Plugin)(nil)
_ pact.HasModels = (*Plugin)(nil)
_ pact.HasMigrations = (*Plugin)(nil)
_ pact.HasCommands = (*Plugin)(nil)
_ pact.HasJobs = (*Plugin)(nil)
_ pact.HasAdminControllers = (*Plugin)(nil)
)
//go:embed config
var configFS embed.FS
//go:embed lang
var langFS embed.FS
//go:embed views/mail
var mailFS embed.FS
// Plugin is the golem15.demo plugin.
type Plugin struct{}
func (p *Plugin) ID() string { return "golem15.demo" }
func (p *Plugin) Requires() []string { return nil }
func (p *Plugin) Register(*backpack.App) error { return nil }
func (p *Plugin) Boot(*backpack.App) error { return nil }
func (p *Plugin) ConfigFS() fs.FS { return configFS }
func (p *Plugin) LangFS() fs.FS { return langFS }
func (p *Plugin) MailTemplatesFS() fs.FS { return mailFS }
func (p *Plugin) MailTemplates() []string { return nil }
func (p *Plugin) MailLayouts() map[string]string { return nil }
func (p *Plugin) Models() []any { return generatedModels() }
func (p *Plugin) Migrations() []*gormigrate.Migration { return generatedMigrations() }
func (p *Plugin) Commands() []bonfire.Command { return generatedCommands() }
func (p *Plugin) Jobs() []pact.Job { return generatedJobs() }
func (p *Plugin) AdminControllers() []pact.AdminController {
return generatedAdminControllers()
}
func init() {
party.Register(&Plugin{})
}

View File

@@ -0,0 +1,23 @@
package demo
import (
"encoding/json"
"net/http"
"git.golem15.com/golem15/summercms/modules/pact"
)
var _ pact.HasRoutes = (*Plugin)(nil)
// Routes registers the starter route: GET / answers with a JSON greeting
// naming this plugin.
func (p *Plugin) Routes(r pact.Router) error {
r.Get("/{$}", func(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(map[string]string{
"message": "Hello from SummerCMS",
"plugin": p.ID(),
})
})
return nil
}

View File

@@ -0,0 +1,5 @@
module: git.golem15.com/golem15/sm-demo-app
binary: demo
plugins:
- id: golem15.demo
module: git.golem15.com/golem15/sm-demo-app/plugins/demo