← back to Designer Wallcoverings
Harden mirror sync: dotenv self-load (no more token-less silent fail) + fail-loud on GraphQL error / page-1 empty (was exiting 0 on auth failure)
977a5291131a8de982af327eb9f81e49f87a8744 · 2026-07-12 11:54:20 -0700 · Steve
Files touched
M shopify/scripts/sync-shopify-products.js
Diff
commit 977a5291131a8de982af327eb9f81e49f87a8744
Author: Steve <steve@designerwallcoverings.com>
Date: Sun Jul 12 11:54:20 2026 -0700
Harden mirror sync: dotenv self-load (no more token-less silent fail) + fail-loud on GraphQL error / page-1 empty (was exiting 0 on auth failure)
---
shopify/scripts/sync-shopify-products.js | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/shopify/scripts/sync-shopify-products.js b/shopify/scripts/sync-shopify-products.js
index a09a95c8..bacf6ae9 100644
--- a/shopify/scripts/sync-shopify-products.js
+++ b/shopify/scripts/sync-shopify-products.js
@@ -14,6 +14,10 @@
*/
const { Pool } = require('pg');
+// Self-load the repo .env so the launchd/cron job never runs token-less again.
+// 2026-07-12: running this without `source .env` failed with "Invalid API key" and
+// SILENTLY exited 0 ("Synced 0"), masking the failure. dotenv + fail-loud (below) fix it.
+require('dotenv').config({ path: require('path').join(__dirname, '..', '..', '.env') });
// Node 18+ (box is on v26) ships a global fetch. The old `require('node-fetch')`
// shim broke on the Node-26 upgrade — node-fetch v3 is ESM-only, so require()
// returned a namespace object, not a function → "fetch is not a function" crashed
@@ -127,14 +131,24 @@ async function syncProducts(quickMode = false) {
const result = await shopifyGraphQL(query, { cursor });
+ // FAIL-LOUD: a GraphQL error (auth, throttle, schema) must NOT print a "Synced 0"
+ // success and exit 0. Exit non-zero so launchd/the canary sees a real failure.
if (result.errors) {
- console.error('GraphQL errors:', result.errors);
- break;
+ console.error('❌ SYNC FAILED — Shopify GraphQL error on page', page, ':', JSON.stringify(result.errors));
+ await pool.end();
+ process.exit(1);
}
const products = result.data?.products?.edges || [];
const pageInfo = result.data?.products?.pageInfo;
+ // FAIL-LOUD: page 1 returning zero products = auth/empty-store failure, not a real sync.
+ if (page === 1 && products.length === 0) {
+ console.error('❌ SYNC FAILED — page 1 returned 0 products (auth failure or empty store). Refusing to report success.');
+ await pool.end();
+ process.exit(1);
+ }
+
for (const { node: product } of products) {
const variants = (product.variants?.edges || []).map(e => e.node);
← 39fa428b auto-save: 2026-07-12T11:48:02 (4 files) — pending-approval/
·
back to Designer Wallcoverings
·
Fix fail-loud false-positive: page-1-empty guard only fires a798cd48 →