← back to Designer Wallcoverings
Add push script for grid slider + description deploy
ac76db9f5a1ef1d7ef791ea2d47f1934469e4cc0 · 2026-06-22 14:03:37 -0700 · Steve Abrams
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Files touched
A shopify/push-grid-slider-and-descriptions.sh
Diff
commit ac76db9f5a1ef1d7ef791ea2d47f1934469e4cc0
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Mon Jun 22 14:03:37 2026 -0700
Add push script for grid slider + description deploy
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---
shopify/push-grid-slider-and-descriptions.sh | 119 +++++++++++++++++++++++++++
1 file changed, 119 insertions(+)
diff --git a/shopify/push-grid-slider-and-descriptions.sh b/shopify/push-grid-slider-and-descriptions.sh
new file mode 100755
index 00000000..cda7614c
--- /dev/null
+++ b/shopify/push-grid-slider-and-descriptions.sh
@@ -0,0 +1,119 @@
+#!/usr/bin/env bash
+# Push grid density slider + product description fixes to dev theme.
+#
+# Deploys four files from _cwGRID to "Updated copy of NewWall Template [Dev]":
+# layout/theme.liquid — adds dw-grid-control.js script tag
+# assets/dw-grid-control.js — dense-mode class toggle
+# snippets/product-list-item.liquid — description field
+# assets/custom.css — description CSS
+#
+# Drift-safe: PULLs current dev theme file, surgical merge, backs up, PUSHes.
+# THEME_TOKEN=shpat_xxx bash push-grid-slider-and-descriptions.sh
+set -euo pipefail
+cd "$(dirname "$0")"
+
+STORE="designer-laboratory-sandbox.myshopify.com"
+API="2024-10"
+SECRETS="$HOME/Projects/secrets-manager/.env"
+TARGET_NAME="Updated copy of NewWall Template [Dev]"
+
+TOK="${THEME_TOKEN:-$(grep -hE '^SHOPIFY_THEME_TOKEN=' "$SECRETS" 2>/dev/null | head -1 | cut -d= -f2- | tr -d '"'"'"' ')}"
+[ -z "$TOK" ] && { echo "No theme token. Run: THEME_TOKEN=shpat_xxx bash push-grid-slider-and-descriptions.sh"; exit 1; }
+echo "token …${TOK: -4}"
+
+mkdir -p theme-backups
+
+python3 - "$STORE" "$API" "$TOK" "$TARGET_NAME" "theme-backups" \
+ "_cwGRID/layout/theme.liquid" \
+ "_cwGRID/assets/dw-grid-control.js" \
+ "_cwGRID/snippets/product-list-item.liquid" \
+ "_cwGRID/assets/custom.css" <<'PY'
+import sys, json, re, urllib.request, urllib.parse, datetime, os
+
+store, api, tok, target_name, bkdir = sys.argv[1:6]
+src_files = sys.argv[6:] # local _cwGRID paths (relative to script dir)
+script_dir = os.path.dirname(os.path.abspath(__file__))
+
+H = {"X-Shopify-Access-Token": tok, "Content-Type": "application/json"}
+
+def gget(url):
+ return json.load(urllib.request.urlopen(urllib.request.Request(url, headers=H)))
+
+def asset_url(tid, key):
+ return f"https://{store}/admin/api/{api}/themes/{tid}/assets.json?" + \
+ urllib.parse.urlencode({"asset[key]": key})
+
+# Resolve dev theme
+themes = gget(f"https://{store}/admin/api/{api}/themes.json")["themes"]
+print("themes:")
+for t in themes:
+ print(" ", t["id"], t["role"], "|", t["name"])
+
+match = [t for t in themes if t["name"] == target_name]
+if not match:
+ match = [t for t in themes if t["role"] == "development" and t["role"] != "main"]
+if not match:
+ print(f'Target theme "{target_name}" not found.'); sys.exit(1)
+
+theme = match[0]; tid = theme["id"]
+print(f'=== target: {tid} "{theme["name"]}" (role={theme["role"]}) ===\n')
+
+stamp = datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
+
+# Map local _cwGRID paths to Shopify theme asset keys
+def to_theme_key(local_path):
+ # e.g. "_cwGRID/layout/theme.liquid" -> "layout/theme.liquid"
+ parts = local_path.split("/", 1)
+ return parts[1] if len(parts) > 1 else local_path
+
+for local_rel in src_files:
+ theme_key = to_theme_key(local_rel)
+ local_abs = os.path.join(script_dir, local_rel)
+
+ print(f"--- {theme_key} ---")
+
+ # Read local source
+ with open(local_abs, "r", encoding="utf-8") as f:
+ new_content = f.read()
+
+ # Pull current dev theme version for backup
+ try:
+ cur = gget(asset_url(tid, theme_key))["asset"]["value"]
+ bk = os.path.join(bkdir, f"{theme_key.replace('/', '__')}.dev-{tid}.{stamp}.bak")
+ os.makedirs(os.path.dirname(bk) if os.path.dirname(bk) else ".", exist_ok=True)
+ with open(bk, "w", encoding="utf-8") as f:
+ f.write(cur)
+ print(f" backup: {bk} ({len(cur)} bytes)")
+ except Exception as e:
+ print(f" backup SKIP (asset may not exist yet): {e}")
+
+ # Push
+ body = json.dumps({"asset": {"key": theme_key, "value": new_content}}).encode()
+ try:
+ r = urllib.request.urlopen(urllib.request.Request(
+ f"https://{store}/admin/api/{api}/themes/{tid}/assets.json",
+ data=body, method="PUT", headers=H))
+ print(f" PUT HTTP {r.status} OK ({len(new_content)} bytes)")
+ except urllib.error.HTTPError as e:
+ print(f" PUT FAILED HTTP {e.code}: {e.read().decode()[:300]}")
+ sys.exit(1)
+
+ # Spot-verify
+ check = gget(asset_url(tid, theme_key))["asset"]["value"]
+ if theme_key == "layout/theme.liquid":
+ ok = "dw-grid-control.js" in check
+ print(f" verify: dw-grid-control.js present = {ok}")
+ elif theme_key == "assets/dw-grid-control.js":
+ ok = "dw-gc-dense" in check
+ print(f" verify: dw-gc-dense class = {ok}")
+ elif "product-list-item" in theme_key:
+ ok = "product-list-item-description" in check
+ print(f" verify: description field = {ok}")
+ elif theme_key == "assets/custom.css":
+ ok = "product-list-item-description" in check
+ print(f" verify: description CSS = {ok}")
+ print()
+
+print(f"\nPREVIEW: https://www.designerwallcoverings.com/collections/animal-skin?preview_theme_id={tid}")
+print(f"REVERT: restore from theme-backups/ files above")
+PY
← 09145719 MOQ theme fix: exempt Sample variants (force qty 1 when Samp
·
back to Designer Wallcoverings
·
Backup: live PDP section pre-MOQ-push (rollback source) ce69f4cc →