- GPM channel is system.yaml gpm.releases (testing->stable), not GRAV_CHANNEL
- core upgrade is 'bin/gpm self-upgrade -y'; existing remote-upgrade-grav
target ('bin/grav upgrade') is broken and must be fixed
- add server content-pull + cache-clear + preflight steps to Phase 2
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
210 lines
10 KiB
Markdown
210 lines
10 KiB
Markdown
# Grav 2.0.4 Upgrade + GPM-Manage admin2/api/flex-objects — Design
|
||
|
||
**Date:** 2026-07-04
|
||
**Status:** Design approved — pending implementation plan
|
||
|
||
## Goal
|
||
|
||
Perform one coordinated upgrade of the intotheeast stack:
|
||
|
||
1. Bump Grav core from `2.0.0-rc.10` → `2.0.4` (stable).
|
||
2. Promote `admin2`, `api`, and `flex-objects` from bundle-extracted plugins to
|
||
**GPM-managed** plugins (this is "option B").
|
||
|
||
These two changes are **not separable**. The stable plugins hard-require the
|
||
stable core, so GPM enforces an atomic upgrade of the whole chain.
|
||
|
||
## Background / findings
|
||
|
||
### Version gaps
|
||
|
||
The project has been frozen on the final release candidate since the original
|
||
Grav 2.0 upgrade. Grav went stable on 2026-06-21; latest patch is `2.0.4`
|
||
(2026-06-29). No breaking changes exist within the 2.0.x line — the
|
||
2.0.1–2.0.4 releases are security hardening (XSS re-checks on editor Twig,
|
||
ZIP-bomb limits, `.htaccess` case-insensitive bypass fix) plus bugfixes.
|
||
|
||
Installed vs. bundled-in-2.0.4 versions:
|
||
|
||
| Plugin | Installed | 2.0.4 stable |
|
||
|---|---|---|
|
||
| core (grav) | 2.0.0-rc.10 | 2.0.4 |
|
||
| admin2 | 2.0.0-rc.15 | 2.0.9 |
|
||
| api | 1.0.0-rc.15 | 1.0.6 |
|
||
| flex-objects | 1.4.0-rc.7 | 1.4.3 |
|
||
| login | 3.8.9 | 3.8.11 |
|
||
| form | 9.1.6 | 9.1.8 |
|
||
| shortcode-core | 6.0.0 | 6.2.1 |
|
||
|
||
### Dependency chain (why it's atomic)
|
||
|
||
From the stable plugin blueprints:
|
||
|
||
- `api` 1.0.6 requires `grav >= 2.0.4` **and** `login >= 3.8.11`
|
||
- `admin2` 2.0.9 requires `api >= 1.0.6`
|
||
- `flex-objects` 1.4.3 requires `form >= 6.0.0`, `api >= 1.0.0`
|
||
|
||
So stable admin2/api cannot run on the rc.10 core — GPM would refuse. This is
|
||
the core reason option B is the right approach: `gpm` resolves and enforces the
|
||
entire chain automatically, which the previous manual-extract approach never
|
||
did.
|
||
|
||
### Three plugin management categories
|
||
|
||
The upgrade must account for the fact that plugins reached the servers three
|
||
different ways:
|
||
|
||
| Category | Plugins | In `plugins.txt`? | How installed | Upgrade mechanism |
|
||
|---|---|---|---|---|
|
||
| GPM-managed | email, error, form, login, problems, add-page-by-form, shortcode-gallery-plusplus | yes | `gpm install` | `gpm update` |
|
||
| Manually-placed → GPM (option B) | admin2, api, flex-objects | **will add** | hand-extracted from grav-admin zip | `plugins.txt` for fresh installs; `gpm update` on existing test env |
|
||
| Remote-only | git-sync | **no** (config gitignored, holds encrypted token) | installed directly on the server | documented separately; carried by `gpm update`; **disabled during upgrade** |
|
||
|
||
### git-sync compatibility
|
||
|
||
git-sync is version 3.4.4 with an explicit `compatibility: 2.0` flag and is one
|
||
of Grav's own reference plugins for the Admin Next / API. It is safe to carry
|
||
through the upgrade. Note the documented folders-YAML quirk
|
||
(`docs/working/git-sync-notes.md`): its config must list `folders` as an array,
|
||
never the UI-written comma-string.
|
||
|
||
### Local vs. server upgrade mechanisms differ
|
||
|
||
- **Local** bakes the core into the Docker image (`Dockerfile`) → upgrade by
|
||
rebuilding the image.
|
||
- **Server** is a native webroot install → core upgrades via
|
||
`bin/grav upgrade`, plugins via `bin/gpm update`.
|
||
|
||
The plan therefore has distinct local and remote steps.
|
||
|
||
## Decisions
|
||
|
||
- **Option B (GPM management)** for admin2/api/flex-objects. Add them to
|
||
`plugins.txt` so future fresh installs pull them via GPM.
|
||
- **Rollout order:** local → test → prod.
|
||
- **Prod is currently empty** → the prod phase is written as a *documented
|
||
fresh-install runbook only* and is **not executed** in this effort. Fresh prod
|
||
install uses the option-B-modified `server-install.sh` with
|
||
`GRAV_VERSION=2.0.4`.
|
||
- **Rollback = git.** All relevant data (`pages/`, `config/`, `accounts/`,
|
||
`themes/`) is committed. No separate backup step. Rollback is `git revert` of
|
||
this branch plus a rebuild/redeploy.
|
||
- **Upgrade verb on existing installs is `gpm update` (update-all)**, not
|
||
`gpm install <plugins.txt>`. `install` skips already-installed plugins and
|
||
never touches git-sync (which is not in the list); `update` upgrades every
|
||
installed plugin regardless of how it was placed, catching the manual and
|
||
remote-only categories in one shot.
|
||
- **git-sync stays out of `plugins.txt`** (that list is shared with local; git-sync
|
||
is remote-only with a manual encrypted token). Documented as separately
|
||
managed.
|
||
- **git-sync is disabled before the test upgrade and left disabled**, so the
|
||
upgrade cannot auto-commit reformatted/server-specific config back into the
|
||
shared Gitea `user` repo. The user validates first, then re-enables it as a
|
||
separate deliberate step.
|
||
|
||
## File changes (Phase 0)
|
||
|
||
| File | Change |
|
||
|---|---|
|
||
| `Dockerfile` | grav-admin zip URL `2.0.0-rc.10/grav-admin-v2.0.0-rc.10.zip` → `2.0.4/grav-admin-v2.0.4.zip`. Verified: the 2.0.4 zip still extracts to a `grav-admin/` folder, so the existing `cp` block is unchanged. |
|
||
| `plugins.txt` | add `api`, `admin2`, `flex-objects` (`form`, `login` already present as their deps) |
|
||
| `user/config/system.yaml` | **`gpm.releases: testing → stable`** — this is the authoritative GPM channel. `testing` is what has been serving RC/pre-release versions. Tracked in the `user` repo, so it applies to both local and server once pushed. |
|
||
| `docker-compose.yml` | `GRAV_CHANNEL=beta` → `production` for consistency only. This env drives the base image's `docker-entrypoint.sh`, **not** `bin/gpm`'s channel — `gpm.releases` above is what governs updates. |
|
||
| `scripts/server-install.sh` | remove the admin2/api stash+restore special-casing (lines 25–26 and 43–45); they now install via `gpm install` from `PLUGINS` |
|
||
| `Makefile` | **fix broken `remote-upgrade-grav`:** `php bin/grav upgrade` is not a real command — change to `php bin/gpm self-upgrade -y`. Add the new remote targets (below) to `REMOTE_TARGETS` so each gets `-test`/`-prod` variants. |
|
||
| `CLAUDE.md`, `docs/reference/architecture.md`, memory | update stack versions; document the three-category plugin model and the channel change |
|
||
|
||
### New Makefile targets
|
||
|
||
Added to the `REMOTE_TARGETS` list (Makefile:22–24) so the env-suffix macro
|
||
(Makefile:31–34) auto-generates `-test` / `-prod` variants:
|
||
|
||
- `remote-update-plugins` → `cd $(WEBROOT) && php bin/gpm update -y`
|
||
- `remote-git-sync-disable` → set `enabled: false` in
|
||
`$(WEBROOT)/user/config/plugins/git-sync.yaml` (touch only the `enabled` key;
|
||
never rewrite `folders`)
|
||
- `remote-git-sync-enable` → set `enabled: true` in the same file
|
||
|
||
`remote-upgrade-grav` exists but its command is **broken** (`php bin/grav
|
||
upgrade` is not a Grav CLI command) — it is fixed to `php bin/gpm self-upgrade
|
||
-y` as part of Phase 0. Core self-upgrade respects the `gpm.releases` channel.
|
||
|
||
**Verified CLI command names** (against the running rc.10 container):
|
||
`php bin/gpm self-upgrade -y` (core), `php bin/gpm update -y` (all plugins),
|
||
`php bin/grav cache` (clear cache; aliases `clearcache`/`cache-clear`).
|
||
|
||
> The exact idempotent shell used to toggle the `enabled` key is finalized in the
|
||
> implementation plan; it must not disturb the `folders` array or the encrypted
|
||
> token in `git-sync.yaml`.
|
||
|
||
## Phases
|
||
|
||
### Phase 0 — branch + edits
|
||
New branch off `main`. Apply all file changes above.
|
||
|
||
### Phase 1 — local
|
||
1. Remove the stale manually-extracted `admin2`, `api`, `flex-objects` folders
|
||
from `user/plugins/` so GPM does a clean install.
|
||
2. `make build` (core → 2.0.4)
|
||
3. `make start`
|
||
4. Install the newly-listed plugins **and** update the already-installed ones
|
||
to their 2.0.4-compatible versions via GPM. Note: `gpm install` skips plugins
|
||
that are already present, so `login` (3.8.9 → ≥3.8.11, required by `api`) and
|
||
`form` need `gpm update`, not `install`. The exact `gpm update` + `gpm install`
|
||
sequencing (run inside the container via `docker exec`) is pinned in the plan.
|
||
5. Assert versions: admin2 2.0.9, api 1.0.6, flex-objects 1.4.3, login ≥ 3.8.11.
|
||
6. **Smoke test:** admin2 login; submit `/post` → entry appears in the active
|
||
trip's dailies; `/gpx-manager` list + upload + delete; a trip page and a
|
||
story render; maps load.
|
||
|
||
Prerequisite: the Phase 0 config changes (esp. `system.yaml`
|
||
`gpm.releases: stable`) are committed and pushed to Gitea, or GPM on the server
|
||
will still resolve the `testing` channel and pull RCs.
|
||
|
||
1. `make remote-git-sync-disable-test`
|
||
2. `make remote-fetch-content-test` — pull latest `user/` content to the test
|
||
server so `system.yaml` `gpm.releases: stable` is in place before any GPM
|
||
operation.
|
||
3. `make remote-upgrade-grav-test` (core self-upgrade → 2.0.4)
|
||
4. `make remote-update-plugins-test` (`gpm update -y` — all plugins incl.
|
||
admin2/api/flex/git-sync/login/form)
|
||
5. Clear cache on the server (`php bin/grav cache`).
|
||
6. Review `git status` in the server's `user/` for unexpected config diffs;
|
||
handle any deliberately (do not blind-commit).
|
||
7. Smoke test on the test URL (same checklist as Phase 1).
|
||
8. **Leave git-sync disabled and notify the user.** After the user validates,
|
||
re-enable as a separate deliberate step: `make remote-git-sync-enable-test`,
|
||
then a `content-push` round-trip to confirm sync still works.
|
||
|
||
### Phase 3 — prod (DOCUMENTED, NOT EXECUTED)
|
||
Prod is empty, so this is a fresh install, not an upgrade. Documented as a
|
||
runbook:
|
||
|
||
- Run the option-B-modified `server-install.sh` with `GRAV_VERSION=2.0.4`
|
||
(`make remote-install-prod`).
|
||
- admin2/api/flex-objects now install via GPM from `plugins.txt` — no manual
|
||
extraction.
|
||
- Set up git-sync manually afterward: install, add the encrypted token, apply
|
||
the folders-YAML array fix (`docs/working/git-sync-notes.md`).
|
||
|
||
## Rollback
|
||
|
||
`git revert` the branch (Dockerfile + plugins.txt + docker-compose +
|
||
server-install.sh + Makefile) and rebuild/redeploy. Content, config, and
|
||
accounts are already in git, so no data restore is needed.
|
||
|
||
## Risks
|
||
|
||
- **git-sync auto-commit during upgrade** — mitigated by disabling git-sync
|
||
before the test upgrade and reviewing `git status` before re-enabling.
|
||
- **admin2/api behavioral changes across RC→stable** — these back the `/post`
|
||
form and `/gpx-manager`; covered by the smoke tests, which are the
|
||
highest-weight validation in this effort.
|
||
- **GPM channel** — `gpm.releases` must be `stable` on the server *before* any
|
||
`gpm update`/`self-upgrade`, or GPM pulls RCs. Enforced by pushing the
|
||
`system.yaml` change and running `remote-fetch-content` first (Phase 2 step 2).
|
||
- **`bin/gpm self-upgrade` on shared hosting** — Grav 2.0.3 fixed self-upgrade
|
||
failures on shared-folder setups. On the native server this can still be
|
||
fragile; run `php bin/gpm preflight` first and use `-o/--overwrite` if a retry
|
||
is needed.
|