@@ -2,7 +2,12 @@ import { existsSync } from 'fs';
22import { join } from 'path' ;
33import { cwd } from 'process' ;
44
5- import { SpawnOptions , spawn } from '@codeware/core/utils' ;
5+ import {
6+ SpawnOptions ,
7+ SpawnPtyOptions ,
8+ spawn ,
9+ spawnPty
10+ } from '@codeware/core/utils' ;
611import { ZodError } from 'zod' ;
712
813import { AppsCreateTransformedResponseSchema } from './schemas/apps-create.schema' ;
@@ -42,6 +47,8 @@ import type {
4247 UnsetAppSecretsOptions
4348} from './types' ;
4449
50+ export type ExecFlyOptions = SpawnOptions & SpawnPtyOptions ;
51+
4552/**
4653 * Manages deployments to Fly.io using the `flyctl` CLI tool.
4754 */
@@ -171,7 +178,8 @@ export class Fly {
171178 this . logger . info (
172179 `Detaching Postgres cluster '${ cluster } ' from application '${ app } '`
173180 ) ;
174- await this . detachPostgres ( cluster , app ) ;
181+ const output = await this . detachPostgres ( cluster , app ) ;
182+ this . logger . info ( output ) ;
175183 }
176184 // Now it's safe to destroy the app
177185 await this . destroyApp ( app , options ?. force ) ;
@@ -331,6 +339,11 @@ export class Fly {
331339 ) ;
332340 }
333341 } ;
342+ postgres = {
343+ detach : async ( cluster : string , app : string ) => {
344+ await this . detachPostgres ( cluster , app ) ;
345+ }
346+ } ;
334347 /**
335348 * Manage application secrets
336349 */
@@ -667,13 +680,14 @@ export class Fly {
667680
668681 /**
669682 * @private
683+ * @returns Log output from the detach process
670684 * @throws An error if the Postgres database cannot be detached from an app
671685 */
672- private async detachPostgres ( postgres : string , app : string ) : Promise < void > {
686+ private async detachPostgres ( postgres : string , app : string ) : Promise < string > {
673687 const args = [ 'postgres' , 'detach' , postgres , '--app' , app ] ;
674688
675689 // Prompt for confirmation if the database name
676- await this . execFly ( args , {
690+ return await this . execFly < string > ( args , {
677691 prompt : ( output ) => {
678692 if ( output . match ( / s e l e c t .* d e t a c h / i) ) {
679693 return app . replace ( / - / g, '_' ) ;
@@ -1136,9 +1150,9 @@ ${JSON.stringify(response, null, 2)}`);
11361150 * @returns The JSON output of the command, or the raw output if not JSON
11371151 * @throws An error if the command fails
11381152 */
1139- private async execFly < T = void > (
1153+ private async execFly < T > (
11401154 command : string | Array < string > ,
1141- options ?: SpawnOptions
1155+ options ?: ExecFlyOptions
11421156 ) : Promise < T > ;
11431157 /**
11441158 * @private
@@ -1152,15 +1166,15 @@ ${JSON.stringify(response, null, 2)}`);
11521166 * @returns The JSON output of the command, or the raw output if not JSON
11531167 * @throws An error if the command fails and `onError` is `throwOnError`
11541168 */
1155- private async execFly < T = null > (
1169+ private async execFly < T > (
11561170 command : string | Array < string > ,
1157- options ?: SpawnOptions ,
1171+ options ?: ExecFlyOptions ,
11581172 onError ?: 'nullOnError' ,
11591173 skipToken ?: boolean
11601174 ) : Promise < T > ;
11611175 private async execFly < T > (
11621176 command : string | Array < string > ,
1163- options ?: SpawnOptions ,
1177+ options ?: ExecFlyOptions ,
11641178 onError : 'nullOnError' | 'throwOnError' = 'throwOnError' ,
11651179 skipToken ?: boolean
11661180 ) : Promise < T > {
@@ -1176,14 +1190,21 @@ ${JSON.stringify(response, null, 2)}`);
11761190
11771191 const flyCmd = 'flyctl' ;
11781192 const toLogCmd = `${ flyCmd } ${ args . join ( ' ' ) } ` ;
1179- let stdout = '' ;
1193+ let output = '' ;
11801194
11811195 try {
11821196 this . traceCLI ( 'CALL' , toLogCmd ) ;
1183- const result = await spawn ( flyCmd , args , options ) ;
1184- stdout = result . stdout . trim ( ) ;
1185- const stderr = result . stderr . trim ( ) ;
1186- this . traceCLI ( 'RESULT' , `${ stdout } ${ stderr ? `\n${ stderr } ` : '' } ` ) ;
1197+ // If the command requires interactive input, use spawnPty
1198+ if ( options ?. prompt ) {
1199+ output = await spawnPty ( flyCmd , args , { prompt : options . prompt } ) ;
1200+ this . traceCLI ( 'RESULT' , output ) ;
1201+ } else {
1202+ // Otherwise use spawn
1203+ const result = await spawn ( flyCmd , args , options ) ;
1204+ output = result . stdout . trim ( ) ;
1205+ const stderr = result . stderr . trim ( ) ;
1206+ this . traceCLI ( 'RESULT' , `${ output } ${ stderr ? `\n${ stderr } ` : '' } ` ) ;
1207+ }
11871208 } catch ( error ) {
11881209 const errorMsg = ( error as Error ) . message ;
11891210 this . traceCLI ( 'RESULT' , errorMsg ) ;
@@ -1195,13 +1216,13 @@ ${errorMsg}`);
11951216 }
11961217
11971218 try {
1198- return JSON . parse ( stdout ) as T ;
1219+ return JSON . parse ( output ) as T ;
11991220 } catch {
1200- if ( stdout ) {
1221+ if ( output ) {
12011222 if ( this . logger . traceCLI ) {
12021223 this . logger . info ( `Failed to parse as JSON, returning raw output` ) ;
12031224 }
1204- return stdout as T ;
1225+ return output as T ;
12051226 }
12061227 }
12071228 return undefined as T ;
0 commit comments