presenton/electron/scripts/prepare_fastapi_migrations.js

85 lines
2.7 KiB
JavaScript

const fs = require("fs");
const path = require("path");
const projectRoot = path.resolve(__dirname, "..");
const sourceDir = path.join(projectRoot, "..", "servers", "fastapi", "alembic", "versions");
const targetDir = path.join(projectRoot, "servers", "fastapi", "alembic", "versions");
function listPythonMigrations(dirPath) {
return fs
.readdirSync(dirPath, { withFileTypes: true })
.filter((entry) => entry.isFile() && entry.name.endsWith(".py"))
.map((entry) => entry.name)
.sort();
}
function extractRevisionInfo(filePath) {
const content = fs.readFileSync(filePath, "utf8");
const revisionMatch = content.match(/revision:\s*str\s*=\s*['"]([^'"]+)['"]/);
const downRevisionMatch = content.match(
/down_revision:\s*[^=]*=\s*(None|['"][^'"]+['"])/
);
if (!revisionMatch) {
throw new Error(`Missing revision id in ${filePath}`);
}
if (!downRevisionMatch) {
throw new Error(`Missing down_revision in ${filePath}`);
}
const revision = revisionMatch[1];
const downRaw = downRevisionMatch[1];
const downRevision = downRaw === "None" ? null : downRaw.slice(1, -1);
return { revision, downRevision };
}
function validateSingleHead(dirPath, filenames) {
const revisions = new Map();
const downRevisions = new Set();
for (const filename of filenames) {
const filePath = path.join(dirPath, filename);
const { revision, downRevision } = extractRevisionInfo(filePath);
if (revisions.has(revision)) {
throw new Error(`Duplicate revision id ${revision} in ${filename}`);
}
revisions.set(revision, filename);
if (downRevision) {
downRevisions.add(downRevision);
}
}
const heads = [...revisions.keys()].filter((revision) => !downRevisions.has(revision));
if (heads.length !== 1) {
throw new Error(
`Expected exactly one Alembic head, found ${heads.length}: ${heads.join(", ")}`
);
}
}
function syncMigrations() {
if (!fs.existsSync(sourceDir)) {
throw new Error(`Source migrations directory not found: ${sourceDir}`);
}
if (!fs.existsSync(targetDir)) {
throw new Error(`Target migrations directory not found: ${targetDir}`);
}
const sourceFiles = listPythonMigrations(sourceDir);
if (sourceFiles.length === 0) {
throw new Error(`No migration files found in source directory: ${sourceDir}`);
}
for (const filename of sourceFiles) {
fs.copyFileSync(path.join(sourceDir, filename), path.join(targetDir, filename));
}
const targetFiles = listPythonMigrations(targetDir);
validateSingleHead(targetDir, targetFiles);
console.log(
`Synced ${sourceFiles.length} migration files into ${targetFiles.length} target migrations and verified a single Alembic head.`
);
}
syncMigrations();