Files
natascha-rieter-nl/docs/handover/GRAV2-MIGRATION-HANDOVER.md
m038 8c93dfd7c9 Sync local state with remote after untracked local evolution
Local site-ai/ had diverged significantly from the last commit pushed
to Gitea (new remote-env-setup/remove targets, SITE_CONFIG_DIR/MAIN_REPO
split, credential cleanup in server-install.sh, migration docs) without
ever being committed. This catches the repo up to what's actually on disk.

Also untracks the legacy user/ subtree left over from before content was
split into its own standalone repo (natascha-rieter.nl-user) — user/ is
gitignored here and stays a separate git repo, unaffected by this commit.
2026-08-30 14:48:54 +02:00

8.9 KiB

Grav 1.7 → 2.0 Migration Simulation — Handover

Status as of 2026-08-25: Investigation in progress, not concluded. Nothing has touched production. This document is for a future session to pick up where this one left off.

What this is

The user asked (advisory, then "let's look into it — zero impact on production") what the best strategy is to upgrade local Grav from 1.7.49.5 to the latest 2.x, and to try installing a 2.x admin UI. Per explicit instruction, instead of upgrading in place, a second, fully isolated Docker container running Grav 2.0.21 was stood up next to the existing 1.7 dev container, using a copy of the real site content, to see what breaks before committing to a real migration.

Nothing about the live dev container, the user/ git content repo, or production has been changed. All make remote-* targets remain untouched.

Current environment state

