2 · Server sync — capture server changes
Objective — capture changes made on the server (admin-panel uploads, installer output, config) back into git before the next deploy silently wipes them — and do it without ever committing secrets.
Background
Section titled “Background”A deployed app has two sources of truth that can drift apart: git (your code) and the server (files written after deploy — a logo uploaded through the admin panel, files the web installer generated, an admin config change). Your deploy pipeline clears and replaces the release directory on every deploy — so anything written outside the shared directories that git doesn’t know about gets erased on the next deploy.
The deploy keeps a small set of shared directories (notably storage/ and the .env file) that are symlinked into each release and survive every deploy — that’s why an uploaded logo under storage/app/public and the storage/installed marker persist on the server without ever being committed. Server sync is for the durable files that land outside those shared dirs (or for assets you want versioned in git): it pulls them back into git first, so the next deploy preserves them instead of destroying them — while leaving per-env state (storage/installed, public/storage, .env) on the server where it belongs.
Read Zaj-PROJECT.md before syncing. Use the environment matrix for the target row’s SSH alias, deploy path, branch/source, and DB/runtime facts. If you discover a changed SSH alias, deploy path, hosting account, runtime version, DB engine/collation, or capture scope, update Zaj-PROJECT.md before committing the sync.
Before reporting “SSH credentials missing,” inspect the operator’s existing machine config and reuse a matching alias instead of creating duplicate SSH entries:
grep -nE '^Host |HostName|User|Port|IdentityFile' ~/.ssh/config 2>/dev/nullSSH_ALIAS="<target-alias-from-Zaj-PROJECT>"PHPBIN="<versioned-php-path-if-known-or-php>"ssh -o BatchMode=yes -o ConnectTimeout=8 "$SSH_ALIAS" "pwd && $PHPBIN -v | head -1"# Expected: exit 0; alias/host/user/port match the selected environment rowflowchart LR A["Server writes file<br/>upload · installer · config"] --> B["Capture into git<br/>commit on production"] B --> C["Review file list<br/>no secrets"] C --> D["Merge to develop<br/>safe to deploy"]The risk runs the other way too: server files can include .env, private keys, or logs with personal data. Capturing those into git would leak secrets — so every sync is review-first, never auto-merge.
For the command-only card, see the Server sync capture runbook.
1. Confirm the change is worth capturing
Section titled “1. Confirm the change is worth capturing”Not every server change needs syncing. Match what changed against the table so you only capture real, durable files — and never sweep secrets in by accident.
-
Match the change against the capture table.
Change type Example Capture? Admin-panel uploads Logo, favicon, images ✅ Yes Web installer output (real files) Generated config, seeded assets ✅ Yes Composer install on server Package installed on the server ⚠️ Re-scan vendor first — see caution below Admin-panel config SMTP, payment keys ⚠️ Maybe — .env.exampleupdates onlyInstaller marker ( storage/installed)Proof the installer ran 🔴 No — per-env runtime state, gitignored Per-env symlinks ( public/storage,public/packages)storage:linkoutput🔴 No — recreated per environment, gitignored .env,*.key,*.pem, logs with PIIServer secrets 🔴 No — secrets, never captured - ✅ You’ve confirmed the change is a real, durable file worth keeping in git — not a per-env marker, symlink, or secret.
2. Capture the changes into git
Section titled “2. Capture the changes into git”Two ways to capture: the GitHub Action does it for you (preferred), or you do it by hand over SSH. Either way, the server’s new files land as a commit on the production branch.
-
Preferred — trigger the ServerSync GitHub Action. From the repository’s Actions tab, run the capture workflow (e.g.
capture-production.yml) for the right environment. It SSHes in, captures the shared directories, and commits toproductionfor you.- ✅ The workflow run finishes and a capture commit appears on
production.
- ✅ The workflow run finishes and a capture commit appears on
-
Manual fallback — capture over SSH, screening for secrets first. Use this only if the Action isn’t available.
Terminal window ssh [SSH_PRODUCTION_ALIAS]cd [DEPLOY_PATH]git status# Review the list. Three classes must NEVER be staged — abort the add if any appear:# secrets: .env *.key *.pem credentials logs-with-PII# per-env: storage/installed public/storage public/packages# untrusted: vendor/ (re-scan + composer reinstall first — see caution above)# Add ONLY the durable asset files you intend to capture (never a blanket `git add .`):git add [specific upload/asset paths]git status # re-confirm: no secret, no marker, no symlink, no vendor/ stagedgit commit -m "🔄 ⬛ T5 ServerSync-Upload: Capture [description]"git push origin production# Expected: only the intended asset files are committed and pushed on production- ✅ The captured files are committed on
production; no secret, installer marker, per-env symlink, or unscannedvendor/file is staged.
- ✅ The captured files are committed on
3. Review what was captured
Section titled “3. Review what was captured”This is the human gate. A sync commit can include deletions caused by config drift, not just safe additions — so a person reads the file list before it merges anywhere.
-
Pull the capture locally and inspect the file list.
Terminal window git checkout production && git pull origin productiongit log --oneline -3git show --stat HEAD# Expected: the capture commit, and a file list of exactly what changed- ✅ Every captured file is an intentional
ADDED/MODIFIEDasset — no surpriseDELETEDfiles, no secrets.
- ✅ Every captured file is an intentional
If you see DELETED files you didn’t expect, that’s drift between the deploy config’s clear_paths and the sync workflow’s GIT_ONLY_PATHS — fix the symmetry before trusting another sync, rather than committing the deletions.
4. Merge back to develop
Section titled “4. Merge back to develop”Once reviewed, fold the capture into develop so your everyday branch carries the server’s truth and the next ship doesn’t reintroduce the drift.
-
Merge production into develop and push.
Terminal window git checkout develop && git merge production && git push origin develop# Expected: develop now contains the captured server files- ✅
developcarries the captured changes; you can resume normal development and ship when ready.
- ✅
Checklist
Section titled “Checklist”Do not mark this step done until every box below is checked.
- 👤 Worth capturing — the change is a durable server file (upload / installer output / config), not noise.
- 🤖 No secrets staged — no
.env,*.key,*.pem, logs, or credentials in the commit. - 🤖 No per-env state staged — no
storage/installedmarker,public/storage, orpublic/packagessymlinks captured (they stay on the server, gitignored). - 🤖 No unscanned
vendor/— if the server rancomposer install,vendor/was re-scanned +composer reinstalled before any of it entered git. - 🤖 Committed on production — captured via the GitHub Action or manual SSH, with a
T5 ServerSyncmessage. - 👤 File list reviewed — inspected
git show --stat; no unexpectedDELETEDfiles; not auto-merged. - 🤖 Merged to develop —
productionfolded back intodevelopand pushed.