[object Object]

← back to Dw Material Envelope Canary

fix check-bulk.py: pre-flight collision guard + FAILED CNCP alert (TK-10408)

9263d074607627b357a127d643e8da725fa7ea9b · 2026-08-12 04:25:55 -0700 · Steve Abrams

Files touched

Diff

commit 9263d074607627b357a127d643e8da725fa7ea9b
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Aug 12 04:25:55 2026 -0700

    fix check-bulk.py: pre-flight collision guard + FAILED CNCP alert (TK-10408)
---
 check-bulk.py | 20 ++++++++++++++++++--
 1 file changed, 18 insertions(+), 2 deletions(-)

diff --git a/check-bulk.py b/check-bulk.py
index d68b38c..5ff9102 100644
--- a/check-bulk.py
+++ b/check-bulk.py
@@ -28,18 +28,34 @@ def is_env(v):
     return isinstance(v, str) and "single_line_text_field" in v
 START = 'mutation { bulkOperationRunQuery(query: """{ products { edges { node { id handle metafields { edges { node { namespace key value } } } } } } }""") { bulkOperation { id status } userErrors { message } } }'
 POLL = '{ currentBulkOperation { status url objectCount } }'
+def alert_cncp(title, note):
+    try:
+        card = {"title": title, "note": note, "source": "dw-material-envelope-canary-bulk"}
+        urllib.request.urlopen(urllib.request.Request(CNCP, data=json.dumps(card).encode(),
+            headers={"Content-Type": "application/json"}), timeout=5)
+        print("  posted CNCP alert")
+    except Exception as ex: print("  CNCP post failed:", ex)
 def main():
     os.makedirs(os.path.join(HERE, "data"), exist_ok=True)
+    # Pre-flight: bail if a bulk op is already running (avoid collision)
+    existing = gql(POLL)["data"]["currentBulkOperation"]
+    if existing and existing.get("status") == "RUNNING":
+        print("bulk op already running — skipping this scheduled run to avoid collision"); return 0
     r = gql(START)
     ue = r["data"]["bulkOperationRunQuery"]["userErrors"]
     if ue:
-        print("start err", ue); return 2
+        print("start err", ue)
+        alert_cncp("⚠️ Bulk metafield scan failed to start", f"userErrors: {ue}")
+        return 2
     while True:
         op = gql(POLL)["data"]["currentBulkOperation"]
         if op["status"] in ("COMPLETED", "FAILED", "CANCELED"): break
         time.sleep(20)
     if op["status"] != "COMPLETED" or not op.get("url"):
-        print("bulk not completed:", op); return 2
+        print("bulk not completed:", op)
+        alert_cncp("⚠️ Bulk metafield scan returned FAILED/CANCELED",
+                   f"status={op.get('status')} objectCount={op.get('objectCount')} — check for concurrent bulk op collision or Shopify error.")
+        return 2
     path = os.path.join(HERE, "data", "bulk.jsonl")
     urllib.request.urlretrieve(op["url"], path)
     hits = {}; nprod = 0; nmf = 0

← 3f185d6 auto-data-snapshot: 2026-08-11T06:02:59 (1 data files) — dat  ·  back to Dw Material Envelope Canary  ·  auto-data-snapshot: 2026-08-12T05:48:07 (1 data files) — dat c84572d →