#!/usr/bin/env node /** * Cross-platform goose migrate (+ optional sqlc generate). * Loads monorepo-root `.env` when DATABASE_URL is unset. * * Usage: * node scripts/migrate.mjs # goose only (default) * node scripts/migrate.mjs --sqlc # also regenerate apps/api/internal/db/sqlc * * A 4GiB host is enough for goose + the app. sqlc's go-pgquery/wazero path * allocates a ~4GiB WASM heap by itself, so it OOMs on 4GiB VPS — skip by * default; only run --sqlc on a machine with ≥6GiB free (or install a * prebuilt `sqlc` binary and still prefer ≥6GiB). */ import { spawnSync } from "node:child_process"; import path from "node:path"; import { detectHostProfile, formatHostNote, withGoLowMem, } from "./lowmem-env.mjs"; import { loadRootEnv, repoRoot } from "./root-env.mjs"; const args = new Set(process.argv.slice(2)); const wantSqlc = args.has("--sqlc") || process.env.DESCRYBE_SQLC === "1" || /^true|yes|on$/i.test(String(process.env.DESCRYBE_SQLC || "")); /** Prefer this much free/total RAM (MiB) before attempting sqlc go-run/wazero. */ const SQLC_MIN_RAM_MIB = Number(process.env.DESCRYBE_SQLC_MIN_RAM_MIB || 6144); const profile = detectHostProfile(); const env = withGoLowMem(loadRootEnv(process.env), profile); if (!env.DATABASE_URL) { console.error( "DATABASE_URL is required (set in monorepo-root .env or the shell; see .env.example)", ); process.exit(1); } const apiDir = path.join(repoRoot, "apps", "api"); function run(command, cmdArgs, opts = {}) { const r = spawnSync(command, cmdArgs, { cwd: apiDir, env, stdio: "inherit", // Keep go argv intact on Windows (ldflags / module paths). shell: process.platform === "win32" && command !== "go", ...opts, }); if (r.error) { console.error(r.error.message); process.exit(1); } if (r.status !== 0) { process.exit(r.status ?? 1); } } function totalRamMiB() { return profile.totalMiB; } function hasSqlcBinary() { const r = spawnSync("sqlc", ["version"], { encoding: "utf8", shell: process.platform === "win32", }); return !(r.error || r.status !== 0); } console.log("==> goose up (apps/api/sql/schema)"); console.log(`NOTE ${formatHostNote(profile)}`); console.log( `NOTE GOMAXPROCS=${env.GOMAXPROCS}` + (env.GOMEMLIMIT ? ` GOMEMLIMIT=${env.GOMEMLIMIT}` : "") + (env.GOGC ? ` GOGC=${env.GOGC}` : "") + " (sqlc skipped by default; needs ≥6GiB)", ); run("go", [ "run", "github.com/pressly/goose/v3/cmd/goose@v3.24.3", "-dir", "sql/schema", "postgres", env.DATABASE_URL, "up", ]); if (!wantSqlc) { console.log( "==> sqlc skipped (default). 4GiB hosts: goose + app are enough; sqlc needs extra headroom.", ); console.log(" Regenerate on a larger machine: npm run migrate -- --sqlc"); console.log("==> done"); process.exit(0); } const ram = totalRamMiB(); if (ram != null && ram < SQLC_MIN_RAM_MIB && !hasSqlcBinary()) { console.error( `[migrate] refusing sqlc: ~${ram}MiB RAM detected (need ≥${SQLC_MIN_RAM_MIB}MiB for wazero go run).`, ); console.error( "[migrate] goose already applied. Install a prebuilt sqlc binary, or run --sqlc on a larger host.", ); console.error( "[migrate] Or set DESCRYBE_SQLC_MIN_RAM_MIB=0 to force (may OOM).", ); process.exit(1); } console.log("==> sqlc generate"); const sqlc = spawnSync("sqlc", ["generate"], { cwd: apiDir, env, stdio: "inherit", shell: process.platform === "win32", }); if (sqlc.status === 0) { console.log("==> done"); process.exit(0); } console.log("sqlc not on PATH (or failed); using go run"); if (ram != null && ram < SQLC_MIN_RAM_MIB) { console.error( `[migrate] go run sqlc will likely OOM on ~${ram}MiB (wazero ~4GiB heap).`, ); console.error( "[migrate] Install https://docs.sqlc.dev/en/latest/overview/install.html then retry, or use a larger host.", ); process.exit(1); } run("go", [ "run", "github.com/sqlc-dev/sqlc/cmd/sqlc@v1.28.0", "generate", ]); console.log("==> done");