|  | 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); |