SOP-WEB-03: Shopify Theme Deployment Discipline¶
Version: 1.1 Status: Production Ready Owner: Anton Last updated: 15 April 2026
Purpose¶
Eliminate the possibility that a git push, a CLI push, or a local edit can break the live Shopify site. Split the deploy target from the edit target using a dedicated Staging theme and human-gated promotion.
Why this SOP exists¶
On 2026-04-15, a routine git push to the protocol-raw-theme repo deployed an outdated git HEAD to the live Shopify theme, wiping several days of Theme Editor image uploads. The actual deploy mechanism was a GitHub Action (.github/workflows/deploy.yml) that ran shopify theme push --theme <live-id> --allow-live on every push to main. Not a Shopify-managed GitHub integration (there was none). v1.0 misidentified the mechanism and tried to defend with local git hooks, which could not have helped because the deploy ran in CI after the push succeeded. v1.1 repoints the CI workflow at the Staging theme, structurally removing the ability for any git push to reach Live.
The model¶
Two themes exist in Shopify. Neither edits happen on Live.
| Theme | Role | ID | How it updates |
|---|---|---|---|
| Live | published | 192444105079 |
Only by explicit Publish action in Shopify admin on the Staging theme. No CI pipeline, hook, or script targets this theme. |
| Staging | unpublished | 192642417015 |
Two paths: (a) git push origin main triggers the GitHub Action in .github/workflows/deploy.yml which pushes to Staging; (b) ./push-to-staging.sh pushes local uncommitted work-in-progress to Staging. Free to iterate without affecting Live. |
Preview Staging at: https://protocolraw.myshopify.com?preview_theme_id=192642417015
Deploy workflow¶
- Edit locally in the
theme/working tree. ./push-to-staging.sh(usesshopify theme push --theme=192642417015).- Preview in browser at the Staging URL. Check changed pages. Sanity check unrelated pages.
- Commit your changes to git and push to GitHub (audit trail, not a deploy).
- When Staging is verified good, open Shopify admin → Themes → Staging → Actions → Publish. That single human click is the only path to the live site.
After publish, Live takes over Staging's content (including image refs). Create a fresh Staging by duplicating the new Live, or continue pushing code changes to the existing Staging.
Banned operations¶
shopify theme pushwithout a--theme=<staging-id>flag. The wrapper script enforces this.- Pushing anything directly to Live. Use the admin Publish promotion.
- Editing content in Live's Theme Editor. Edit in Staging only, then promote.
- Committing
config/settings_data.jsonto git. This file is in.gitignoreand must stay there.
Files NOT tracked in git¶
config/settings_data.json holds customiser state (section block text, image picker values, schema settings). It stays in Shopify admin only — never committed, never synced. Any workflow that causes git to track this file is wrong and must be reverted.
Image uploads¶
Images live in Shopify admin via the Theme Editor, authoritative per theme.
- Upload to Staging while iterating.
- Promotion to Live via Publish carries the settings across.
For frequently-edited images, use the assets-as-files pattern to eliminate theme drift:
- Place image in
theme/assets/your-image.jpg - Reference in liquid:
{{ 'your-image.jpg' | asset_url }}or{{ 'your-image.jpg' | image_url: width: 1200 }} - Commit the file alongside the liquid change
Images referenced this way live in git, deploy with every ./push-to-staging.sh, and are identical across any theme.
Session-start checklist¶
git status— investigate any drift before editing.shopify theme listif working with multiple themes — confirm the Staging ID hasn't changed.- Edit. Push to Staging. Preview. Promote when ready.
Recovery procedure¶
The previous live-site-breaking failure mode is no longer reachable given the workflow above. If something does go wrong on Live:
- Do not attempt code rollback to fix a Live issue. Instead, publish the previous-known-good theme.
- Shopify admin → Themes. Old theme versions appear as unpublished themes. Select one known good → Publish. Live recovers in seconds.
- Separately, diagnose the Staging issue at leisure. Live is already safe.
If you have inherited dirty state or inconsistent repo/Shopify state:
- Stop. Do not push.
shopify theme pull --theme=192642417015into a fresh clone to see Staging's current state.- Compare against the working tree. Decide which side is correct, per file.
- Reconcile, push to Staging, preview.
Migration notes (from v1.0)¶
The pre-push git hook, sync-from-shopify.sh, and the "git push deploys to Live" assumption all came from v1.0's defensive model. Under v1.1:
- The real deploy mechanism is
.github/workflows/deploy.yml, a GitHub Action. It was repointed from Live (192444105079with--allow-live) to Staging (192642417015). This is the load-bearing change. - The pre-push hook no longer matters structurally. Retain as minor housekeeping.
sync-from-shopify.shis superseded. If you need admin state on disk,shopify theme pull --theme=192642417015does it explicitly.- There was no Shopify-managed GitHub integration to disconnect. The GitHub Action was the coupling, and it is now rerouted.
Related SOPs¶
- SOP-WEB-01: Social Acquisition Landing Page
- SOP-WEB-02: Responsive Nav Breakpoints
Version history¶
- v1.1 (15 April 2026): Staging-first model. Live is only updated via explicit admin Publish. Git push no longer deploys. Two-theme pattern (Staging for iteration, Live as promotion target) structurally removes the v1.0 incident class.
- v1.0 (15 April 2026): Initial release. Defensive model with pre-push hook, session-start checklist, image upload workflow, recovery procedure.