Skip to content

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

  1. Edit locally in the theme/ working tree.
  2. ./push-to-staging.sh (uses shopify theme push --theme=192642417015).
  3. Preview in browser at the Staging URL. Check changed pages. Sanity check unrelated pages.
  4. Commit your changes to git and push to GitHub (audit trail, not a deploy).
  5. 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 push without 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.json to git. This file is in .gitignore and 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:

  1. Place image in theme/assets/your-image.jpg
  2. Reference in liquid: {{ 'your-image.jpg' | asset_url }} or {{ 'your-image.jpg' | image_url: width: 1200 }}
  3. 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

  1. git status — investigate any drift before editing.
  2. shopify theme list if working with multiple themes — confirm the Staging ID hasn't changed.
  3. 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:

  1. Do not attempt code rollback to fix a Live issue. Instead, publish the previous-known-good theme.
  2. Shopify admin → Themes. Old theme versions appear as unpublished themes. Select one known good → Publish. Live recovers in seconds.
  3. Separately, diagnose the Staging issue at leisure. Live is already safe.

If you have inherited dirty state or inconsistent repo/Shopify state:

  1. Stop. Do not push.
  2. shopify theme pull --theme=192642417015 into a fresh clone to see Staging's current state.
  3. Compare against the working tree. Decide which side is correct, per file.
  4. 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 (192444105079 with --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.sh is superseded. If you need admin state on disk, shopify theme pull --theme=192642417015 does it explicitly.
  • There was no Shopify-managed GitHub integration to disconnect. The GitHub Action was the coupling, and it is now rerouted.
  • 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.