Files
summercms/modules/sunrise/README.md
Jakub Zych cfd110eddb feat(14.3-02): add the sunrise bootstrap script module
- modules/sunrise/new.sh: curl | bash installer wrapped in main(), checks
  Go 1.27+/git/Docker, clones or fast-forwards the framework at the newest
  release tag (SUMMERCMS_REPO/DIR/REF overrides), installs summer and runs
  summer new next to the checkout, prints the cd line and a PATH hint
- sunrise.Script() returns a fresh copy of the embedded script
- smoke, shape, truncation and gated end-to-end tests; README and modules row
2026-10-08 11:51:39 +02:00

4.3 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, an export PATH=... hint.

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.Z tag, or at SUMMERCMS_REF when set.
  • Fast-forward-only updates: an existing checkout is fetched (tags are never force-updated) and moved only when its HEAD is an ancestor of the target.
  • Refusals that protect local work: an existing directory that is not a git repository, whose go.mod is 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.
  • No privilege escalation and no dynamic code; only bash builtins plus git, go and docker are run, with stdin closed and GIT_TERMINAL_PROMPT=0 so 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. 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.