stack/apps/backend/scripts/db-migrations.ts
Zai Shi a7acab4646
Auto migration (#526)
<!--

Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md

-->

<!-- ELLIPSIS_HIDDEN -->


----

> [!IMPORTANT]
> Introduces an automated database migration system, replacing manual
Prisma commands with new scripts and updating workflows, configurations,
and tests accordingly.
> 
>   - **Auto-Migration System**:
> - Introduces `db-migrations.ts` script for handling database
migrations automatically.
> - Adds utility functions in `utils.tsx` for managing migration files.
> - Implements `applyMigrations` and `runMigrationNeeded` in `index.tsx`
for executing migrations.
>   - **Workflow and Scripts**:
> - Updates GitHub workflows (`check-prisma-migrations.yaml`,
`e2e-api-tests.yaml`) to use new migration commands.
> - Replaces `prisma migrate` commands with `db:init`, `db:migrate`,
etc., in `package.json` and `README.md`.
>   - **Testing**:
> - Adds `auto-migration.tests.ts` for testing migration logic and
concurrency handling.
>   - **Configuration**:
> - Updates `.env.development` and `vitest.config.ts` for new
environment variables and paths.
> - Modifies `turbo.json` and `package.json` to include new migration
tasks and scripts.
> 
> <sup>This description was created by </sup>[<img alt="Ellipsis"
src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=stack-auth%2Fstack-auth&utm_source=github&utm_medium=referral)<sup>
for 2c24183879. You can
[customize](https://app.ellipsis.dev/stack-auth/settings/summaries) this
summary. It will automatically update as commits are pushed.</sup>


<!-- ELLIPSIS_HIDDEN -->

---------

Co-authored-by: Konsti Wohlwend <n2d4xc@gmail.com>
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-07-24 02:38:37 +02:00

108 lines
2.8 KiB
TypeScript

import { applyMigrations } from "@/auto-migrations";
import { MIGRATION_FILES_DIR, getMigrationFiles } from "@/auto-migrations/utils";
import { globalPrismaClient, globalPrismaSchema } from "@/prisma-client";
import { execSync } from "child_process";
import * as readline from 'readline';
const dropSchema = async () => {
await globalPrismaClient.$executeRaw`DROP SCHEMA ${globalPrismaSchema} CASCADE`;
await globalPrismaClient.$executeRaw`CREATE SCHEMA ${globalPrismaSchema}`;
await globalPrismaClient.$executeRaw`GRANT ALL ON SCHEMA ${globalPrismaSchema} TO postgres`;
await globalPrismaClient.$executeRaw`GRANT ALL ON SCHEMA ${globalPrismaSchema} TO public`;
};
const seed = async () => {
execSync('pnpm run db-seed-script', { stdio: 'inherit' });
};
const promptDropDb = async () => {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const answer = await new Promise<string>(resolve => {
rl.question('Are you sure you want to drop everything in the database? This action cannot be undone. (y/N): ', resolve);
});
rl.close();
if (answer.toLowerCase() !== 'y') {
console.log('Operation cancelled');
process.exit(0);
}
};
const migrate = async () => {
await applyMigrations({
prismaClient: globalPrismaClient,
migrationFiles: getMigrationFiles(MIGRATION_FILES_DIR),
logging: true,
schema: globalPrismaSchema,
});
};
const showHelp = () => {
console.log(`Database Migration Script
Usage: pnpm db-migrations <command>
Commands:
reset Drop all data and recreate the database, then apply migrations and seed
generate-migration-file Generate a new migration file using Prisma, then reset and migrate
seed [Advanced] Run database seeding only
init Apply migrations and seed the database
migrate Apply migrations
help Show this help message
`);
};
const main = async () => {
const args = process.argv.slice(2);
const command = args[0];
switch (command) {
case 'reset': {
await promptDropDb();
await dropSchema();
await migrate();
await seed();
break;
}
case 'generate-migration-file': {
execSync('pnpm prisma migrate dev --skip-seed', { stdio: 'inherit' });
await dropSchema();
await migrate();
await seed();
break;
}
case 'seed': {
await seed();
break;
}
case 'init': {
await migrate();
await seed();
break;
}
case 'migrate': {
await migrate();
break;
}
case 'help': {
showHelp();
break;
}
default: {
console.error('Unknown command.');
showHelp();
process.exit(1);
}
}
};
// eslint-disable-next-line no-restricted-syntax
main().catch((error) => {
console.error(error);
process.exit(1);
});