feat(sunrise): update an application in place with summercms.io/update

- 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
This commit is contained in:
Jakub Zych
2026-10-08 20:15:50 +02:00
parent 27f75308d7
commit 9b3803c098
8 changed files with 1292 additions and 108 deletions

View File

@@ -29,7 +29,7 @@ summer serve
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.
1. 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.
2. It clones the framework into `./summercms` and checks out the newest `vX.Y.Z` release tag, detached. When `./summercms` already 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.
2. It clones the framework into `./summercms` and checks out the newest `vX.Y.Z` release tag, detached. When `./summercms` already 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](#updating-an-application) pulls a branch checkout instead.
3. It installs the `summer` CLI from that checkout with `go install ./cmd/summer`. When the Go install directory is not on your `PATH`, the script reads your login shell from `SHELL` and ends with `fish_add_path <dir>` in fish, which keeps the directory for new sessions, or with `export PATH="<dir>:$PATH"`, to add to `~/.zshrc` in zsh, `~/.bashrc` in bash or your shell profile in any other shell.
4. It runs `summer new demo` next to the checkout, with `--framework` pointing at it, which writes `sm-demo-app` and prints the next steps. The script itself ends with the `cd` line into the new application.
@@ -41,7 +41,7 @@ The script stops before cloning when the name is invalid or `sm-demo-app` alread
| `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 [sunrise](../../modules/sunrise/README.md) module holds the script itself.
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](../../modules/sunrise/README.md) module holds the script itself.
## Run the application
@@ -76,6 +76,43 @@ summer admin:create --email you@example.com --superuser
[Admin controllers](../backend/admin-controllers.md) shows how to add a controller, and [Users and permissions](../backend/users-and-permissions.md) how administrators sign in.
## Updating an application
To move an application to a newer framework, run the update script from inside the application directory:
```sh
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:
1. It checks that the current directory holds a `go.mod` and a `summer.yaml`, and reads the framework checkout from the `go.mod` replace that `summer new` wrote (`../summercms` for a generated application). A replace that names a module version instead of a local path is refused; update that requirement with `go get`.
2. It moves the checkout forward. A detached release checkout is fetched and moved, fast-forward only, to the newest release tag, or to `SUMMERCMS_REF` when set. A checkout on a branch is pulled from its upstream with `git 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.
3. When other `sm-*-app` directories next to the application or next to the checkout use the same checkout, it names them on one `not rebuilt` line. It never builds them or writes their files; each picks up the new framework on its next `summer build`.
4. It reinstalls the `summer` CLI from the checkout.
5. When the application's `.env` has no `SUMMER_ADMIN__JWT__SECRET` line, 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.
6. It runs `summer build` in the application and ends with a reminder to migrate and restart.
Then run the new migrations and restart the server:
```sh
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:
```sh
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: