@@ -6,7 +6,7 @@ import { cosmiconfig } from 'cosmiconfig'
66import createDebug from 'debug'
77// import codeGenieSampleOpenAiOutputJson from '../sample-api-output.js'
88import { App , convertOpenAiOutputToCodeGenieInput , getAppOutputDir } from '../app-definition-generator.js'
9- import copyAwsProfile from '../copyAwsProfile.js'
9+ import copyAwsProfile , { awsCredentialsFileExists } from '../copyAwsProfile.js'
1010import sleep from '../sleep.js'
1111import { execSync } from 'node:child_process'
1212import { Readable } from 'node:stream'
@@ -86,7 +86,7 @@ generating app...
8686 } ) ,
8787 }
8888
89- async run ( ) : Promise < void | { description ?: string ; deploy : boolean ; awsProfileToCopy : string } > {
89+ async run ( ) : Promise < { description ?: string ; deploy : boolean ; awsProfileToCopy : string ; appDir : string } > {
9090 const { flags } = await this . parse ( Generate )
9191 const { description, deploy, awsProfileToCopy, noCopyAwsProfile } = flags
9292
@@ -97,37 +97,62 @@ generating app...
9797 } )
9898 }
9999
100- // If a description is provided we generate an App Definition .codegenie directory based on it
101- let appDir : string | undefined
100+ let appDir = cwd ( )
101+
102+ // If a description is provided we create a new directory using the name provided (--name)
103+ // or the AI-generated name, and generate an App Definition (.codegenie directory) within it based on the description
102104 if ( description ) {
103- await this . handleExistingAppDefinition ( )
105+ // First check if we're within an existing Code Genie project directory by checking if a .codegnie directory already exists.
106+ // handleExistingAppDefinition MUST be run before generateAppDefinition, since generateAppDefinition creates a .codegenie directory.
107+ const hasExistingAppDefinition = await this . handleExistingAppDefinition ( )
104108 const generateAppDefinitionResult = await this . generateAppDefinition ( )
105- if ( generateAppDefinitionResult ) {
106- const { appName } = generateAppDefinitionResult
107- appDir = getAppOutputDir ( { appName } )
109+
110+ // Usually we expect that `generate --description` is run NOT within an existing Code Genie project directory; therefore
111+ // hasExistingAppDefinition will usually be false. If `generate --description` is run within an existing Code Genie project directory,
112+ // handleExistingAppDefinition will throw unless --replaceAppDefinitionf is included, in which case we want `appDir` to remain as cwd().
113+ if ( ! hasExistingAppDefinition ) {
114+ appDir = getAppOutputDir ( { appName : generateAppDefinitionResult . appName } )
108115 }
109- return
110116 }
111117
112118 const { headObjectPresignedUrl, getObjectPresignedUrl } = await this . uploadAppDefinition ( { appDir } )
113- await this . downloadS3OutputObject ( {
119+
120+ await this . downloadProject ( {
114121 headObjectPresignedUrl,
115122 getObjectPresignedUrl,
123+ appDir,
116124 } )
117125
126+ const appName = await this . getAppName ( { appDir } )
127+ if ( deploy && ! awsCredentialsFileExists ( ) ) {
128+ const appDirRelative = getAppOutputDir ( { appName, absolute : false } )
129+ this . log ( `The project has successfully been generated and downloaded to \`./${ appDirRelative } \`.
130+
131+ The project wasn't able to be automatically built and deployed to AWS because the AWS CLI isn\'t set up on this machine. Install the AWS CLI and then run \`npm run init:dev\` inside the \`./${ appDirRelative } \` directory.
132+
133+ For now you can open \`./${ appDirRelative } \` in your favorite IDE like VS Code. Tip: You may even be able to simply run \`code ./${ appDirRelative } \` to open it now.` )
134+
135+ return {
136+ description,
137+ deploy,
138+ awsProfileToCopy,
139+ appDir,
140+ }
141+ }
142+
118143 if ( ! noCopyAwsProfile ) {
119- const appName = await this . getAppName ( { appDir } )
120144 copyAwsProfile ( { appName } )
121145 }
122146
123147 if ( deploy ) {
124- await this . runInitDev ( )
148+ await this . runInitDev ( { appDir } )
125149 }
126150
127151 return {
128152 description,
129153 deploy,
130154 awsProfileToCopy,
155+ appDir,
131156 }
132157 }
133158
@@ -155,13 +180,13 @@ generating app...
155180 const { flags } = await this . parse ( Generate )
156181 const { replaceAppDefinition } = flags
157182
158- if ( ! existsSync ( '.codegenie' ) ) return
183+ if ( ! existsSync ( '.codegenie' ) ) return false
159184
160185 if ( replaceAppDefinition ) {
161186 ux . action . start ( 'Deleting .codegenie' )
162187 rmSync ( '.codegenie' , { recursive : true , force : true } )
163188 ux . action . stop ( '✅' )
164- return
189+ return true
165190 }
166191
167192 this . error ( 'A .codegenie directory already exists.' , {
@@ -176,7 +201,7 @@ generating app...
176201 /**
177202 * Generates a [.codegenie app definition](https://codegenie.codes/docs) based on the provided description
178203 */
179- async generateAppDefinition ( ) : Promise < void | { app : App ; appName : string ; appDescription : string } > {
204+ async generateAppDefinition ( ) : Promise < { app : App ; appName : string ; appDescription : string } > {
180205 const { flags } = await this . parse ( Generate )
181206 const { description, name } = flags
182207 ux . action . start ( '🧞 Generating App Definition' )
@@ -223,12 +248,12 @@ generating app...
223248 * @param root0
224249 * @param root0.appDir
225250 */
226- async uploadAppDefinition ( { appDir } : { appDir ? : string } = { } ) {
251+ async uploadAppDefinition ( { appDir } : { appDir : string } ) {
227252 ux . action . start ( '⬆️📦 Uploading App Definition' )
228253 const appName = await this . getAppName ( { appDir } )
229254 const output = await axios . get ( `/build-upload-presigned-url?appName=${ appName } ` )
230255 const { putObjectPresignedUrl, headObjectPresignedUrl, getObjectPresignedUrl } = output . data . data
231- const appDefinitionZip = await this . createZip ( path . resolve ( appDir || cwd ( ) , '.codegenie' ) )
256+ const appDefinitionZip = await this . createZip ( path . resolve ( appDir , '.codegenie' ) )
232257 const zipFileSizeBytes = Buffer . byteLength ( appDefinitionZip )
233258 const readable = getReadableFromBuffer ( appDefinitionZip )
234259
@@ -305,12 +330,14 @@ generating app...
305330 }
306331 }
307332
308- async downloadS3OutputObject ( {
333+ async downloadProject ( {
309334 headObjectPresignedUrl,
310335 getObjectPresignedUrl,
336+ appDir,
311337 } : {
312338 headObjectPresignedUrl : string
313339 getObjectPresignedUrl : string
340+ appDir : string
314341 } ) : Promise < undefined > {
315342 ux . action . start ( '🏗️ Generating project' )
316343 await this . pollS3ObjectExistence ( { headObjectPresignedUrl } )
@@ -319,14 +346,15 @@ generating app...
319346 const response = await axios . get ( getObjectPresignedUrl , { responseType : 'arraybuffer' } )
320347 const zip = new AdmZip ( response . data )
321348 const overwrite = false
322- zip . extractAllTo ( '.' , overwrite )
349+ zip . extractAllTo ( appDir , overwrite )
323350 ux . action . stop ( '✅' )
324351 }
325352
326- async runInitDev ( ) : Promise < undefined > {
353+ async runInitDev ( { appDir } : { appDir : string } ) : Promise < undefined > {
327354 ux . action . start ( '🌩️ Deploying to AWS. The first deploy may take up to 10 minutes' )
328355 execSync ( 'npm run init:dev' , {
329356 stdio : 'inherit' ,
357+ cwd : appDir ,
330358 } )
331359 ux . action . stop ( '✅' )
332360 }
0 commit comments