forked from syntaxbullet/aurorabot
feat: add bot-triggered deployment via /update deploy command
- Added Docker socket mount to docker-compose.prod.yml - Added project directory mount for git operations - Added performDeploy, isDeployAvailable methods to UpdateService - Added /update deploy subcommand for Discord-triggered deployments - Added deploy-related embeds to update.view.ts
This commit is contained in:
@@ -469,4 +469,109 @@ export class UpdateService {
|
||||
}
|
||||
// Don't clear rollback cache here - rollback file persists
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Bot-Triggered Deployment Methods
|
||||
// =========================================================================
|
||||
|
||||
private static readonly DEPLOY_TIMEOUT_MS = 300_000; // 5 minutes for full deploy
|
||||
|
||||
/**
|
||||
* Get the deploy directory path from environment or default
|
||||
*/
|
||||
static getDeployDir(): string {
|
||||
return process.env.DEPLOY_DIR || "/app/deploy";
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if deployment is available (docker socket accessible)
|
||||
*/
|
||||
static async isDeployAvailable(): Promise<boolean> {
|
||||
try {
|
||||
await execWithTimeout("docker --version", 5000);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a full deployment: git pull + docker compose rebuild
|
||||
* This will restart the container with the new code
|
||||
*
|
||||
* @param onProgress - Callback for progress updates
|
||||
* @returns Object with success status, output, and commit info
|
||||
*/
|
||||
static async performDeploy(onProgress?: (step: string) => void): Promise<{
|
||||
success: boolean;
|
||||
previousCommit: string;
|
||||
newCommit: string;
|
||||
output: string;
|
||||
error?: string;
|
||||
}> {
|
||||
const deployDir = this.getDeployDir();
|
||||
let previousCommit = "";
|
||||
let newCommit = "";
|
||||
let output = "";
|
||||
|
||||
try {
|
||||
// 1. Get current commit
|
||||
onProgress?.("Getting current version...");
|
||||
const { stdout: currentSha } = await execWithTimeout(
|
||||
`cd ${deployDir} && git rev-parse --short HEAD`,
|
||||
DEFAULT_TIMEOUT_MS
|
||||
);
|
||||
previousCommit = currentSha.trim();
|
||||
output += `📍 Current: ${previousCommit}\n`;
|
||||
|
||||
// 2. Pull latest changes
|
||||
onProgress?.("Pulling latest code...");
|
||||
const { stdout: pullOutput } = await execWithTimeout(
|
||||
`cd ${deployDir} && git pull origin main`,
|
||||
DEFAULT_TIMEOUT_MS
|
||||
);
|
||||
output += `📥 Pull: ${pullOutput.includes("Already up to date") ? "Already up to date" : "Updated"}\n`;
|
||||
|
||||
// 3. Get new commit
|
||||
const { stdout: newSha } = await execWithTimeout(
|
||||
`cd ${deployDir} && git rev-parse --short HEAD`,
|
||||
DEFAULT_TIMEOUT_MS
|
||||
);
|
||||
newCommit = newSha.trim();
|
||||
output += `📍 New: ${newCommit}\n`;
|
||||
|
||||
// 4. Rebuild and restart container (this will kill the current process)
|
||||
onProgress?.("Rebuilding and restarting...");
|
||||
|
||||
// Use spawn with detached mode so the command continues after we exit
|
||||
const { spawn } = await import("child_process");
|
||||
const deployProcess = spawn(
|
||||
"sh",
|
||||
["-c", `cd ${deployDir} && docker compose -f docker-compose.prod.yml up -d --build`],
|
||||
{
|
||||
detached: true,
|
||||
stdio: "ignore"
|
||||
}
|
||||
);
|
||||
deployProcess.unref();
|
||||
|
||||
output += `🚀 Deploy triggered - container will restart\n`;
|
||||
|
||||
return {
|
||||
success: true,
|
||||
previousCommit,
|
||||
newCommit,
|
||||
output
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
previousCommit,
|
||||
newCommit,
|
||||
output,
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user