From db0f5bbd7ad2d487b67ae22cf368c27ba80666f8 Mon Sep 17 00:00:00 2001 From: Christopher Monsanto Date: Sat, 15 Aug 2026 23:32:05 -0400 Subject: [PATCH] Hash-stamp forumsprites and xyicons, write url manifests Deployed names become -.png (sha256/base32/8, same format as the existing minisprites dir hash) with a manifest.json per set mapping the unhashed name to the hashed one, riding the existing rsyncs. Other deploy sections are unchanged; alias collisions now error via the manifest-key check since hashed destinations no longer collide. Co-Authored-By: Claude Fable 5 --- smogon.deploy.js | 61 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 12 deletions(-) diff --git a/smogon.deploy.js b/smogon.deploy.js index 78e90636..9f48083a 100644 --- a/smogon.deploy.js +++ b/smogon.deploy.js @@ -9,7 +9,29 @@ function toPSID(name) { return name.toLowerCase().replace(/[^a-z0-9]+/g, ''); } -function spritecopy(f, {dir, ext}, allowUnknown=false) { +// Copy with a content-hash-stamped name and record the unhashed -> hashed +// mapping in `manifest`. +function stampcopy(f, {dir, ext, name}, manifest) { + const key = `${name}.${ext ?? f.ext}`; + // ActionQueue only dedups final dsts; hashed dsts differ even when + // unhashed names collide, so check the manifest key explicitly. + if (manifest[key] !== undefined) { + throw new Error(`duplicate sprite name ${key}`); + } + const h = hash(f); + manifest[key] = `${name}-${h}.${ext ?? f.ext}`; + copy(f, {dir, ext, name: `${name}-${h}`}); +} + +function writeManifest(dst, manifest) { + const sorted = {}; + for (const k of Object.keys(manifest).sort()) { + sorted[k] = manifest[k]; + } + write(dst, JSON.stringify(sorted, null, 4) + "\n"); +} + +function spritecopy(f, {dir, ext}, allowUnknown=false, manifest=null) { const sn = spritedata.parseFilename(f.name); let name; @@ -39,16 +61,24 @@ function spritecopy(f, {dir, ext}, allowUnknown=false) { name += "-gmax"; } - copy(f, {dir, ext, name}); + if (manifest) { + stampcopy(f, {dir, ext, name}, manifest); + } else { + copy(f, {dir, ext, name}); + } } // TODO: merge with above -function itemspritecopy(f, {dir, ext}) { +function itemspritecopy(f, {dir, ext}, manifest=null) { const sn = spritedata.parseFilename(f.name); const sd = spritedata.get(sn.id); for (const n of sd.names) { const name = toSmogonAlias(n); - copy(f, {dir, ext, name}); + if (manifest) { + stampcopy(f, {dir, ext, name}, manifest); + } else { + copy(f, {dir, ext, name}); + } } } @@ -102,8 +132,12 @@ for (const f of list("build/gen5-gif")) { spritecopy(f, {dir: "xy"}); } -for (const f of list("build/gen6-minisprites-trimmed")) { - spritecopy(f, {dir: "xyicons"}); +{ + const manifest = {}; + for (const f of list("build/gen6-minisprites-trimmed")) { + spritecopy(f, {dir: "xyicons"}, false, manifest); + } + writeManifest("xyicons/manifest.json", manifest); } for (const f of list("build/item-minisprites-trimmed")) { @@ -116,12 +150,15 @@ for (const f of list("build/smogon/minisprites")) { } write("minisprites/hash.txt", h); -for (const f of list("build/item-minisprites-padded")) { - itemspritecopy(f, {dir: "forumsprites"}); -} - -for (const f of list("build/gen6-minisprites-padded")) { - spritecopy(f, {dir: "forumsprites"}, true); +{ + const manifest = {}; + for (const f of list("build/item-minisprites-padded")) { + itemspritecopy(f, {dir: "forumsprites"}, manifest); + } + for (const f of list("build/gen6-minisprites-padded")) { + spritecopy(f, {dir: "forumsprites"}, true, manifest); + } + writeManifest("forumsprites/manifest.json", manifest); } for (const f of list("build/smogon/fbsprites/xy")) {