- Task 2 Step 8: version-assert Expected output now includes the 'version:' prefix the grep command actually prints (coherence). - Rollback: state core self-upgrade is forward-only (no revert) and warn that server-install.sh must never be used as a live-server rollback — it rm -rf's the untracked git-sync.yaml encrypted token (feasibility + adversarial). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Np4cMQLF77i664CAQXySzU
523 lines
19 KiB
Markdown
523 lines
19 KiB
Markdown
# Grav 2.0.4 Upgrade + GPM-Manage Plugins — Implementation Plan
|
||
|
||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||
|
||
**Status:** 📋 Not started
|
||
|
||
**Goal:** Upgrade Grav core `2.0.0-rc.10` → `2.0.4` stable and promote `admin2`/`api`/`flex-objects` to GPM management, validated on local then the remote test env (prod is documented-only).
|
||
|
||
**Architecture:** One dependency-forced atomic upgrade. Local core is baked into the Docker image (rebuild); the server upgrades in place via `bin/gpm self-upgrade` + `bin/gpm update`. The GPM release channel is switched from `testing` to `stable` in `user/config/system.yaml`. `git-sync` is disabled for the duration of the remote upgrade and left off pending user validation.
|
||
|
||
**Tech Stack:** Grav 2.0 (PHP 8.3), GPM CLI, Docker Compose, Make (env-suffixed remote targets), Gitea content sync.
|
||
|
||
**Spec:** `docs/working/specs/2026-07-04-grav-2.0.4-upgrade-design.md`
|
||
|
||
## Global Constraints
|
||
|
||
- Version floors (GPM enforces): `grav >= 2.0.4`, `api >= 1.0.6`, `admin2 >= 2.0.9`, `flex-objects >= 1.4.3`, `login >= 3.8.11`, `form >= 6.0.0`. Assert with `>=`, not `==` — GPM may serve a newer stable patch at execution time.
|
||
- Only write inside `travel-blog-intotheeast/` or its subfolders.
|
||
- **Dual git repos.** The project root is one repo; `user/` is a *separate* repo where only `pages/ config/ accounts/ themes/` are tracked (`plugins/` is gitignored except `cache-on-save/` and `story-blocks/`). Changes to `user/config/system.yaml` commit to the **user repo** and reach the server via `make content-push` → server pull; everything else commits to the **root repo**.
|
||
- GPM channel authority is `user/config/system.yaml` → `gpm.releases` (must be `stable` on the server *before* any GPM op). `GRAV_CHANNEL` in docker-compose is cosmetic/consistency only.
|
||
- Never read `.env*`. Use `make remote-*` targets for all server ops.
|
||
- Do not touch `twig.cache` (stays `false` in dev per CLAUDE.md).
|
||
- `git-sync` is remote-only: never add it to `plugins.txt`; disable it during the remote upgrade and leave it disabled until the user re-enables.
|
||
- Prod is empty → **Phase 3 is documentation only, never executed.**
|
||
- Verified CLI names (against the rc.10 container): `php bin/gpm self-upgrade -y` (core), `php bin/gpm update -y` (all plugins), `php bin/grav cache` (clear cache). `bin/grav upgrade` does **not** exist.
|
||
|
||
---
|
||
|
||
## File structure
|
||
|
||
| File | Repo | Responsibility |
|
||
|---|---|---|
|
||
| `Dockerfile` | root | Baked local core version (grav-admin zip URL) |
|
||
| `plugins.txt` | root | GPM plugin manifest — gains admin2/api/flex-objects |
|
||
| `docker-compose.yml` | root | `GRAV_CHANNEL` cosmetic bump |
|
||
| `user/config/system.yaml` | **user** | Authoritative GPM channel (`gpm.releases`) |
|
||
| `scripts/server-install.sh` | root | Fresh-install script — drop admin2/api special-casing |
|
||
| `scripts/git-sync-toggle.sh` | root | New: idempotently set git-sync `enabled:` on the server |
|
||
| `Makefile` | root | Fix `remote-upgrade-grav`; add 3 remote targets |
|
||
| `docs/working/plans/...` `CLAUDE.md` `docs/reference/architecture.md` | root | Runbook + stack docs |
|
||
|
||
---
|
||
|
||
## Task 1: Phase 0 — core, plugin-list, and channel edits
|
||
|
||
**Files:**
|
||
- Modify: `Dockerfile` (the grav-admin zip URL line)
|
||
- Modify: `plugins.txt`
|
||
- Modify: `docker-compose.yml` (`GRAV_CHANNEL`)
|
||
- Modify: `user/config/system.yaml` (`gpm.releases`)
|
||
|
||
**Interfaces:**
|
||
- Produces: local image that installs Grav 2.0.4; `plugins.txt` containing `api`, `admin2`, `flex-objects`; `stable` GPM channel consumed by Task 2 (local) and Task 5 (server).
|
||
|
||
- [ ] **Step 1: Bump the core version in the Dockerfile**
|
||
|
||
In `Dockerfile`, change the download URL:
|
||
|
||
```dockerfile
|
||
RUN curl -sL 'https://github.com/getgrav/grav/releases/download/2.0.4/grav-admin-v2.0.4.zip' \
|
||
-o /tmp/grav-admin.zip \
|
||
```
|
||
|
||
(Only the URL changes — the zip still extracts to `/tmp/grav-admin/`, so every `cp` line below it is unchanged.)
|
||
|
||
- [ ] **Step 2: Add the three plugins to `plugins.txt`**
|
||
|
||
Append these lines to `plugins.txt` (order is not significant; GPM resolves deps):
|
||
|
||
```
|
||
api
|
||
admin2
|
||
flex-objects
|
||
```
|
||
|
||
- [ ] **Step 3: Switch the GPM channel to stable**
|
||
|
||
In `user/config/system.yaml`, under the `gpm:` block (currently line ~212):
|
||
|
||
```yaml
|
||
gpm:
|
||
releases: stable
|
||
official_gpm_only: true
|
||
```
|
||
|
||
(Change `testing` → `stable`. Leave `official_gpm_only` as-is.)
|
||
|
||
- [ ] **Step 4: Bump the cosmetic channel env**
|
||
|
||
In `docker-compose.yml`, under the `grav` service environment:
|
||
|
||
```yaml
|
||
- GRAV_CHANNEL=production
|
||
```
|
||
|
||
- [ ] **Step 5: Commit the user-repo change**
|
||
|
||
```bash
|
||
cd user
|
||
git add config/system.yaml
|
||
git commit -m "config: switch GPM release channel testing -> stable"
|
||
cd ..
|
||
```
|
||
|
||
Expected: commit succeeds in the `user` repo.
|
||
|
||
- [ ] **Step 6: Commit the root-repo changes**
|
||
|
||
```bash
|
||
git add Dockerfile plugins.txt docker-compose.yml
|
||
git commit -m "build: pin Grav core 2.0.4 and add admin2/api/flex-objects to plugins.txt"
|
||
```
|
||
|
||
Expected: commit succeeds on branch `grav-2.0.4-upgrade`.
|
||
|
||
---
|
||
|
||
## Task 2: Phase 1 — local build, clean install, validation
|
||
|
||
**Files:**
|
||
- No file edits. Executes the Task 1 changes locally.
|
||
|
||
**Interfaces:**
|
||
- Consumes: Task 1 (Dockerfile 2.0.4, plugins.txt, stable channel).
|
||
- Produces: a proven-working local 2.0.4 stack — the go/no-go gate for the remote phases.
|
||
|
||
- [ ] **Step 1: Remove the stale manually-extracted plugin folders**
|
||
|
||
These were hand-extracted from the rc.10 bundle; GPM must install them fresh.
|
||
|
||
```bash
|
||
rm -rf user/plugins/admin2 user/plugins/api user/plugins/flex-objects
|
||
```
|
||
|
||
Expected: the three folders are gone (`ls user/plugins/` no longer lists them). They are gitignored, so `git status` in `user/` is unaffected.
|
||
|
||
- [ ] **Step 2: Rebuild the image with core 2.0.4**
|
||
|
||
```bash
|
||
make build
|
||
```
|
||
|
||
Expected: build completes; the RUN layer downloads `grav-admin-v2.0.4.zip`.
|
||
|
||
- [ ] **Step 3: Recreate the container**
|
||
|
||
```bash
|
||
make start
|
||
```
|
||
|
||
Expected: `intotheeast_grav` is up on http://localhost:8081.
|
||
|
||
- [ ] **Step 4: Confirm the core version is 2.0.4**
|
||
|
||
```bash
|
||
docker exec intotheeast_grav php bin/grav --version
|
||
```
|
||
|
||
Expected output contains: `Grav CLI Application 2.0.4` (or a newer 2.0.x).
|
||
|
||
- [ ] **Step 5: Update already-installed GPM plugins to stable**
|
||
|
||
This bumps `login` (3.8.9 → ≥3.8.11, required by `api`) and `form` before the new plugins install. `gpm install` alone would skip them because they already exist.
|
||
|
||
```bash
|
||
docker exec -w /var/www/html intotheeast_grav php bin/gpm update -y
|
||
```
|
||
|
||
Expected: `login`, `form`, `shortcode-core`, etc. report as updated (or already up to date).
|
||
|
||
- [ ] **Step 6: Install the newly-listed plugins**
|
||
|
||
```bash
|
||
make install-plugins
|
||
```
|
||
|
||
Expected: `admin2`, `api`, `flex-objects` install; their dependencies resolve against the now-current `login`/`form`; no "requires grav >= 2.0.4" errors.
|
||
|
||
- [ ] **Step 7: Clear the cache**
|
||
|
||
```bash
|
||
docker exec intotheeast_grav php bin/grav cache
|
||
```
|
||
|
||
Expected: "Cache cleared" output.
|
||
|
||
- [ ] **Step 8: Assert plugin versions meet the floors**
|
||
|
||
```bash
|
||
docker exec intotheeast_grav sh -c 'cd /var/www/html && for p in admin2 api flex-objects login form; do printf "%s: " "$p"; grep -m1 "^version:" user/plugins/$p/blueprints.yaml; done'
|
||
```
|
||
|
||
Expected (at least): `admin2: version: 2.0.9`, `api: version: 1.0.6`, `flex-objects: version: 1.4.3`, `login: version: 3.8.11`, `form: version: 9.1.8` — equal or higher.
|
||
|
||
- [ ] **Step 9: Run the automated smoke suite**
|
||
|
||
This exercises the posting pipeline (`test-post` submits via the real form → add-page-by-form → cache-on-save) and renders pages via Playwright — the exact admin2/api-critical path.
|
||
|
||
```bash
|
||
make test
|
||
```
|
||
|
||
Expected: `test-config`, `test-post`, and `test-ui` all pass.
|
||
|
||
- [ ] **Step 10: Manual browser spot-check**
|
||
|
||
Visit and confirm each renders without error:
|
||
- http://localhost:8081/ (home)
|
||
- the active trip page (`/trips/japan-korea-2026`) — filter bar + map load
|
||
- one story page — hero + shortcodes render
|
||
- http://localhost:8081/admin2 — login page loads; log in
|
||
- http://localhost:8081/gpx-manager — list loads; upload a small `.gpx`, then delete it
|
||
|
||
Expected: all load; no PHP errors in `docker logs intotheeast_grav`.
|
||
|
||
- [ ] **Step 11: Checkpoint (no commit needed)**
|
||
|
||
No files changed in this task. If any step failed, stop and diagnose before proceeding — this is the go/no-go gate for remote work.
|
||
|
||
---
|
||
|
||
## Task 3: Remote Makefile targets (fix + additions)
|
||
|
||
**Files:**
|
||
- Create: `scripts/git-sync-toggle.sh`
|
||
- Modify: `Makefile` (fix `remote-upgrade-grav`; add `remote-update-plugins`, `remote-git-sync-disable`, `remote-git-sync-enable`; register the new targets in `REMOTE_TARGETS`)
|
||
|
||
**Interfaces:**
|
||
- Produces: `make remote-upgrade-grav-<env>`, `make remote-update-plugins-<env>`, `make remote-git-sync-disable-<env>`, `make remote-git-sync-enable-<env>` — consumed by Task 5.
|
||
|
||
- [ ] **Step 1: Create the git-sync toggle script**
|
||
|
||
Create `scripts/git-sync-toggle.sh` (piped to the server via `bash -s`, matching the `server-install.sh` pattern). It only ever rewrites the top-level `enabled:` key — never `folders` or the encrypted token.
|
||
|
||
```bash
|
||
#!/bin/bash
|
||
set -e
|
||
|
||
FILE="$1"
|
||
STATE="$2"
|
||
: "${FILE:?usage: git-sync-toggle.sh <git-sync.yaml path> <true|false>}"
|
||
: "${STATE:?usage: git-sync-toggle.sh <git-sync.yaml path> <true|false>}"
|
||
|
||
if [ ! -f "$FILE" ]; then
|
||
echo "ERROR: $FILE not found — is git-sync installed on this server?" >&2
|
||
exit 1
|
||
fi
|
||
|
||
if grep -qE '^enabled:' "$FILE"; then
|
||
sed -i -E "s/^enabled:.*/enabled: ${STATE}/" "$FILE"
|
||
else
|
||
printf 'enabled: %s\n' "$STATE" | cat - "$FILE" > "$FILE.tmp" && mv "$FILE.tmp" "$FILE"
|
||
fi
|
||
|
||
echo "git-sync now: $(grep -E '^enabled:' "$FILE")"
|
||
```
|
||
|
||
- [ ] **Step 2: Make it executable**
|
||
|
||
```bash
|
||
chmod +x scripts/git-sync-toggle.sh
|
||
```
|
||
|
||
- [ ] **Step 3: Fix the broken `remote-upgrade-grav` target**
|
||
|
||
In `Makefile`, replace the body of `remote-upgrade-grav` (currently `php bin/grav upgrade`, which is not a real command):
|
||
|
||
```make
|
||
remote-upgrade-grav: guard-env
|
||
$(SSH) "cd $(WEBROOT) && php bin/gpm self-upgrade -y && php bin/grav cache"
|
||
```
|
||
|
||
- [ ] **Step 4: Add the plugin-update target**
|
||
|
||
Add below `remote-install-plugins`:
|
||
|
||
```make
|
||
remote-update-plugins: guard-env
|
||
$(SSH) "cd $(WEBROOT) && php bin/gpm update -y && php bin/grav cache"
|
||
```
|
||
|
||
- [ ] **Step 5: Add the git-sync toggle targets**
|
||
|
||
```make
|
||
remote-git-sync-disable: guard-env
|
||
$(SSH) "bash -s -- '$(WEBROOT)/user/config/plugins/git-sync.yaml' false" < scripts/git-sync-toggle.sh
|
||
|
||
remote-git-sync-enable: guard-env
|
||
$(SSH) "bash -s -- '$(WEBROOT)/user/config/plugins/git-sync.yaml' true" < scripts/git-sync-toggle.sh
|
||
```
|
||
|
||
- [ ] **Step 6: Add a server content-status target**
|
||
|
||
For reviewing config drift after the plugin upgrade without raw SSH:
|
||
|
||
```make
|
||
remote-content-status: guard-env
|
||
$(SSH) "cd $(WEBROOT)/user && git status --short && echo '--- config diff ---' && git diff -- config/"
|
||
```
|
||
|
||
- [ ] **Step 7: Register the new targets for env-suffix generation**
|
||
|
||
In `Makefile`, extend the `REMOTE_TARGETS` list so the `-test`/`-prod` variants get generated:
|
||
|
||
```make
|
||
REMOTE_TARGETS := remote-env-setup remote-env-remove remote-wipe remote-install \
|
||
remote-fetch remote-fetch-content remote-install-plugins remote-update-plugins \
|
||
remote-upgrade-grav remote-git-sync-disable remote-git-sync-enable \
|
||
remote-content-status remote-clean remote-maintenance-on remote-maintenance-off
|
||
```
|
||
|
||
- [ ] **Step 8: Verify the targets exist and expand correctly (dry run)**
|
||
|
||
```bash
|
||
make -n remote-update-plugins-test
|
||
make -n remote-git-sync-disable-test
|
||
make -n remote-upgrade-grav-test
|
||
make -n remote-content-status-test
|
||
```
|
||
|
||
Expected: each prints the intended `ssh ...` command with `ENV=test` resolved, and no "No rule to make target" error. (No server is contacted by `-n`.)
|
||
|
||
- [ ] **Step 9: Commit**
|
||
|
||
```bash
|
||
git add scripts/git-sync-toggle.sh Makefile
|
||
git commit -m "build: fix remote-upgrade-grav; add remote plugin-update, git-sync toggle, content-status targets"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 4: Fresh-install script cleanup (option B server-side)
|
||
|
||
**Files:**
|
||
- Modify: `scripts/server-install.sh` (remove admin2/api stash+restore)
|
||
|
||
**Interfaces:**
|
||
- Consumes: `plugins.txt` now containing admin2/api/flex-objects (Task 1).
|
||
- Produces: a fresh-install path where admin2/api/flex install purely via `gpm install $PLUGINS`.
|
||
|
||
- [ ] **Step 1: Remove the zip-stash lines**
|
||
|
||
In `scripts/server-install.sh`, delete lines 25–26 (the admin2/api stash into `/tmp`):
|
||
|
||
```bash
|
||
cp -rf grav-admin/user/plugins/admin2 /tmp/admin2-plugin
|
||
cp -rf grav-admin/user/plugins/api /tmp/api-plugin
|
||
```
|
||
|
||
- [ ] **Step 2: Remove the restore lines**
|
||
|
||
Delete lines 43–45 (the restore after the user re-clone):
|
||
|
||
```bash
|
||
cp -rf /tmp/admin2-plugin user/plugins/admin2
|
||
cp -rf /tmp/api-plugin user/plugins/api
|
||
rm -rf /tmp/admin2-plugin /tmp/api-plugin
|
||
```
|
||
|
||
Leave `mkdir -p user/plugins user/accounts user/data` in place. admin2/api/flex now come from `php bin/gpm install $PLUGINS -y` (unchanged line ~48).
|
||
|
||
- [ ] **Step 3: Syntax-check the script**
|
||
|
||
```bash
|
||
bash -n scripts/server-install.sh
|
||
```
|
||
|
||
Expected: no output (valid syntax).
|
||
|
||
- [ ] **Step 4: Commit**
|
||
|
||
```bash
|
||
git add scripts/server-install.sh
|
||
git commit -m "build: drop admin2/api zip-stash from server-install; install via GPM (option B)"
|
||
```
|
||
|
||
---
|
||
|
||
## Task 5: Phase 2 — test env upgrade
|
||
|
||
**Files:**
|
||
- No file edits. Executes against the test environment using Task 3 targets.
|
||
|
||
**Interfaces:**
|
||
- Consumes: Tasks 1–4 (pushed to Gitea), the `-test` make targets.
|
||
- Produces: test env on 2.0.4 with GPM-managed plugins, validated; git-sync left disabled.
|
||
|
||
- [ ] **Step 1: Push all changes to Gitea**
|
||
|
||
The server pulls `user/` content (incl. the stable-channel `system.yaml`) from Gitea; the root repo pushes normally.
|
||
|
||
```bash
|
||
git push origin grav-2.0.4-upgrade # or merge to the branch the server tracks, per your deploy convention
|
||
make content-push # pushes the user repo commit (system.yaml) to Gitea
|
||
```
|
||
|
||
Expected: both remotes updated. (Confirm with the user which branch the test server tracks before pushing.)
|
||
|
||
- [ ] **Step 2: Disable git-sync on test**
|
||
|
||
```bash
|
||
make remote-git-sync-disable-test
|
||
```
|
||
|
||
Expected: prints `git-sync now: enabled: false`.
|
||
|
||
- [ ] **Step 3: Pull latest content to the test server**
|
||
|
||
Brings the `gpm.releases: stable` change onto the server *before* any GPM op.
|
||
|
||
```bash
|
||
make remote-fetch-content-test
|
||
```
|
||
|
||
Expected: server `user/` fast-forwards; `user/config/system.yaml` shows `releases: stable`.
|
||
|
||
- [ ] **Step 4: Upgrade the core on test**
|
||
|
||
```bash
|
||
make remote-upgrade-grav-test
|
||
```
|
||
|
||
Expected: `bin/gpm self-upgrade` moves core rc.10 → 2.0.4 (stable channel); cache cleared. If it fails on a shared-folder error (see spec Risks), retry is safe — `self-upgrade` supports `-o/--overwrite`; add it to the target temporarily if a retry is needed.
|
||
|
||
- [ ] **Step 5: Update all plugins on test**
|
||
|
||
```bash
|
||
make remote-update-plugins-test
|
||
```
|
||
|
||
Expected: admin2 → ≥2.0.9, api → ≥1.0.6, flex-objects → ≥1.4.3, login → ≥3.8.11, form, git-sync all update to their stable versions; cache cleared.
|
||
|
||
- [ ] **Step 6: Review server config drift**
|
||
|
||
Inspect the server `user/` working tree for unexpected rewrites from the plugin upgrades (do NOT blind-commit):
|
||
|
||
```bash
|
||
make remote-content-status-test
|
||
```
|
||
|
||
Expected: review any `config/` diffs deliberately. Discard server-specific/reformatting churn; keep only intended changes. (git-sync is disabled, so nothing auto-commits while you review.)
|
||
|
||
- [ ] **Step 7: Smoke-test the test URL**
|
||
|
||
Against the test site (URL per your test env), confirm:
|
||
- home, a trip page, a story render
|
||
- admin2 login works
|
||
- submit one `/post` → the entry appears in the active trip's dailies
|
||
- `/gpx-manager` lists, uploads, and deletes a file
|
||
|
||
Expected: all pass. (git-sync stays disabled, so the new post will not auto-sync yet — that's expected and verified in Step 9.)
|
||
|
||
- [ ] **Step 8: Notify the user — validation checkpoint**
|
||
|
||
Report results and explicitly state that **git-sync remains disabled** on test pending their validation. Do not re-enable automatically.
|
||
|
||
- [ ] **Step 9: (User-gated) Re-enable git-sync and verify sync**
|
||
|
||
After the user confirms validation:
|
||
|
||
```bash
|
||
make remote-git-sync-enable-test
|
||
make content-push # or trigger a content change; confirm it syncs through
|
||
```
|
||
|
||
Expected: `git-sync now: enabled: true`; a content round-trip syncs between the test server and Gitea.
|
||
|
||
---
|
||
|
||
## Task 6: Docs, prod runbook, and memory
|
||
|
||
**Files:**
|
||
- Modify: `CLAUDE.md` (stack versions + plugin-management model)
|
||
- Modify: `docs/reference/architecture.md` (versions/channel)
|
||
- Modify: `docs/working/plans/2026-07-04-grav-2.0.4-upgrade.md` (this file — Phase 3 runbook + Status)
|
||
- Modify: memory files under the auto-memory dir (project-grav2-upgrade, project-plugin-architecture)
|
||
|
||
**Interfaces:**
|
||
- Consumes: the completed local + test upgrade.
|
||
- Produces: current docs; an executable-but-unexecuted prod runbook.
|
||
|
||
- [ ] **Step 1: Update the stack facts in `CLAUDE.md`**
|
||
|
||
Change the "Current stack" block: Grav `2.0.4` (not rc.10); Admin2 to the installed stable version; note that admin2/api/flex-objects are now **GPM-managed via `plugins.txt`** (no longer hand-extracted); note `gpm.releases: stable`.
|
||
|
||
- [ ] **Step 2: Update `docs/reference/architecture.md`**
|
||
|
||
Reflect core 2.0.4, stable channel, and the three-category plugin model (GPM-managed / former-manual-now-GPM / remote-only git-sync).
|
||
|
||
- [ ] **Step 3: Write the Phase 3 prod runbook**
|
||
|
||
Append a "Phase 3 — Production (fresh install, NOT executed)" section to this plan documenting: run `make remote-install-prod` with `GRAV_VERSION=2.0.4`; admin2/api/flex install via GPM from `plugins.txt`; then set up git-sync manually (install, add encrypted token, apply the `folders:` array fix per `docs/working/git-sync-notes.md`), and leave it disabled until first validation.
|
||
|
||
- [ ] **Step 4: Update memory**
|
||
|
||
Update `project-grav2-upgrade.md` (now on 2.0.4 stable; GPM serves stable so direct-download-only no longer applies) and `project-plugin-architecture.md` (admin2/api/flex now GPM-managed; git-sync remote-only category). Refresh the `MEMORY.md` pointers if the hooks change.
|
||
|
||
- [ ] **Step 5: Set the plan Status to complete**
|
||
|
||
Change the `**Status:**` line at the top of this file to `✅ Complete (YYYY-MM-DD)` (today's date at execution).
|
||
|
||
- [ ] **Step 6: Commit**
|
||
|
||
```bash
|
||
git add CLAUDE.md docs/reference/architecture.md docs/working/plans/2026-07-04-grav-2.0.4-upgrade.md
|
||
git commit -m "docs: record Grav 2.0.4 upgrade; GPM-managed plugins; prod runbook"
|
||
```
|
||
|
||
(Memory files live outside the repo; they are written directly, not committed here.)
|
||
|
||
---
|
||
|
||
## Rollback
|
||
|
||
If any phase fails and cannot be fixed forward:
|
||
1. `git revert` the relevant commits on `grav-2.0.4-upgrade` (root repo) and the `user` repo `system.yaml` commit.
|
||
2. Local: `make build && make start && make install-plugins`.
|
||
3. Test server: config, content, and plugin reverts are delivered via `make content-push` + `make remote-fetch-content-test`. **The core is the exception — once `bin/gpm self-upgrade` has completed it cannot downgrade, so treat a completed core upgrade as forward-only and fix forward; there is no revert for it.**
|
||
|
||
> ⚠️ Do **not** run the fresh-install path (`scripts/server-install.sh`) against a live server as a rollback. It does `rm -rf user; git clone`, which destroys the server-only, gitignored `user/config/plugins/git-sync.yaml` (the encrypted git-sync token a clone never restores). The fresh-install path is for empty/new servers only.
|
||
|
||
Content, config, and accounts are in git, so no data restore is required — but note the core caveat above: "rollback = git" covers config/content/plugins, **not** a completed server core self-upgrade.
|