- new.sh reads the basename of SHELL (via ${SHELL:-} under set -u) and
prints fish_add_path "<dir>" for fish, which keeps it for new sessions,
export PATH="<dir>:$PATH" naming ~/.zshrc or ~/.bashrc for zsh and
bash, and today's generic export line with "your shell profile" for
any other, empty or unset shell
- dq_escape escapes the install directory for the target shell's double
quotes: backslash, double quote and dollar always, plus backtick and a
single-quoted ! for POSIX shells; a per-character loop, so bash 3.2
and 5.2 behave the same
- the behaviour harness defaults SHELL to empty so cases do not depend on
the developer's login shell, adds an unset-SHELL mode and covers fish,
zsh, bash, empty, unset, unknown and two metacharacter directories
- TestBootstrapEndToEnd pins SHELL=/bin/bash
- sunrise README and docs/setup/installation.md describe the per-shell
hint; the manual PATH section gains fish_add_path (go env GOPATH)/bin
4.9 KiB
sunrise
Bootstrap script that installs the summer CLI and creates a new SummerCMS application in one command, embedded so it is versioned with the framework.
import "git.golem15.com/golem15/summercms/modules/sunrise"
Overview
sunrise holds new.sh, the script behind the one-liner served at https://summercms.io/new:
curl -fsSL https://summercms.io/new | bash -s -- blog
The script checks for Go 1.27 or newer, git and Docker, clones the framework into ./summercms at its newest release tag (or moves an existing release checkout forward to it), installs the summer CLI with go install ./cmd/summer and runs summer new blog --framework <checkout> next to the checkout, which writes ./sm-blog-app. summer new prints the next steps; the script adds the cd line and, when the Go install directory is not on PATH, a hint for the login shell named by SHELL: fish_add_path <dir> for fish (which keeps it for new sessions), an export PATH=... line naming ~/.zshrc or ~/.bashrc for zsh and bash, and the generic export PATH=... line with "your shell profile" for any other shell.
The script lives inside this package so //go:embed can reach it, and a web server serves the bytes of the framework release it was built against. sunrise.Script returns them.
Features
- Truncation-safe: everything is a function definition and the last line is
main "$@", so a download cut short runs nothing. - Prerequisites first: 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 compose plugin is only a warning.
- Release tags: a fresh clone is checked out detached at the newest
vX.Y.Ztag, or atSUMMERCMS_REFwhen set. - Fast-forward-only updates: an existing checkout is fetched (tags are never force-updated) and moved only when its
HEADis an ancestor of the target. - Refusals that protect local work: an existing directory that is not a git repository, whose
go.modis not the framework module, that has changes to tracked files, or that is on a branch is left untouched and the script stops with a message. - Early name check: a name that is not lower-case letters and digits starting with a letter, or an existing
sm-<name>-app, stops the script before any clone or install. - Login-shell-aware PATH hint: the closing hint is one correct line for fish, zsh or bash, with a generic
export PATH=...line for any other shell; the directory is escaped for the target shell's double quotes and$PATHis printed literally. - No privilege escalation and no dynamic code; only bash builtins plus
git,goanddockerare run, with stdin closed andGIT_TERMINAL_PROMPT=0so nothing waits for input.
Usage
Run the script from the directory that should hold the framework checkout and the application:
curl -fsSL https://summercms.io/new | bash -s -- blog
bash -s -- <name> passes the application name to the piped script; -h or --help prints the usage. A local copy runs the same way with bash new.sh blog.
Serving the script from a Go handler:
package acme
import (
"net/http"
"git.golem15.com/golem15/summercms/modules/sunrise"
)
func newScript(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.Header().Set("X-Content-Type-Options", "nosniff")
_, _ = w.Write(sunrise.Script())
}
API reference
| Identifier | Description |
|---|---|
sunrise.Script |
Returns a fresh copy of the embedded new.sh bytes on every call. |
Configuration
The script reads three environment variables, plus SHELL, which only picks the form of the PATH hint. It never reads or sets SUMMER_-prefixed variables, which the framework reads as configuration.
| Variable | Default | Purpose |
|---|---|---|
SUMMERCMS_REPO |
https://git.golem15.com/golem15/summercms |
Repository to clone. |
SUMMERCMS_DIR |
summercms (relative to the current directory) |
Framework checkout directory; the application is created next to it. |
SUMMERCMS_REF |
newest vX.Y.Z tag |
Tag or commit to check out instead of the newest release. |
Dependencies
- SummerCMS modules: none.
- Third-party: none.
- Standard library:
bytes,embed.
Testing
go test ./modules/sunrise/...
The default run checks the embedded bytes, the script's shape and syntax (bash -n), and runs every line-boundary prefix and every byte prefix of the final line against stub commands to prove a truncated download invokes nothing.
SUMMERCMS_BOOTSTRAP_CHECK=1 go test ./modules/sunrise/ -run TestBootstrapEndToEnd -timeout 20m
The gated run serves the script from a local HTTP server, pipes it through curl | bash with SUMMERCMS_REPO set to this repository and SUMMERCMS_REF to its HEAD, and builds the generated application. It needs bash, curl, git, Go and access to the Go module proxy.