[object Object]

← back to Greenland Onboard

greenland cork go-live step 6: reactivate+publish+image-attach 15 archived Cork-500210-500350 (all unique non-dups), add audit/reactivate scripts + run log

4b24288db167cae9c3768c9fd4ca8c1a6d352bd4 · 2026-08-10 16:22:11 -0700 · Steve Abrams

Files touched

Diff

commit 4b24288db167cae9c3768c9fd4ca8c1a6d352bd4
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Mon Aug 10 16:22:11 2026 -0700

    greenland cork go-live step 6: reactivate+publish+image-attach 15 archived Cork-500210-500350 (all unique non-dups), add audit/reactivate scripts + run log
---
 backups/runs/reactivate-500210-500350.jsonl | 16 ++++++++++
 scripts/reactivate-archived-cork.mjs        | 47 +++++++++++++++++++++++++++++
 scripts/shopify-cork-audit.mjs              | 46 ++++++++++++++++++++++++++++
 3 files changed, 109 insertions(+)

diff --git a/backups/runs/reactivate-500210-500350.jsonl b/backups/runs/reactivate-500210-500350.jsonl
new file mode 100644
index 0000000..4b72670
--- /dev/null
+++ b/backups/runs/reactivate-500210-500350.jsonl
@@ -0,0 +1,16 @@
+{"ts":"2026-08-10T23:17:47.546Z","sku":"Cork-500210","id":"gid://shopify/Product/7880972369971","before":{"status":"ARCHIVED","online":false},"after":{"status":"ACTIVE","online":true},"title":"Southampton-Sharkskin Cork Wallcovering | Phillipe Romano","vendor":"Phillipe Romano","leak":false}
+{"ts":"2026-08-10T23:19:45.255Z","step":"image+activate","sku":"Cork-500210","id":"gid://shopify/Product/7880972369971","mfr":"G0154NQ8290","before":{"status":"ACTIVE","online":true,"hadImage":false},"after":{"status":"ACTIVE","online":true,"hasImage":true,"altText":"Southampton-Sharkskin Cork Wallcovering | Phillipe Romano — Cork-500210"},"imgResult":"attached","vendor":"Phillipe Romano","leak":false}
+{"ts":"2026-08-10T23:20:27.084Z","step":"image+activate","sku":"Cork-500220","id":"gid://shopify/Product/7880972402739","mfr":"G0154NQ8291","before":{"status":"ARCHIVED","online":false,"hadImage":false},"after":{"status":"ACTIVE","online":true,"hasImage":true,"altText":"Southampton-Eden Cork Wallcovering | Phillipe Romano — Cork-500220"},"imgResult":"attached","vendor":"Phillipe Romano","leak":false}
+{"ts":"2026-08-10T23:20:32.230Z","step":"image+activate","sku":"Cork-500230","id":"gid://shopify/Product/7880972533811","mfr":"G0154NQ8292","before":{"status":"ARCHIVED","online":false,"hadImage":false},"after":{"status":"ACTIVE","online":true,"hasImage":true,"altText":"Southampton-Deep Taupe Cork Wallcovering | Phillipe Romano — Cork-500230"},"imgResult":"attached","vendor":"Phillipe Romano","leak":false}
+{"ts":"2026-08-10T23:20:37.319Z","step":"image+activate","sku":"Cork-500240","id":"gid://shopify/Product/7880972468275","mfr":"G0154NQ8293","before":{"status":"ARCHIVED","online":false,"hadImage":false},"after":{"status":"ACTIVE","online":true,"hasImage":true,"altText":"Southampton-Coffee Bean Cork Wallcovering | Phillipe Romano — Cork-500240"},"imgResult":"attached","vendor":"Phillipe Romano","leak":false}
+{"ts":"2026-08-10T23:20:42.460Z","step":"image+activate","sku":"Cork-500250","id":"gid://shopify/Product/7880972435507","mfr":"G0154NQ8294","before":{"status":"ARCHIVED","online":false,"hadImage":false},"after":{"status":"ACTIVE","online":true,"hasImage":true,"altText":"Southampton-Argan Oil Cork Wallcovering | Phillipe Romano — Cork-500250"},"imgResult":"attached","vendor":"Phillipe Romano","leak":false}
+{"ts":"2026-08-10T23:20:47.469Z","step":"image+activate","sku":"Cork-500260","id":"gid://shopify/Product/7880972501043","mfr":"G0154NQ8295","before":{"status":"ARCHIVED","online":false,"hadImage":false},"after":{"status":"ACTIVE","online":true,"hasImage":true,"altText":"Southampton-Winter Bloom Cork Wallcovering | Phillipe Romano — Cork-500260"},"imgResult":"attached","vendor":"Phillipe Romano","leak":false}
+{"ts":"2026-08-10T23:20:52.070Z","step":"image+activate","sku":"Cork-500270","id":"gid://shopify/Product/7880972140595","mfr":"G0155NQ8296","before":{"status":"ARCHIVED","online":false,"hadImage":false},"after":{"status":"ACTIVE","online":true,"hasImage":true,"altText":"Palm Beach-White Cork Wallcovering | Phillipe Romano — Cork-500270"},"imgResult":"attached","vendor":"Phillipe Romano","leak":false}
+{"ts":"2026-08-10T23:20:57.485Z","step":"image+activate","sku":"Cork-500280","id":"gid://shopify/Product/7880972075059","mfr":"G0155NQ8297","before":{"status":"ARCHIVED","online":false,"hadImage":false},"after":{"status":"ACTIVE","online":true,"hasImage":true,"altText":"Palm Beach-Eiffel Tower Cork Wallcovering | Phillipe Romano — Cork-500280"},"imgResult":"attached","vendor":"Phillipe Romano","leak":false}
+{"ts":"2026-08-10T23:21:02.401Z","step":"image+activate","sku":"Cork-500290","id":"gid://shopify/Product/7880972173363","mfr":"G0155NQ8298","before":{"status":"ARCHIVED","online":false,"hadImage":false},"after":{"status":"ACTIVE","online":true,"hasImage":true,"altText":"Palm Beach-Amphora Cork Wallcovering | Phillipe Romano — Cork-500290"},"imgResult":"attached","vendor":"Phillipe Romano","leak":false}
+{"ts":"2026-08-10T23:21:07.060Z","step":"image+activate","sku":"Cork-500300","id":"gid://shopify/Product/7880972206131","mfr":"G0155NQ8299","before":{"status":"ARCHIVED","online":false,"hadImage":false},"after":{"status":"ACTIVE","online":true,"hasImage":true,"altText":"Palm Beach-Rhododendron Cork Wallcovering | Phillipe Romano — Cork-500300"},"imgResult":"attached","vendor":"Phillipe Romano","leak":false}
+{"ts":"2026-08-10T23:21:11.764Z","step":"image+activate","sku":"Cork-500310","id":"gid://shopify/Product/7880972238899","mfr":"G0155NQ8300","before":{"status":"ARCHIVED","online":false,"hadImage":false},"after":{"status":"ACTIVE","online":true,"hasImage":true,"altText":"Palm Beach-Picante Cork Wallcovering | Phillipe Romano — Cork-500310"},"imgResult":"attached","vendor":"Phillipe Romano","leak":false}
+{"ts":"2026-08-10T23:21:16.688Z","step":"image+activate","sku":"Cork-500320","id":"gid://shopify/Product/7880972271667","mfr":"G0155NQ8301","before":{"status":"ARCHIVED","online":false,"hadImage":false},"after":{"status":"ACTIVE","online":true,"hasImage":true,"altText":"Palm Beach-Taupe Gray Cork Wallcovering | Phillipe Romano — Cork-500320"},"imgResult":"attached","vendor":"Phillipe Romano","leak":false}
+{"ts":"2026-08-10T23:21:21.613Z","step":"image+activate","sku":"Cork-500330","id":"gid://shopify/Product/7880972337203","mfr":"G0155NQ8302","before":{"status":"ARCHIVED","online":false,"hadImage":false},"after":{"status":"ACTIVE","online":true,"hasImage":true,"altText":"Palm Beach-Java Cork Wallcovering | Phillipe Romano — Cork-500330"},"imgResult":"attached","vendor":"Phillipe Romano","leak":false}
+{"ts":"2026-08-10T23:21:27.356Z","step":"image+activate","sku":"Cork-500340","id":"gid://shopify/Product/7880972304435","mfr":"G0155NQ8303","before":{"status":"ARCHIVED","online":false,"hadImage":false},"after":{"status":"ACTIVE","online":true,"hasImage":true,"altText":"Palm Beach-Blue  Nights Cork Wallcovering | Phillipe Romano — Cork-500340"},"imgResult":"attached","vendor":"Phillipe Romano","leak":false}
+{"ts":"2026-08-10T23:21:32.479Z","step":"image+activate","sku":"Cork-500350","id":"gid://shopify/Product/7880972107827","mfr":"G0155NQ8304","before":{"status":"ARCHIVED","online":false,"hadImage":false},"after":{"status":"ACTIVE","online":true,"hasImage":true,"altText":"Palm Beach-Turkish  Tile Cork Wallcovering | Phillipe Romano — Cork-500350"},"imgResult":"attached","vendor":"Phillipe Romano","leak":false}
diff --git a/scripts/reactivate-archived-cork.mjs b/scripts/reactivate-archived-cork.mjs
new file mode 100644
index 0000000..9079aae
--- /dev/null
+++ b/scripts/reactivate-archived-cork.mjs
@@ -0,0 +1,47 @@
+import { execSync } from 'node:child_process';
+import { readFileSync, appendFileSync, existsSync } from 'node:fs';
+const TOKEN = execSync(`grep -E '^SHOPIFY_ADMIN_TOKEN=' ~/Projects/secrets-manager/.env | head -1 | cut -d= -f2-`, {encoding:'utf8'}).trim().replace(/^["']|["']$/g,'');
+const SHOP='designer-laboratory-sandbox.myshopify.com';
+const GQL=`https://${SHOP}/admin/api/2024-10/graphql.json`;
+const PUB='gid://shopify/Publication/22208643184';
+const IMGDIR='/Users/macstudio3/Projects/greenland-onboard/public/img';
+const RUNFILE='/Users/macstudio3/Projects/greenland-onboard/backups/runs/reactivate-500210-500350.jsonl';
+async function gql(q,v={}){for(let i=0;i<6;i++){const r=await fetch(GQL,{method:'POST',headers:{'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json'},body:JSON.stringify({query:q,variables:v})});const j=await r.json();if(j.errors){if(JSON.stringify(j.errors).includes('Throttled')){await new Promise(s=>setTimeout(s,2500));continue;}throw new Error(JSON.stringify(j.errors));}return j.data;}}
+async function rest(path,opts={}){for(let i=0;i<6;i++){const r=await fetch(`https://${SHOP}/admin/api/2024-10${path}`,{method:opts.method||'GET',headers:{'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json'},body:opts.body?JSON.stringify(opts.body):undefined});if(r.status===429){await new Promise(s=>setTimeout(s,2500));continue;}return {status:r.status, json: await r.json().catch(()=>({}))};}return {status:0,json:{}};}
+
+const MODE=process.argv[2]||'canary';
+const state=JSON.parse(readFileSync('/tmp/gl_shopify_state.json','utf8'));
+const archived=JSON.parse(readFileSync('/tmp/gl_archived.json','utf8')).sort((a,b)=>a.sku>b.sku?1:-1);
+const idBySku={}; for(const r of state.rows) if(r.sku) idBySku[r.sku]=r.id;
+const canary='Cork-500210';
+let targets = MODE==='canary'?archived.filter(a=>a.sku===canary): MODE==='rest'?archived.filter(a=>a.sku!==canary):archived;
+
+for(const a of targets){
+  const id=idBySku[a.sku]; const pid=id.split('/').pop();
+  const before=await gql(`query{ product(id:"${id}"){ status vendor featuredImage{url} publishedOnPublication(publicationId:"${PUB}") } }`);
+  const b=before.product;
+  const title=a.title; const alt=`${title} — ${a.sku}`;
+  // 1) attach local image (if none)
+  let imgResult='had-image';
+  if(!b.featuredImage){
+    const f=`${IMGDIR}/${a.sku}.jpg`;
+    if(!existsSync(f)){ console.log(`SKIP ${a.sku}: local image missing`); continue; }
+    const b64=readFileSync(f).toString('base64');
+    const r=await rest(`/products/${pid}/images.json`,{method:'POST',body:{image:{attachment:b64, filename:`${a.sku}.jpg`, alt}}});
+    imgResult=(r.status===200||r.status===201)?'attached':`img-err-${r.status}`;
+  }
+  // 2) status ACTIVE
+  const upd=await gql(`mutation($id:ID!){ productUpdate(input:{id:$id,status:ACTIVE}){ product{status} userErrors{message} } }`,{id});
+  // 3) publish online
+  const pub=await gql(`mutation($id:ID!,$pub:ID!){ publishablePublish(id:$id,input:{publicationId:$pub}){ userErrors{message} } }`,{id,pub:PUB});
+  const after=await gql(`query{ product(id:"${id}"){ status vendor title featuredImage{url altText} publishedOnPublication(publicationId:"${PUB}") } }`);
+  const af=after.product;
+  const rec={ts:new Date().toISOString(),step:'image+activate',sku:a.sku,id,mfr:a.mfr,
+    before:{status:b.status,online:b.publishedOnPublication,hadImage:!!b.featuredImage},
+    after:{status:af.status,online:af.publishedOnPublication,hasImage:!!af.featuredImage,altText:af.featuredImage?.altText},
+    imgResult, vendor:af.vendor, leak:/greenland/i.test(af.title)||/greenland/i.test(af.vendor||'')};
+  appendFileSync(RUNFILE,JSON.stringify(rec)+'\n');
+  console.log(`${a.sku}: img=${imgResult} ${b.status}/${b.publishedOnPublication?'on':'off'}->${af.status}/${af.publishedOnPublication?'on':'off'} hasImg=${!!af.featuredImage} vendor=${af.vendor} leak=${rec.leak}`);
+  await new Promise(s=>setTimeout(s,400));
+}
+console.log(`\nDONE (${MODE}) -> ${RUNFILE}`);
diff --git a/scripts/shopify-cork-audit.mjs b/scripts/shopify-cork-audit.mjs
new file mode 100644
index 0000000..577d874
--- /dev/null
+++ b/scripts/shopify-cork-audit.mjs
@@ -0,0 +1,46 @@
+import { execSync } from 'node:child_process';
+import { writeFileSync } from 'node:fs';
+const TOKEN = execSync(`grep -E '^SHOPIFY_ADMIN_TOKEN=' ~/Projects/secrets-manager/.env | head -1 | cut -d= -f2-`, {encoding:'utf8'}).trim().replace(/^["']|["']$/g,'');
+const STORE = 'designer-laboratory-sandbox.myshopify.com';
+const API = `https://${STORE}/admin/api/2024-10/graphql.json`;
+async function gql(query, variables={}) {
+  for (let i=0;i<6;i++){
+    const r = await fetch(API, { method:'POST', headers:{'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json'}, body: JSON.stringify({query,variables}) });
+    const j = await r.json();
+    if (j.errors) { if(JSON.stringify(j.errors).includes('Throttled')){await new Promise(s=>setTimeout(s,2500));continue;} throw new Error(JSON.stringify(j.errors)); }
+    return j.data;
+  }
+}
+const pubs = await gql(`{ publications(first:25){ nodes{ id name } } }`);
+const onlineStore = pubs.publications.nodes.find(p=>/online store/i.test(p.name));
+console.log('ONLINE_STORE_PUB', onlineStore?.id, onlineStore?.name);
+
+let after=null, all=[];
+do {
+  const d = await gql(`query($after:String){ products(first:100, query:"sku:Cork-*", after:$after){ pageInfo{hasNextPage endCursor} nodes{
+      id title status vendor handle
+      publishedOnPublication(publicationId:"${onlineStore.id}")
+      variants(first:10){ nodes{ sku price } }
+  } } }`, {after});
+  const conn = d.products;
+  all.push(...conn.nodes);
+  after = conn.pageInfo.hasNextPage ? conn.pageInfo.endCursor : null;
+} while(after);
+
+const cork = all.filter(p => p.variants.nodes.some(v => /^Cork-\d/.test(v.sku||'')));
+const rows = cork.map(p => {
+  const corkSku = (p.variants.nodes.find(v=>/^Cork-\d/.test(v.sku||''))||{}).sku;
+  const yardV = p.variants.nodes.find(v=>/^Cork-\d+$/.test(v.sku||''));
+  return { id:p.id, title:p.title, status:p.status, vendor:p.vendor, handle:p.handle,
+    onlinePublished: p.publishedOnPublication, sku:corkSku,
+    yardPrice: yardV?.price, skus:p.variants.nodes.map(v=>v.sku) };
+});
+writeFileSync('/tmp/gl_shopify_state.json', JSON.stringify({onlineStorePub:onlineStore.id, rows}, null, 2));
+
+const byStatus = {};
+for (const r of rows){ const k=r.status+(r.onlinePublished?'/online':'/offline'); byStatus[k]=(byStatus[k]||0)+1; }
+console.log('TOTAL cork products:', rows.length);
+console.log('breakdown status/online:', JSON.stringify(byStatus,null,2));
+console.log('vendors:', JSON.stringify([...new Set(rows.map(r=>r.vendor))]));
+const leaks = rows.filter(r => /greenland/i.test(r.title) || /greenland/i.test(r.vendor||''));
+console.log('GREENLAND LEAKS:', leaks.length, JSON.stringify(leaks.map(l=>l.sku)));

← 9ae7f09 greenland cork go-live: 100% color coverage (lift from patte  ·  back to Greenland Onboard  ·  auto-data-snapshot: 2026-08-10T16:40:28 (2 data files) — bac 475d7a8 →