|
import { $, sleep, fetch, env } from "bun"; |
|
import chalk from "chalk"; |
|
|
|
//Console helpers |
|
const _cGroup = console.group; |
|
const _cGroupEnd = console.groupEnd; |
|
const groupDiv = '='.repeat(42); |
|
console.group = (...args: any[]) => { |
|
const header = ('== ' + args.join(' ')).padEnd(42, ' '); |
|
console.log(chalk.inverse(groupDiv)); |
|
console.log(chalk.inverse(header)); |
|
console.log(chalk.inverse(groupDiv)); |
|
_cGroup(); //start group |
|
}; |
|
console.groupEnd = () => { |
|
process.stdout.write('\n'); |
|
_cGroupEnd(); //close group |
|
} |
|
|
|
|
|
//Configs |
|
const attemptDelay = 5000; // 5 seconds |
|
const attemptLimit = 30; // 30 attempts (5 minutes total) |
|
|
|
console.group('Building image'); |
|
await $`docker build --progress=plain --pull -t $AWS_ECR_URL/$AWS_ECR_IMAGE:latest .`; |
|
console.groupEnd(); |
|
|
|
console.group('Getting ECR auth'); |
|
await $`aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ECR_URL`; |
|
console.groupEnd(); |
|
|
|
console.group('Pushing build'); |
|
const dockerPushOutput = await $`docker push $AWS_ECR_URL/$AWS_ECR_IMAGE:latest`; |
|
const buildShaMatch = dockerPushOutput.text().match(/latest: digest: (sha256:[a-f0-9]{64})/); |
|
if (!buildShaMatch || buildShaMatch.length < 2) { |
|
console.error('Failed to extract digest from push output:', dockerPushOutput.stdout); |
|
process.exit(1); |
|
} |
|
const buildSha = buildShaMatch[1]!; |
|
console.groupEnd(); |
|
|
|
//Awaiting the image to be available |
|
console.group('Awaiting image to be available on ECR'); |
|
console.log('Checking for image with digest:', buildSha); |
|
let checkPushAttempts = 0; |
|
while (true) { |
|
const awsDesc = await $`aws ecr describe-images --repository-name $AWS_ECR_IMAGE --image-ids imageTag=latest`.quiet(); |
|
const output = awsDesc.text(); |
|
if (output.includes(buildSha)) { |
|
console.log('Image is available!'); |
|
break; |
|
} |
|
console.log('Image not available yet, waiting...'); |
|
|
|
// Wait before next check |
|
checkPushAttempts++; |
|
if (checkPushAttempts >= attemptLimit) { |
|
console.error('Push check timed out after', attemptLimit * attemptDelay / 1000, 'seconds'); |
|
process.exit(1); |
|
} |
|
await sleep(attemptDelay); |
|
}; |
|
console.groupEnd(); |
|
|
|
|
|
//Helper function to fetch Coolify API |
|
const fetchCoolifyApi = (path: string, body?: any) => { |
|
const reqUrl = new URL(path, `https://${env.COOLIFY_API_DOMAIN}`); |
|
const options: RequestInit = { |
|
signal: AbortSignal.timeout(2500), |
|
method: body ? 'POST' : 'GET', |
|
headers: { |
|
'Content-Type': 'application/json', |
|
Authorization: `Bearer ${env.COOLIFY_API_TOKEN}`, |
|
}, |
|
}; |
|
if (body) { |
|
options.body = JSON.stringify(body); |
|
} |
|
return fetch(reqUrl.href, options); |
|
} |
|
|
|
|
|
//Trigger Coolify deploy |
|
console.group('Triggering Coolify deploy'); |
|
const deployResp = await fetchCoolifyApi(`/api/v1/deploy?uuid=${env.COOLIFY_RESOURCE_ID}&force=false`); |
|
console.log('Coolify deploy response:', deployResp.status, deployResp.statusText); |
|
const respData = await deployResp.json() as any; |
|
if (!Array.isArray(respData.deployments) || respData.deployments.length === 0) { |
|
console.error('No deployments queed:', respData); |
|
process.exit(1); |
|
} |
|
const deploymentUuid = respData.deployments[0].deployment_uuid; |
|
if (!deploymentUuid) { |
|
console.error('No deployment UUID found:', respData); |
|
process.exit(1); |
|
} |
|
console.groupEnd(); |
|
|
|
|
|
// Await Deployment |
|
let checkDeploymentAttempts = 0; |
|
console.group('Awaiting Coolify deployment:'); |
|
while (true) { |
|
const elapsedSecs = Math.floor(checkDeploymentAttempts * attemptDelay / 1000).toString().padStart(2, ' '); |
|
const msgTag = `[${elapsedSecs}s]`; |
|
|
|
const statusResp = await fetchCoolifyApi(`/api/v1/deployments/${deploymentUuid}`); |
|
const statusData = await statusResp.json() as any; |
|
if (!statusData || !statusData.status) { |
|
console.error(msgTag, 'Failed to fetch:'); |
|
console.log(statusData); |
|
process.exit(1); |
|
} |
|
|
|
console.log(msgTag, 'Status:', statusData.status); |
|
if (statusData.status === 'finished') { |
|
break; |
|
} else if (statusData.status === 'failed') { |
|
console.error('Deployment failed:'); |
|
console.log(statusData); |
|
process.exit(1); |
|
} |
|
|
|
// Wait before next check |
|
checkDeploymentAttempts++; |
|
if (checkDeploymentAttempts >= attemptLimit) { |
|
console.error('Deployment check timed out after', attemptLimit * attemptDelay / 1000, 'seconds'); |
|
process.exit(1); |
|
} |
|
await sleep(attemptDelay); |
|
}; |
|
console.groupEnd(); |
|
|
|
console.log(chalk.bgGreenBright.black('== Deployment succeeded! ==')); |
|
process.exit(0); |