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: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", "test:integration:cov": "jest -c jest.integration.config.js --runInBand --coverage --forceExit",
"bench:hashing": "tsx scripts/bench-hash-latency.ts", "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:up": "docker compose -f docker-compose.test.yml up -d",
"db:test:down": "docker compose -f docker-compose.test.yml down -v", "db:test:down": "docker compose -f docker-compose.test.yml down -v",
"migrate:up": "node migrate.js up", "migrate:up": "node migrate.js up",
@@ -15,11 +15,25 @@
* - It never renames the stored file, so item_images.image_path stays correct * - It never renames the stored file, so item_images.image_path stays correct
* and no database write is needed at all. * 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 * Usage
* ----- * -----
* Locally, after `npm run build`:
*
* npm run backfill:images # report only * npm run backfill:images # report only
* npm run backfill:images -- --apply # rewrite the files * 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, * Point it at QA first. Compare a handful of images by eye before production,
* and take a backup that you have confirmed restores. * and take a backup that you have confirmed restores.
*/ */
@@ -27,8 +41,8 @@
import sharp from 'sharp'; import sharp from 'sharp';
import { promises as fs } from 'fs'; import { promises as fs } from 'fs';
import path from 'path'; import path from 'path';
import { pool } from '../src/db'; import { pool } from './db';
import { needsProcessing, reencodeInPlace } from '../src/imageProcessing'; import { needsProcessing, reencodeInPlace } from './imageProcessing';
const UPLOADS_DIR = process.env.UPLOADS_DIR || '/app/uploads'; const UPLOADS_DIR = process.env.UPLOADS_DIR || '/app/uploads';
const APPLY = process.argv.includes('--apply'); const APPLY = process.argv.includes('--apply');
@@ -152,9 +166,17 @@ async function run(): Promise<void> {
} }
} }
run() // 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) => { .catch((err) => {
console.error(err); console.error(err);
process.exitCode = 1; process.exitCode = 1;
}) })
.finally(() => pool.end()); .finally(() => pool.end());
}