@@ -3,8 +3,11 @@ package repository
3
3
import (
4
4
"context"
5
5
"fmt"
6
+ "maps"
6
7
"os"
7
8
"path/filepath"
9
+ "slices"
10
+ "strings"
8
11
"time"
9
12
10
13
"github.com/azure/azure-dev/cli/azd/internal"
@@ -39,6 +42,8 @@ var dbMap = map[appdetect.DatabaseDep]struct{}{
39
42
appdetect .DbRedis : {},
40
43
}
41
44
45
+ var featureCompose = alpha .MustFeatureKey ("compose" )
46
+
42
47
// InitFromApp initializes the infra directory and project file from the current existing app.
43
48
func (i * Initializer ) InitFromApp (
44
49
ctx context.Context ,
@@ -244,30 +249,31 @@ func (i *Initializer) InitFromApp(
244
249
245
250
// Create the infra spec
246
251
var infraSpec * scaffold.InfraSpec
247
- if ! i .features .IsEnabled (alpha .Compose ) { // backwards compatibility
252
+ composeEnabled := i .features .IsEnabled (featureCompose )
253
+ if ! composeEnabled { // backwards compatibility
248
254
spec , err := i .infraSpecFromDetect (ctx , detect )
249
255
if err != nil {
250
256
return err
251
257
}
252
258
infraSpec = & spec
253
- }
254
259
255
- // Prompt for environment before proceeding with generation
256
- _ , err = initializeEnv ()
257
- if err != nil {
258
- return err
260
+ // Prompt for environment before proceeding with generation
261
+ _ , err = initializeEnv ()
262
+ if err != nil {
263
+ return err
264
+ }
259
265
}
260
266
261
267
tracing .SetUsageAttributes (fields .AppInitLastStep .String ("generate" ))
262
268
263
- i .console .Message (ctx , "\n " + output .WithBold ("Generating files to run your app on Azure:" )+ "\n " )
264
269
title = "Generating " + output .WithHighLightFormat ("./" + azdcontext .ProjectFileName )
265
270
i .console .ShowSpinner (ctx , title , input .Step )
266
- err = i .genProjectFile (ctx , azdCtx , detect )
271
+ err = i .genProjectFile (ctx , azdCtx , detect , composeEnabled )
267
272
if err != nil {
268
273
i .console .StopSpinner (ctx , title , input .GetStepResultFormat (err ))
269
274
return err
270
275
}
276
+ i .console .Message (ctx , "\n " + output .WithBold ("Generating files to run your app on Azure:" )+ "\n " )
271
277
i .console .StopSpinner (ctx , title , input .StepDone )
272
278
273
279
if infraSpec != nil {
@@ -279,6 +285,20 @@ func (i *Initializer) InitFromApp(
279
285
return err
280
286
}
281
287
i .console .StopSpinner (ctx , title , input .StepDone )
288
+ } else {
289
+ t , err := scaffold .Load ()
290
+ if err != nil {
291
+ return fmt .Errorf ("loading scaffold templates: %w" , err )
292
+ }
293
+
294
+ err = scaffold .Execute (t , "next-steps-alpha.md" , nil , filepath .Join (azdCtx .ProjectDirectory (), "next-steps.md" ))
295
+ if err != nil {
296
+ return err
297
+ }
298
+
299
+ i .console .MessageUxItem (ctx , & ux.DoneMessage {
300
+ Message : "Generating " + output .WithHighLightFormat ("./next-steps.md" ),
301
+ })
282
302
}
283
303
284
304
return nil
@@ -341,8 +361,9 @@ func (i *Initializer) genFromInfra(
341
361
func (i * Initializer ) genProjectFile (
342
362
ctx context.Context ,
343
363
azdCtx * azdcontext.AzdContext ,
344
- detect detectConfirm ) error {
345
- config , err := prjConfigFromDetect (azdCtx .ProjectDirectory (), detect )
364
+ detect detectConfirm ,
365
+ addResources bool ) error {
366
+ config , err := i .prjConfigFromDetect (ctx , azdCtx .ProjectDirectory (), detect , addResources )
346
367
if err != nil {
347
368
return fmt .Errorf ("converting config: %w" , err )
348
369
}
@@ -359,16 +380,20 @@ func (i *Initializer) genProjectFile(
359
380
360
381
const InitGenTemplateId = "azd-init"
361
382
362
- func prjConfigFromDetect (
383
+ func (i * Initializer ) prjConfigFromDetect (
384
+ ctx context.Context ,
363
385
root string ,
364
- detect detectConfirm ) (project.ProjectConfig , error ) {
386
+ detect detectConfirm ,
387
+ addResources bool ) (project.ProjectConfig , error ) {
365
388
config := project.ProjectConfig {
366
389
Name : azdcontext .ProjectName (root ),
367
390
Metadata : & project.ProjectMetadata {
368
391
Template : fmt .Sprintf ("%s@%s" , InitGenTemplateId , internal .VersionInfo ().Version ),
369
392
},
370
393
Services : map [string ]* project.ServiceConfig {},
371
394
}
395
+
396
+ svcMapping := map [string ]string {}
372
397
for _ , prj := range detect .Services {
373
398
rel , err := filepath .Rel (root , prj .Path )
374
399
if err != nil {
@@ -429,7 +454,108 @@ func prjConfigFromDetect(
429
454
name = config .Name
430
455
}
431
456
name = names .LabelName (name )
457
+ svc .Name = name
432
458
config .Services [name ] = & svc
459
+
460
+ svcMapping [prj .Path ] = name
461
+ }
462
+
463
+ if addResources {
464
+ config .Resources = map [string ]* project.ResourceConfig {}
465
+ dbNames := map [appdetect.DatabaseDep ]string {}
466
+
467
+ databases := slices .SortedFunc (maps .Keys (detect .Databases ),
468
+ func (a appdetect.DatabaseDep , b appdetect.DatabaseDep ) int {
469
+ return strings .Compare (string (a ), string (b ))
470
+ })
471
+
472
+ for _ , database := range databases {
473
+ if database == appdetect .DbRedis {
474
+ redis := project.ResourceConfig {
475
+ Type : project .ResourceTypeDbRedis ,
476
+ Name : "redis" ,
477
+ }
478
+ config .Resources [redis .Name ] = & redis
479
+ dbNames [database ] = redis .Name
480
+ continue
481
+ }
482
+
483
+ var dbType project.ResourceType
484
+ switch database {
485
+ case appdetect .DbMongo :
486
+ dbType = project .ResourceTypeDbMongo
487
+ case appdetect .DbPostgres :
488
+ dbType = project .ResourceTypeDbPostgres
489
+ }
490
+
491
+ db := project.ResourceConfig {
492
+ Type : dbType ,
493
+ }
494
+
495
+ for {
496
+ dbName , err := promptDbName (i .console , ctx , database )
497
+ if err != nil {
498
+ return config , err
499
+ }
500
+
501
+ if dbName == "" {
502
+ i .console .Message (ctx , "Database name is required." )
503
+ continue
504
+ }
505
+
506
+ db .Name = dbName
507
+ break
508
+ }
509
+
510
+ config .Resources [db .Name ] = & db
511
+ dbNames [database ] = db .Name
512
+ }
513
+
514
+ backends := []* project.ResourceConfig {}
515
+ frontends := []* project.ResourceConfig {}
516
+
517
+ for _ , svc := range detect .Services {
518
+ name := svcMapping [svc .Path ]
519
+ resSpec := project.ResourceConfig {
520
+ Type : project .ResourceTypeHostContainerApp ,
521
+ }
522
+
523
+ props := project.ContainerAppProps {
524
+ Port : - 1 ,
525
+ }
526
+
527
+ port , err := promptPort (i .console , ctx , name , svc )
528
+ if err != nil {
529
+ return config , err
530
+ }
531
+ props .Port = port
532
+
533
+ for _ , db := range svc .DatabaseDeps {
534
+ // filter out databases that were removed
535
+ if _ , ok := detect .Databases [db ]; ! ok {
536
+ continue
537
+ }
538
+
539
+ resSpec .Uses = append (resSpec .Uses , dbNames [db ])
540
+ }
541
+
542
+ resSpec .Name = name
543
+ resSpec .Props = props
544
+ config .Resources [name ] = & resSpec
545
+
546
+ frontend := svc .HasWebUIFramework ()
547
+ if frontend {
548
+ frontends = append (frontends , & resSpec )
549
+ } else {
550
+ backends = append (backends , & resSpec )
551
+ }
552
+ }
553
+
554
+ for _ , frontend := range frontends {
555
+ for _ , backend := range backends {
556
+ frontend .Uses = append (frontend .Uses , backend .Name )
557
+ }
558
+ }
433
559
}
434
560
435
561
return config , nil
0 commit comments