@@ -273,6 +273,72 @@ jobs:
273273 const orderedRefs = [...new Set([...priorityRefs, ...inlineRefs])].filter(
274274 (id) => !devUploadedIds.has(id) && imageAssetMap.has(id)
275275 );
276+ if (orderedRefs.length === 0) {
277+ console.log("All active image binaries are already present in 'dev' dataset.");
278+ return devUploadedIds;
279+ }
280+
281+ // Check if any of orderedRefs exist in 'dev' as metadata-only docs pointing to 'production' bucket
282+ // (Sanity's /assets/images/dev endpoint skips writing the binary if the document _id already exists in 'dev')
283+ const prodPathQuery = encodeURIComponent(`*[_type == "sanity.imageAsset" && path match "images/${projectId}/production/*"]._id`);
284+ let metadataOnlyIds = new Set();
285+ for (const token of tokens) {
286+ const res = await fetch(`https://${projectId}.api.sanity.io/v2025-02-19/data/query/dev?query=${prodPathQuery}`, {
287+ headers: { Authorization: `Bearer ${token}` },
288+ });
289+ if (res.ok) {
290+ const json = await res.json();
291+ metadataOnlyIds = new Set(json.result || []);
292+ break;
293+ }
294+ }
295+
296+ const toDeleteBeforeUpload = orderedRefs.filter((id) => metadataOnlyIds.has(id));
297+ if (toDeleteBeforeUpload.length > 0) {
298+ const toDeleteSet = new Set(toDeleteBeforeUpload);
299+ console.log(`Replacing ${toDeleteBeforeUpload.length} metadata-only asset docs with real 'dev' binaries...`);
300+
301+ function weakenTargetRefs(obj) {
302+ if (!obj || typeof obj !== "object") return { val: obj, hit: false };
303+ if (Array.isArray(obj)) {
304+ let anyHit = false;
305+ const arr = obj.map((item) => {
306+ const r = weakenTargetRefs(item);
307+ if (r.hit) anyHit = true;
308+ return r.val;
309+ });
310+ return { val: arr, hit: anyHit };
311+ }
312+ let anyHit = false;
313+ const copy = {};
314+ for (const [k, v] of Object.entries(obj)) {
315+ const r = weakenTargetRefs(v);
316+ if (r.hit) anyHit = true;
317+ copy[k] = r.val;
318+ }
319+ if (typeof copy._ref === "string" && toDeleteSet.has(copy._ref)) {
320+ copy._weak = true;
321+ anyHit = true;
322+ }
323+ return { val: copy, hit: anyHit };
324+ }
325+
326+ const weakenedDocs = [];
327+ for (const doc of sanitized) {
328+ const { val, hit } = weakenTargetRefs(doc);
329+ if (hit) weakenedDocs.push(val);
330+ }
331+ if (weakenedDocs.length > 0) {
332+ await sendChunked(weakenedDocs, "Pre-upload: Temporarily Weaken Asset Refs", "createOrReplace");
333+ }
334+
335+ for (let i = 0; i < toDeleteBeforeUpload.length; i += 200) {
336+ const delBatch = toDeleteBeforeUpload.slice(i, i + 200).map((id) => ({ delete: { id } }));
337+ await mutateBatch(delBatch, "dev");
338+ }
339+ console.log(`Deleted ${toDeleteBeforeUpload.length} metadata-only asset docs; ready for binary upload.`);
340+ }
341+
276342 console.log(`Uploading ${orderedRefs.length} missing active image binaries to 'dev' dataset...`);
277343
278344 let uploaded = 0;
0 commit comments