diff --git a/deploy.sh b/deploy.sh index 4b3235f..5990329 100644 --- a/deploy.sh +++ b/deploy.sh @@ -12,6 +12,9 @@ VPS=humanjing@100.71.119.27 CONTAINER=forum-nginx WEBROOT=/usr/share/nginx/html +echo "— optimize assets (idempotent; skips anything already meshopt-compressed) —" +node fktry/tools/optimize-assets.mjs + echo "— build —" (cd fktry && npx vite build --base=./) cp index.html fktry/dist/match.html @@ -27,7 +30,19 @@ ssh "$VPS" "docker exec $CONTAINER rm -rf $WEBROOT/glytch \ echo "— verify (cache-busted) —" curl -fsS -o /dev/null -w '%{http_code} https://partly.party/glytch/\n' "https://partly.party/glytch/?v=$(date +%s)" curl -fsS -o /dev/null -w '%{http_code} https://partly.party/glytch/match.html\n' "https://partly.party/glytch/match.html?v=$(date +%s)" -# data-endpoint check, not just index (classic nginx-routing failure mode): +# Data-endpoint checks, not just the index (classic nginx-routing failure mode). +# Round-7 fix: this used to test ONE js file, so a deploy that shipped zero models +# still reported success. Now the JS bundle, a real GLB, and the model COUNT. ASSET=$(ls fktry/dist/assets | grep -m1 '\.js$') -curl -fsS -o /dev/null -w "%{http_code} https://partly.party/glytch/assets/$ASSET\n" "https://partly.party/glytch/assets/$ASSET?v=$(date +%s)" +curl -fsS -o /dev/null -w "%{http_code} assets/$ASSET\n" "https://partly.party/glytch/assets/$ASSET?v=$(date +%s)" + +MODELS_LOCAL=$(ls fktry/dist/assets/models/*.glb 2>/dev/null | wc -l | tr -d ' ') +MODEL=$(basename "$(ls fktry/dist/assets/models/*.glb 2>/dev/null | head -1)") +if [ -z "$MODEL" ] || [ "$MODELS_LOCAL" -eq 0 ]; then + echo "FAIL: build produced no GLB models" >&2; exit 1 +fi +curl -fsS -o /dev/null -w "%{http_code} assets/models/$MODEL\n" "https://partly.party/glytch/assets/models/$MODEL?v=$(date +%s)" +MODELS_LIVE=$(ssh "$VPS" "docker exec $CONTAINER sh -c 'ls $WEBROOT/glytch/assets/models/*.glb 2>/dev/null | wc -l'" | tr -d ' ') +echo "models: $MODELS_LIVE live / $MODELS_LOCAL built" +[ "$MODELS_LIVE" = "$MODELS_LOCAL" ] || { echo "FAIL: model count mismatch" >&2; exit 1; } echo "done. If this was a major change: purge Cloudflare cache for partly.party." diff --git a/fktry/public/assets/models/archaeology-lab.glb b/fktry/public/assets/models/archaeology-lab.glb index f171ebc..7163df4 100644 Binary files a/fktry/public/assets/models/archaeology-lab.glb and b/fktry/public/assets/models/archaeology-lab.glb differ diff --git a/fktry/public/assets/models/artifact-bottler.glb b/fktry/public/assets/models/artifact-bottler.glb index 8dfef3a..7f9745b 100644 Binary files a/fktry/public/assets/models/artifact-bottler.glb and b/fktry/public/assets/models/artifact-bottler.glb differ diff --git a/fktry/public/assets/models/asic-cooler.glb b/fktry/public/assets/models/asic-cooler.glb index ae35896..e5ecdea 100644 Binary files a/fktry/public/assets/models/asic-cooler.glb and b/fktry/public/assets/models/asic-cooler.glb differ diff --git a/fktry/public/assets/models/bloom-duplicator.glb b/fktry/public/assets/models/bloom-duplicator.glb index ec748ca..0ab4407 100644 Binary files a/fktry/public/assets/models/bloom-duplicator.glb and b/fktry/public/assets/models/bloom-duplicator.glb differ diff --git a/fktry/public/assets/models/chroma-keyer.glb b/fktry/public/assets/models/chroma-keyer.glb index 8c6e272..d46cdc5 100644 Binary files a/fktry/public/assets/models/chroma-keyer.glb and b/fktry/public/assets/models/chroma-keyer.glb differ diff --git a/fktry/public/assets/models/dct-press.glb b/fktry/public/assets/models/dct-press.glb index a053ddc..6d8c00c 100644 Binary files a/fktry/public/assets/models/dct-press.glb and b/fktry/public/assets/models/dct-press.glb differ diff --git a/fktry/public/assets/models/decode-asic.glb b/fktry/public/assets/models/decode-asic.glb index fa4c795..3805a98 100644 Binary files a/fktry/public/assets/models/decode-asic.glb and b/fktry/public/assets/models/decode-asic.glb differ diff --git a/fktry/public/assets/models/demosaicer.glb b/fktry/public/assets/models/demosaicer.glb index eec1ebc..7db2537 100644 Binary files a/fktry/public/assets/models/demosaicer.glb and b/fktry/public/assets/models/demosaicer.glb differ diff --git a/fktry/public/assets/models/demuxer.glb b/fktry/public/assets/models/demuxer.glb index b136e9b..3f882d2 100644 Binary files a/fktry/public/assets/models/demuxer.glb and b/fktry/public/assets/models/demuxer.glb differ diff --git a/fktry/public/assets/models/field-loom.glb b/fktry/public/assets/models/field-loom.glb index f705735..67af390 100644 Binary files a/fktry/public/assets/models/field-loom.glb and b/fktry/public/assets/models/field-loom.glb differ diff --git a/fktry/public/assets/models/gop-assembler.glb b/fktry/public/assets/models/gop-assembler.glb index aa8b0e2..f2aff77 100644 Binary files a/fktry/public/assets/models/gop-assembler.glb and b/fktry/public/assets/models/gop-assembler.glb differ diff --git a/fktry/public/assets/models/hex-splicer.glb b/fktry/public/assets/models/hex-splicer.glb index b198ddb..b4d4f41 100644 Binary files a/fktry/public/assets/models/hex-splicer.glb and b/fktry/public/assets/models/hex-splicer.glb differ diff --git a/fktry/public/assets/models/matte-bath.glb b/fktry/public/assets/models/matte-bath.glb index e55f635..0377305 100644 Binary files a/fktry/public/assets/models/matte-bath.glb and b/fktry/public/assets/models/matte-bath.glb differ diff --git a/fktry/public/assets/models/mosh-reactor.glb b/fktry/public/assets/models/mosh-reactor.glb index 733e0d7..29959cd 100644 Binary files a/fktry/public/assets/models/mosh-reactor.glb and b/fktry/public/assets/models/mosh-reactor.glb differ diff --git a/fktry/public/assets/models/mosquito-trap.glb b/fktry/public/assets/models/mosquito-trap.glb index 45dfedb..61a3dad 100644 Binary files a/fktry/public/assets/models/mosquito-trap.glb and b/fktry/public/assets/models/mosquito-trap.glb differ diff --git a/fktry/public/assets/models/muxer.glb b/fktry/public/assets/models/muxer.glb index 3cd1590..b252b92 100644 Binary files a/fktry/public/assets/models/muxer.glb and b/fktry/public/assets/models/muxer.glb differ diff --git a/fktry/public/assets/models/mv-extractor.glb b/fktry/public/assets/models/mv-extractor.glb index 089eb7d..464d6d2 100644 Binary files a/fktry/public/assets/models/mv-extractor.glb and b/fktry/public/assets/models/mv-extractor.glb differ diff --git a/fktry/public/assets/models/neural-keyer.glb b/fktry/public/assets/models/neural-keyer.glb index 156c761..1dab88a 100644 Binary files a/fktry/public/assets/models/neural-keyer.glb and b/fktry/public/assets/models/neural-keyer.glb differ diff --git a/fktry/public/assets/models/optical-printer.glb b/fktry/public/assets/models/optical-printer.glb index 595a944..c19b40b 100644 Binary files a/fktry/public/assets/models/optical-printer.glb and b/fktry/public/assets/models/optical-printer.glb differ diff --git a/fktry/public/assets/models/p-caster.glb b/fktry/public/assets/models/p-caster.glb index 24ac8d8..436e2d8 100644 Binary files a/fktry/public/assets/models/p-caster.glb and b/fktry/public/assets/models/p-caster.glb differ diff --git a/fktry/public/assets/models/quantizer.glb b/fktry/public/assets/models/quantizer.glb index 61cf079..9ad2c20 100644 Binary files a/fktry/public/assets/models/quantizer.glb and b/fktry/public/assets/models/quantizer.glb differ diff --git a/fktry/public/assets/models/roto-bench.glb b/fktry/public/assets/models/roto-bench.glb index a78aa2e..e9a2634 100644 Binary files a/fktry/public/assets/models/roto-bench.glb and b/fktry/public/assets/models/roto-bench.glb differ diff --git a/fktry/public/assets/models/seam-extractor.glb b/fktry/public/assets/models/seam-extractor.glb index 2878fca..dae1d03 100644 Binary files a/fktry/public/assets/models/seam-extractor.glb and b/fktry/public/assets/models/seam-extractor.glb differ diff --git a/fktry/public/assets/models/shipper.glb b/fktry/public/assets/models/shipper.glb index a2afccf..3441aae 100644 Binary files a/fktry/public/assets/models/shipper.glb and b/fktry/public/assets/models/shipper.glb differ diff --git a/fktry/public/assets/models/software-decoder.glb b/fktry/public/assets/models/software-decoder.glb index 966a192..e3f3ea6 100644 Binary files a/fktry/public/assets/models/software-decoder.glb and b/fktry/public/assets/models/software-decoder.glb differ diff --git a/fktry/public/assets/models/subsampler.glb b/fktry/public/assets/models/subsampler.glb index dd04fc4..ac8d4fb 100644 Binary files a/fktry/public/assets/models/subsampler.glb and b/fktry/public/assets/models/subsampler.glb differ diff --git a/fktry/public/assets/models/telecine-puller.glb b/fktry/public/assets/models/telecine-puller.glb index 2da4c08..f357ab3 100644 Binary files a/fktry/public/assets/models/telecine-puller.glb and b/fktry/public/assets/models/telecine-puller.glb differ diff --git a/fktry/tools/optimize-assets.mjs b/fktry/tools/optimize-assets.mjs new file mode 100644 index 0000000..da73bbb --- /dev/null +++ b/fktry/tools/optimize-assets.mjs @@ -0,0 +1,69 @@ +#!/usr/bin/env node +/** + * Idempotent GLB optimization gate. Runs from deploy.sh BEFORE vite build. + * + * Why this exists: the farm emits 1–8 MB PBR GLBs. Shipped raw, 27 of them were + * 27.3 MB; through `gltf-transform optimize` they are 7.0 MB with the same PBR. + * That step used to live in my head, which is not a build system. An asset added + * by hand — or by a farm run that skipped the post-step — would silently ship raw. + * + * Skips anything already carrying EXT_meshopt_compression, so it is safe to run + * every deploy and costs ~0s when there is nothing new. + */ +import { execFileSync } from 'node:child_process'; +import { readdirSync, readFileSync, statSync, renameSync, rmSync } from 'node:fs'; +import { join } from 'node:path'; + +const DIR = new URL('../public/assets/models/', import.meta.url).pathname; +const MAX_KB = 900; // per-asset ceiling; a farm regression should fail the build + +/** Read the GLB JSON chunk without a glTF library. */ +function meta(path) { + const b = readFileSync(path); + if (b.length < 20 || b.subarray(0, 4).toString() !== 'glTF') return null; + const jsonLen = b.readUInt32LE(12); + try { + return JSON.parse(b.subarray(20, 20 + jsonLen).toString()); + } catch { + return null; + } +} + +const files = readdirSync(DIR).filter((f) => f.endsWith('.glb')); +let optimized = 0, skipped = 0, before = 0, after = 0; +const oversize = []; + +for (const f of files) { + const p = join(DIR, f); + const sizeBefore = statSync(p).size; + before += sizeBefore; + const m = meta(p); + if (m?.extensionsRequired?.includes('EXT_meshopt_compression')) { + skipped++; + after += sizeBefore; + } else { + const tmp = `${p}.opt`; + execFileSync('npx', ['--yes', '@gltf-transform/cli@latest', 'optimize', p, tmp, + '--texture-compress', 'webp', '--texture-size', '1024', + '--simplify-error', '0.002', '--compress', 'meshopt'], + { stdio: 'pipe', timeout: 900_000 }); + rmSync(p); + renameSync(tmp, p); + optimized++; + after += statSync(p).size; + console.log(` optimized ${f}: ${(sizeBefore / 1024) | 0} KB -> ${(statSync(p).size / 1024) | 0} KB`); + } + const kb = (statSync(p).size / 1024) | 0; + if (kb > MAX_KB) oversize.push(`${f} (${kb} KB)`); +} + +console.log(`assets: ${files.length} files, ${optimized} optimized, ${skipped} already lean — ` + + `${(before / 1048576).toFixed(1)} MB -> ${(after / 1048576).toFixed(1)} MB`); + +if (oversize.length) { + console.error(`\nFAIL: ${oversize.length} asset(s) over ${MAX_KB} KB after optimization:`); + for (const o of oversize) console.error(` ${o}`); + console.error('A mesh this heavy usually means the bake used too dense a pipeline ' + + '(trellis2 1024_cascade produces ~487k tris; hunyuan3d_mlx is the machine default).'); + process.exit(1); +}