Merge pull request 'fix(uploads): ship the image backfill script in the container image (#231)' (#232) from fix/231-ship-backfill-script into main
Linting / lint (push) Successful in 2m54s
SonarQube Analysis / sonarqube (push) Failing after 20m52s

Reviewed-on: #232
This commit was merged in pull request #232.
This commit is contained in:
2026-08-29 15:10:06 -05:00
2 changed files with 31 additions and 9 deletions
+1 -1
View File
@@ -19,7 +19,7 @@
"test:integration:json": "jest -c jest.integration.config.js --runInBand --json --outputFile=integration-results.json",
"test:integration:cov": "jest -c jest.integration.config.js --runInBand --coverage --forceExit",
"bench:hashing": "tsx scripts/bench-hash-latency.ts",
"backfill:images": "tsx scripts/backfill-image-reencode.ts",
"backfill:images": "node dist/backfillImageReencode.js",
"db:test:up": "docker compose -f docker-compose.test.yml up -d",
"db:test:down": "docker compose -f docker-compose.test.yml down -v",
"migrate:up": "node migrate.js up",
@@ -15,11 +15,25 @@
* - It never renames the stored file, so item_images.image_path stays correct
* and no database write is needed at all.
*
* It lives in src/ rather than scripts/ so that it compiles into dist and ships
* in the container image. `scripts/` is excluded by tsconfig, is never copied by
* the Dockerfile, and would need `tsx` a devDependency that `npm install
* --omit=dev` removes. An operational task that can only be useful where the
* images are has to be somewhere the image actually carries it, which is the
* same reason `migrate.js` sits where it does. See #231.
*
* Usage
* -----
* Locally, after `npm run build`:
*
* npm run backfill:images # report only
* npm run backfill:images -- --apply # rewrite the files
*
* In a deployed container, identically package.json ships in the image:
*
* docker exec <container> npm run backfill:images
* docker exec <container> npm run backfill:images -- --apply
*
* Point it at QA first. Compare a handful of images by eye before production,
* and take a backup that you have confirmed restores.
*/
@@ -27,8 +41,8 @@
import sharp from 'sharp';
import { promises as fs } from 'fs';
import path from 'path';
import { pool } from '../src/db';
import { needsProcessing, reencodeInPlace } from '../src/imageProcessing';
import { pool } from './db';
import { needsProcessing, reencodeInPlace } from './imageProcessing';
const UPLOADS_DIR = process.env.UPLOADS_DIR || '/app/uploads';
const APPLY = process.argv.includes('--apply');
@@ -152,9 +166,17 @@ async function run(): Promise<void> {
}
}
run()
.catch((err) => {
console.error(err);
process.exitCode = 1;
})
.finally(() => pool.end());
// Guarded rather than run on import. This file lives in src/ so that it
// compiles into dist and therefore ships in the image (#231) — but that puts a
// catalogue-wide, irreversible rewrite in the same directory as the modules the
// server imports at boot. Without this guard, importing it by mistake would run
// it. Nothing imports it today; the guard is here so that staying true does not
// depend on anyone noticing.
if (require.main === module) {
run()
.catch((err) => {
console.error(err);
process.exitCode = 1;
})
.finally(() => pool.end());
}