Coordinated upgrade of core rc.10 -> 2.0.4 and promotion of admin2/api/flex-objects to GPM management (option B). Documents the dependency-forced atomicity, the three plugin-management categories, git-sync handling (disable-during-upgrade, remote-only), and the local -> test -> prod(documented-only) rollout. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
8.8 KiB
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:
- Bump Grav core from
2.0.0-rc.10→2.0.4(stable). - Promote
admin2,api, andflex-objectsfrom 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:
api1.0.6 requiresgrav >= 2.0.4andlogin >= 3.8.11admin22.0.9 requiresapi >= 1.0.6flex-objects1.4.3 requiresform >= 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 viabin/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.txtso 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.shwithGRAV_VERSION=2.0.4. - Rollback = git. All relevant data (
pages/,config/,accounts/,themes/) is committed. No separate backup step. Rollback isgit revertof this branch plus a rebuild/redeploy. - Upgrade verb on existing installs is
gpm update(update-all), notgpm install <plugins.txt>.installskips already-installed plugins and never touches git-sync (which is not in the list);updateupgrades 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
userrepo. 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) |
docker-compose.yml |
GRAV_CHANNEL=beta → production so GPM serves stable releases, not RCs |
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 |
add 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 -yremote-git-sync-disable→ setenabled: falsein$(WEBROOT)/user/config/plugins/git-sync.yaml(touch only theenabledkey; never rewritefolders)remote-git-sync-enable→ setenabled: truein the same file
remote-upgrade-grav (bin/grav upgrade) already exists.
The exact idempotent shell used to toggle the
enabledkey is finalized in the implementation plan; it must not disturb thefoldersarray or the encrypted token ingit-sync.yaml.
Phases
Phase 0 — branch + edits
New branch off main. Apply all file changes above.
Phase 1 — local
- Remove the stale manually-extracted
admin2,api,flex-objectsfolders fromuser/plugins/so GPM does a clean install. make build(core → 2.0.4)make start- Install the newly-listed plugins and update the already-installed ones
to their 2.0.4-compatible versions via GPM. Note:
gpm installskips plugins that are already present, sologin(3.8.9 → ≥3.8.11, required byapi) andformneedgpm update, notinstall. The exactgpm update+gpm installsequencing (run inside the container viadocker exec) is pinned in the plan. - Assert versions: admin2 2.0.9, api 1.0.6, flex-objects 1.4.3, login ≥ 3.8.11.
- Smoke test: admin2 login; submit
/post→ entry appears in the active trip's dailies;/gpx-managerlist + upload + delete; a trip page and a story render; maps load.
Phase 2 — test
make remote-git-sync-disable-testmake remote-upgrade-grav-test(core → 2.0.4)make remote-update-plugins-test(gpm update -y— all plugins incl. admin2/api/flex/git-sync/login/form)- Review
git statusin the server'suser/for unexpected config diffs; handle any deliberately (do not blind-commit). - Smoke test on the test URL (same checklist as Phase 1).
- 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 acontent-pushround-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.shwithGRAV_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 statusbefore re-enabling. - admin2/api behavioral changes across RC→stable — these back the
/postform and/gpx-manager; covered by the smoke tests, which are the highest-weight validation in this effort. - GPM channel — the server must resolve stable releases; verify the channel
(
gpm.releases/GRAV_CHANNEL) yields 2.0.x stable, not RCs.