Container Image Port Data State
existing dev natascha_grav lscr.io/linuxserver/grav:1.7.49.5-ls244 8080 ./user (live mount) Up, compose project site-ai (docker-compose.yml)
migration sandbox natascha_grav2_test lscr.io/linuxserver/grav:2.0.21-ls271 8081 ./migration-test/user (copy, no .git) Stopped (exited cleanly, restart: "no" so it doesn't auto-start)

Docker runs via Colima (no Docker Desktop). Colima must be running before any of this works: check with colima status, start with colima start.

To resume the sandbox container

cd /Users/mischa/Nextcloud/Projects/Natascha/natascha-rieter.nl/site-ai
docker compose -f docker-compose.grav2.yml -p natascha-grav2-test up -d

Then it's reachable at http://localhost:8081 (front-end) and http://localhost:8081/admin (admin login).

To tear it down completely (once done investigating)

cd /Users/mischa/Nextcloud/Projects/Natascha/natascha-rieter.nl/site-ai
docker compose -f docker-compose.grav2.yml -p natascha-grav2-test down
rm -rf migration-test

docker-compose.grav2.yml itself can also be deleted once the simulation is finished and a real decision is made — it was created purely as a scratch sandbox definition and is separate from the production-mirroring docker-compose.yml.

Files created this investigation (all under site-ai/)

  • docker-compose.grav2.yml — the sandbox compose file (image 2.0.21-ls271, port 8081, mounts ./migration-test/user, restart: "no").
  • migration-test/user/rsync -a --exclude='.git' copy of the live user/ folder (~50M, .git excluded on purpose since user/ is itself a separate git content-sync repo — copying .git would have created a confusing nested repo). This copy is stale as of whenever it was taken; re-sync before further testing if user/ content has changed since:
    rsync -a --exclude='.git' ./user/ ./migration-test/user/
    
  • ~/.docker/cli-plugins/docker-compose (outside repo, machine-level) — symlink to the Homebrew docker-compose binary, registering it as the docker compose CLI plugin. This was a durable fix for make start (which calls docker compose up -d and previously failed with unknown shorthand flag: 'd' because no compose plugin was registered). Chosen explicitly by the user over editing the Makefile. Not something a future session needs to redo, but worth knowing about if make start ever breaks again on a different machine.

Findings so far

  1. Grav 2.0 is not an in-place upgrade. Official docs say so explicitly. Also, in this project's setup, Grav core lives inside the Docker image, not in the ./user volume mount — so an in-place gpm selfupgrade wouldn't persist across container recreation anyway. The real upgrade path is an image tag swap (1.7.49.5-ls2442.0.21-ls271 or whatever is current) combined with a content/plugin compatibility pass.

  2. Front-end renders correctly on Grav 2.0.21 core against the unmodified 1.7-era user/ content and theme — verified via curl against http://localhost:8081/, including the langswitcher redirect (//nl).

  3. Classic admin plugin (v1.10.55) login page renders correctly on Grav 2.0.21 — confirmed via curl http://localhost:8081/admin: full CSS/JS asset loading, Dutch-localized labels, working nonce, no visible PHP errors. This is a better result than official Grav 2.0 docs suggest — the docs imply classic admin is incompatible with 2.0 and that migration should go through admin2 (the alpha SvelteKit rewrite) instead.

  4. No errors, exceptions, fatals, deprecation notices, or warnings appeared in docker logs natascha_grav2_test for either the / or /admin requests.

  5. bin/grav/bin/gpm CLI layout is the same path as 1.7 (/app/www/public/bin/, must cd there first — FATAL: Must be run from ROOT directory of Grav! otherwise), but the CLI command set differs between 1.7 and 2.0 — e.g. php bin/grav plugins doesn't exist in 2.0 (confirm via php bin/grav list).

  6. Grav's built-in page-system-validator tool (meant for before/after render-diffing across an upgrade) cannot be used from the CLI on this setup — it throws a PHP fatal (Call to a member function param() on null in .../admin/admin.php:354) because the admin plugin's onPageInitialized() handler expects an HTTP request object that doesn't exist under CLI invocation. This happened on the old 1.7 container, so it's an artifact of the admin plugin's CLI handling generally, not a 2.0-specific regression. Don't waste time trying to make this tool work as a validation method — it needs a different approach (see "Next steps").

What has NOT been tested yet

  • The actual admin dashboard post-login — page editing, media management, anything behind auth. This requires real admin credentials, which were deliberately not obtained/guessed. If the user provides a throwaway password (or creates one via bin/grav user create in the sandbox container, which is safe since it's isolated test data), this is the obvious next concrete step.

  • Individual plugin compatibility, specifically the ones most likely to break because they hook deep into core APIs that changed in 2.0:

    • git-sync
    • flex-objects
    • admin-media-move, admin-media-replace, admin-media-actions
    • automagic-images
    • social-meta-tags
    • langswitcher (front-end redirect already confirmed working, but not deeper admin-side behavior)
    • draft-preview

    Grav 2.0's plugin blueprints can declare a compatibility: key; check each plugin's blueprints.yaml under migration-test/user/plugins/<name>/ for this key, and/or check each plugin's upstream repo/changelog for 2.0 support statements. bin/gpm info <plugin> may also surface something once run from inside the sandbox container (untested).

  • admin2 (the alpha SvelteKit/Vite/Tailwind admin rewrite) has not been installed or tested in the sandbox at all. It's not GPM-installable — it requires manually git-cloning grav-plugin-api and grav-plugin-admin2 into user/plugins/. Given it's explicitly alpha/non-production-ready upstream, and classic admin already renders fine on 2.0 in this test, it's worth explicitly asking the user whether they still want this installed before spending time on it, rather than assuming yes from the original advisory question.

Suggested next steps (pick up here)

  1. Bring the sandbox back up (command above), re-sync migration-test/user from ./user if content has changed since.
  2. Either:
    • Ask the user for a throwaway admin password to log in and click through the real dashboard/page-editing/media flows, or
    • Create a fresh test admin user inside the sandbox container only (docker exec -it natascha_grav2_test sh -c "cd /app/www/public && php bin/grav user create") — safe since it only affects the isolated migration-test/user copy.
  3. Go through the plugin list above one by one: check blueprint compatibility: declarations, and exercise each plugin's actual feature (git-sync a commit, upload/replace media, edit a flex-objects-backed page) inside the sandbox to see what actually breaks vs. what merely lacks a compatibility flag.
  4. Once a real compatibility picture exists, come back to the original question — decide whether the production upgrade path should be: (a) image-tag swap + fix whatever plugins broke, or (b) use Grav's official migrate-grav GPM plugin (side-by-side staged migration with Reset/Restart/Promote controls) instead of doing it by hand. This official tool was identified during research but deliberately not used yet, since the user asked specifically for a separate-container simulation first.
  5. Revisit whether admin2 is still wanted given classic admin's better-than-expected 2.0 compatibility.