feat: add Turborepo per-project monorepo scaffold and project API

- Add Turborepo scaffold templates (apps: product, website, admin, storybook; packages: ui, tokens, types, config)
- Add ProjectRecord and AppRecord types to control plane
- Add Gitea integration service (repo creation, scaffold push, webhooks)
- Add Coolify integration service (project + per-app service provisioning with turbo --filter)
- Add project routes: GET/POST /projects, GET /projects/:id/apps, POST /projects/:id/deploy
- Update chat route to inject project/monorepo context into AI requests
- Add deploy_app and scaffold_app tools to Gemini tool set
- Update deploy executor with monorepo-aware /execute/deploy endpoint
- Add TURBOREPO_MIGRATION_PLAN.md documenting rationale and scope

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-02-21 15:07:35 -08:00
parent 57b9ce2f1a
commit 2c3e7f9dfb
40 changed files with 1625 additions and 33 deletions

View File

@@ -11,37 +11,64 @@ await app.register(sensible);
app.get("/healthz", async () => ({ ok: true, executor: "deploy" }));
/**
* Deploy a Cloud Run service
* In production: triggers Cloud Build, deploys to Cloud Run
* In dev: returns mock response
* Deploy an app from a Turborepo monorepo.
*
* Expects input to include:
* - repo_url: git clone URL (the project monorepo)
* - app_name: the app folder name under apps/ (e.g. "product", "website")
* - ref: git branch/tag/sha (default "main")
* - env: target environment ("dev" | "staging" | "prod")
*
* Build command: turbo run build --filter={app_name}
* In production this triggers Coolify via its API; in dev it returns a mock.
*/
app.post("/execute/deploy", async (req) => {
const body = req.body as any;
const { run_id, tenant_id, input } = body;
const appName = input.app_name ?? input.service_name ?? "product";
const repoUrl = input.repo_url ?? "";
const ref = input.ref ?? "main";
const env = input.env ?? "dev";
console.log(`🚀 Monorepo deploy request:`, { run_id, tenant_id, appName, repoUrl, ref, env });
await new Promise(r => setTimeout(r, 1500));
const mockRevision = `${appName}-${Date.now().toString(36)}`;
const mockUrl = `https://${appName}-${ref}.vibnai.com`;
console.log(`✅ Deploy complete:`, { appName, revision: mockRevision, url: mockUrl });
return {
app_name: appName,
service_url: mockUrl,
revision: mockRevision,
build_command: `turbo run build --filter=${appName}`,
build_id: `build-${Date.now()}`,
deployed_at: new Date().toISOString(),
env,
};
});
// Legacy Cloud Run endpoint — kept for backwards compatibility
app.post("/execute/cloudrun/deploy", async (req) => {
const body = req.body as any;
const { run_id, tenant_id, input } = body;
console.log(`🚀 Deploy request:`, { run_id, tenant_id, input });
// Simulate deployment time
console.log(`🚀 Deploy request (legacy):`, { run_id, tenant_id, input });
await new Promise(r => setTimeout(r, 1500));
// In production, this would:
// 1. Clone the repo
// 2. Trigger Cloud Build
// 3. Deploy to Cloud Run
// 4. Return the service URL
const mockRevision = `${input.service_name}-${Date.now().toString(36)}`;
const mockUrl = `https://${input.service_name}-abc123.a.run.app`;
console.log(`✅ Deploy complete:`, { revision: mockRevision, url: mockUrl });
return {
service_url: mockUrl,
revision: mockRevision,
build_id: `build-${Date.now()}`,
deployed_at: new Date().toISOString(),
region: input.region ?? "us-central1",
env: input.env
env: input.env,
};
});