fix: crash loop recovery, auto-migrations, CI test pipeline

- Wrap startup syncs in safeStartup() to prevent crash on external service failure
- Add migrate-entrypoint.sh for auto-generating migrations from schema diff
- Update Dockerfile migration stage to use entrypoint script
- Add test job to build-and-publish workflow (runs before build)
- Add tests.yaml workflow to run tests on every push
- Fix test setup to use real RSA key pair instead of plain strings
- Add test script to package.json
This commit is contained in:
2026-02-27 16:11:28 -06:00
parent b1f6462ac3
commit 508fa39835
8 changed files with 124 additions and 16 deletions
+29 -10
View File
@@ -45,36 +45,55 @@ if (!existingAdmin) {
events.emit("role:created", new RoleController(created));
}
// Helper to run a startup sync safely — failures are logged but never crash the process.
const safeStartup = async (label: string, fn: () => Promise<void>) => {
try {
await fn();
} catch (err) {
console.error(`[startup] ${label} failed — will retry on next interval`, err);
}
};
// Refresh the internal list of companies every minute
await refreshCompanies();
await safeStartup("refreshCompanies", refreshCompanies);
setInterval(() => {
return refreshCompanies();
return refreshCompanies().catch((err) =>
console.error("[interval] refreshCompanies failed", err),
);
}, 60 * 1000);
// Refresh the internal catalog every minute
await refreshCatalog();
await safeStartup("refreshCatalog", refreshCatalog);
setInterval(() => {
return refreshCatalog();
return refreshCatalog().catch((err) =>
console.error("[interval] refreshCatalog failed", err),
);
}, 60 * 1000);
// Refresh inventory on hand every 2 minutes
await refreshInventory();
await safeStartup("refreshInventory", refreshInventory);
setInterval(
() => {
return refreshInventory();
return refreshInventory().catch((err) =>
console.error("[interval] refreshInventory failed", err),
);
},
2 * 60 * 1000,
);
// Refresh opportunities every minute
await refreshOpportunities();
await safeStartup("refreshOpportunities", refreshOpportunities);
setInterval(() => {
return refreshOpportunities();
return refreshOpportunities().catch((err) =>
console.error("[interval] refreshOpportunities failed", err),
);
}, 60 * 1000);
await unifiSites.syncSites();
await safeStartup("syncSites", () => unifiSites.syncSites());
setInterval(() => {
return unifiSites.syncSites();
return unifiSites.syncSites().catch((err) =>
console.error("[interval] syncSites failed", err),
);
}, 60 * 1000);
Bun.serve({