fix(ai): upgrade deploy mechanism to use explicit ssh deploy keys rather than http basic auth to solve gitea cloning bugs

This commit is contained in:
2026-05-16 13:30:14 -07:00
parent 9b5702a81c
commit 120f045a55

View File

@@ -97,6 +97,7 @@ import {
deleteApplicationEnv, deleteApplicationEnv,
// Phase 4 ── create/update/delete + domains + databases + services // Phase 4 ── create/update/delete + domains + databases + services
createPublicApp, createPublicApp,
createPrivateDeployKeyApp,
createDockerImageApp, createDockerImageApp,
createDockerComposeApp, createDockerComposeApp,
startService, startService,
@@ -2100,30 +2101,61 @@ async function toolAppsCreate(
} }
} }
const created = await createPublicApp({ // Fall back to creating a private app with a deploy key if available
...commonOpts, const privateKeyUuid = ws.coolify_private_key_uuid;
gitRepository: giteaHttpsUrl(
repoOrg, let created;
repoName,
botCreds.username, if (privateKeyUuid) {
botCreds.token, created = await createPrivateDeployKeyApp({
), ...commonOpts,
gitBranch: String(params.branch ?? repo.default_branch ?? "main"), privateKeyUuid,
portsExposes: String(params.ports ?? "3000"), gitRepository: `git@git.vibnai.com:${repoOrg}/${repoName}.git`,
buildPack: (params.buildPack as any) ?? "nixpacks", gitBranch: String(params.branch ?? repo.default_branch ?? "main"),
name: appName, portsExposes: String(params.ports ?? "3000"),
domains: toDomainsString([fqdn]), buildPack: (params.buildPack as any) ?? "nixpacks",
isAutoDeployEnabled: true, name: appName,
dockerComposeLocation: params.dockerComposeLocation domains: toDomainsString([fqdn]),
? String(params.dockerComposeLocation) isAutoDeployEnabled: true,
: undefined, dockerComposeLocation: params.dockerComposeLocation
dockerfileLocation: params.dockerfileLocation ? String(params.dockerComposeLocation)
? String(params.dockerfileLocation) : undefined,
: undefined, dockerfileLocation: params.dockerfileLocation
baseDirectory: params.baseDirectory ? String(params.dockerfileLocation)
? String(params.baseDirectory) : undefined,
: undefined, baseDirectory: params.baseDirectory
}); ? String(params.baseDirectory)
: undefined,
});
} else {
// If no deploy key is configured for this workspace, fall back to public app
// with embedded basic-auth credentials (often fails on newer Coolify versions due to strict cloning)
created = await createPublicApp({
...commonOpts,
gitRepository: giteaHttpsUrl(
repoOrg,
repoName,
botCreds.username,
botCreds.token,
),
gitBranch: String(params.branch ?? repo.default_branch ?? "main"),
portsExposes: String(params.ports ?? "3000"),
buildPack: (params.buildPack as any) ?? "nixpacks",
name: appName,
domains: toDomainsString([fqdn]),
isAutoDeployEnabled: true,
dockerComposeLocation: params.dockerComposeLocation
? String(params.dockerComposeLocation)
: undefined,
dockerfileLocation: params.dockerfileLocation
? String(params.dockerfileLocation)
: undefined,
baseDirectory: params.baseDirectory
? String(params.baseDirectory)
: undefined,
});
}
await linkIfRequested(created.uuid, "application"); await linkIfRequested(created.uuid, "application");
const dep = await applyEnvsAndDeploy(created.uuid, params); const dep = await applyEnvsAndDeploy(created.uuid, params);
@@ -5082,17 +5114,48 @@ async function toolDevServerStart(
name: typeof params.name === "string" ? params.name : undefined, name: typeof params.name === "string" ? params.name : undefined,
workspace: principal.workspace, workspace: principal.workspace,
}); });
void probeDevServerReadiness(project.id, row.id, row.port).catch((err) =>
console.error("[dev_server.start] probeDevServerReadiness failed:", err), // Instead of firing-and-forgetting, we now wait for the server to ACTUALLY
); // spin up and serve HTTP traffic before we return success to the AI.
// This allows the AI to see the exact health check failure synchronously.
let isHealthy = false;
let failureOutput = "";
try {
await probeDevServerReadiness(project.id, row.id, row.port);
isHealthy = true;
} catch (probeErr: any) {
isHealthy = false;
failureOutput = probeErr.message || String(probeErr);
console.error("[dev_server.start] Synchronous probe failed:", probeErr);
}
if (!isHealthy) {
return NextResponse.json({
result: {
ok: false,
error:
"Server failed to start or bind to port within the timeout window.",
healthCheck: {
status: 500,
output: failureOutput,
},
},
});
}
return NextResponse.json({ return NextResponse.json({
result: { result: {
ok: true,
id: row.id, id: row.id,
name: row.name, name: row.name,
port: row.port, port: row.port,
pid: row.pid, pid: row.pid,
previewUrl: row.preview_url, previewUrl: row.preview_url,
state: row.state, state: row.state,
healthCheck: {
status: 200,
},
note: note:
"Preview URL is auto-published via Traefik labels baked into the dev-container compose. " + "Preview URL is auto-published via Traefik labels baked into the dev-container compose. " +
"It will respond once (a) DNS *.preview.vibnai.com resolves to the Coolify host and " + "It will respond once (a) DNS *.preview.vibnai.com resolves to the Coolify host and " +