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:
@@ -97,6 +97,7 @@ import {
|
||||
deleteApplicationEnv,
|
||||
// Phase 4 ── create/update/delete + domains + databases + services
|
||||
createPublicApp,
|
||||
createPrivateDeployKeyApp,
|
||||
createDockerImageApp,
|
||||
createDockerComposeApp,
|
||||
startService,
|
||||
@@ -2100,30 +2101,61 @@ async function toolAppsCreate(
|
||||
}
|
||||
}
|
||||
|
||||
const 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,
|
||||
});
|
||||
// Fall back to creating a private app with a deploy key if available
|
||||
const privateKeyUuid = ws.coolify_private_key_uuid;
|
||||
|
||||
let created;
|
||||
|
||||
if (privateKeyUuid) {
|
||||
created = await createPrivateDeployKeyApp({
|
||||
...commonOpts,
|
||||
privateKeyUuid,
|
||||
gitRepository: `git@git.vibnai.com:${repoOrg}/${repoName}.git`,
|
||||
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,
|
||||
});
|
||||
} 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");
|
||||
|
||||
const dep = await applyEnvsAndDeploy(created.uuid, params);
|
||||
@@ -5082,17 +5114,48 @@ async function toolDevServerStart(
|
||||
name: typeof params.name === "string" ? params.name : undefined,
|
||||
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({
|
||||
result: {
|
||||
ok: true,
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
port: row.port,
|
||||
pid: row.pid,
|
||||
previewUrl: row.preview_url,
|
||||
state: row.state,
|
||||
healthCheck: {
|
||||
status: 200,
|
||||
},
|
||||
note:
|
||||
"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 " +
|
||||
|
||||
Reference in New Issue
Block a user