uat-seed-script

This commit is contained in:
phaichayon
2026-06-30 10:48:15 +07:00
parent e4573ac0c9
commit e12ae120b0
31 changed files with 1791 additions and 314 deletions

View File

@@ -42,11 +42,21 @@ function loadLocalEnv() {
loadEnvFile(path.resolve(process.cwd(), '.env'));
}
const ALLOW_DB_RESET_FLAG = '--allow-db-reset';
function assertSafeToReset(databaseUrl: string) {
if (process.env.ALLOW_DB_RESET !== 'true') {
throw new Error('ALLOW_DB_RESET=true is required.');
const allowDbReset =
process.env.ALLOW_DB_RESET === 'true' ||
process.argv.includes(ALLOW_DB_RESET_FLAG);
if (!allowDbReset) {
throw new Error(
`Database reset requires explicit confirmation. Run "npm run db:reset -- ${ALLOW_DB_RESET_FLAG}".`
);
}
process.env.ALLOW_DB_RESET = 'true';
if ((process.env.NODE_ENV ?? '').toLowerCase() === 'production') {
throw new Error('Database reset is blocked in NODE_ENV=production.');
}

110
scripts/lib/seed-utils.ts Normal file
View File

@@ -0,0 +1,110 @@
import { spawnSync } from 'node:child_process';
import { createHash } from 'node:crypto';
import fs from 'node:fs';
import path from 'node:path';
import postgres from 'postgres';
export type SqlClient = ReturnType<typeof postgres>;
function loadEnvFile(filePath: string) {
if (!fs.existsSync(filePath)) {
return;
}
const content = fs.readFileSync(filePath, 'utf8');
for (const rawLine of content.split(/\r?\n/)) {
const line = rawLine.trim();
if (!line || line.startsWith('#')) {
continue;
}
const separatorIndex = line.indexOf('=');
if (separatorIndex === -1) {
continue;
}
const key = line.slice(0, separatorIndex).trim();
let value = line.slice(separatorIndex + 1).trim();
if (
(value.startsWith('"') && value.endsWith('"')) ||
(value.startsWith("'") && value.endsWith("'"))
) {
value = value.slice(1, -1);
}
if (!(key in process.env)) {
process.env[key] = value;
}
}
}
export function loadLocalEnv() {
loadEnvFile(path.resolve(process.cwd(), '.env.local'));
loadEnvFile(path.resolve(process.cwd(), '.env'));
}
export function requireEnv(name: string): string {
const value = process.env[name]?.trim();
if (!value) {
throw new Error(`Missing required environment variable: ${name}`);
}
return value;
}
export function createSqlClient(): SqlClient {
loadLocalEnv();
return postgres(requireEnv('DATABASE_URL'), { prepare: false });
}
export function deterministicId(input: string): string {
const digest = createHash('sha256').update(input).digest('hex');
const part3 = `5${digest.slice(13, 16)}`;
const part4 = `a${digest.slice(17, 20)}`;
return [
digest.slice(0, 8),
digest.slice(8, 12),
part3,
part4,
digest.slice(20, 32)
].join('-');
}
export const NODE_TS_RUNTIME_ARGS = [
'--disable-warning=ExperimentalWarning',
'--disable-warning=MODULE_TYPELESS_PACKAGE_JSON',
'--experimental-strip-types'
];
export function runNodeScript(label: string, scriptPath: string, args: string[] = []) {
const result = spawnSync(
process.execPath,
[...NODE_TS_RUNTIME_ARGS, scriptPath, ...args],
{
stdio: 'inherit',
env: process.env
}
);
if (result.status !== 0) {
throw new Error(`[${label}] failed with exit code ${result.status ?? 1}`);
}
}
export function runCommand(label: string, command: string, args: string[] = []) {
const result = spawnSync(command, args, {
stdio: 'inherit',
env: process.env,
shell: process.platform === 'win32'
});
if (result.status !== 0) {
throw new Error(`[${label}] failed with exit code ${result.status ?? 1}`);
}
}

25
scripts/seed-reset.ts Normal file
View File

@@ -0,0 +1,25 @@
import { runCommand, runNodeScript } from './lib/seed-utils.ts';
const ALLOW_DB_RESET_FLAG = '--allow-db-reset';
function main() {
const allowDbReset =
process.env.ALLOW_DB_RESET === 'true' ||
process.argv.includes(ALLOW_DB_RESET_FLAG);
if (!allowDbReset) {
throw new Error(
`seed:reset requires explicit confirmation. Run "npm run seed:reset -- ${ALLOW_DB_RESET_FLAG}".`
);
}
// Normalize the guard so downstream reset scripts keep the same safety contract.
process.env.ALLOW_DB_RESET = 'true';
runNodeScript('seed:reset:db-reset', 'scripts/db/reset-database.ts');
runCommand('seed:reset:db-migrate', 'npx', ['drizzle-kit', 'migrate']);
runNodeScript('seed:reset:seed-system', 'scripts/seed-system.ts');
runNodeScript('seed:reset:seed-uat', 'scripts/seed-uat.ts');
}
main();

7
scripts/seed-system.ts Normal file
View File

@@ -0,0 +1,7 @@
import { runNodeScript } from './lib/seed-utils.ts';
function main() {
runNodeScript('seed:system:core', 'src/db/seeds/system.seed.ts');
}
main();

7
scripts/seed-uat.ts Normal file
View File

@@ -0,0 +1,7 @@
import { runNodeScript } from './lib/seed-utils.ts';
function main() {
runNodeScript('seed:uat:core', 'src/db/seeds/uat.seed.ts');
}
main();