1
1
package command
2
2
3
3
import (
4
+ "archive/tar"
5
+ "compress/gzip"
4
6
"context"
7
+ "encoding/json"
5
8
"errors"
6
9
"fmt"
7
10
"io"
8
11
"math/rand"
12
+ "net/http"
9
13
"os"
10
14
"os/exec"
11
15
"regexp"
@@ -424,16 +428,66 @@ var generateCmd = &cobra.Command{
424
428
return errors .New ("cannot run in non-interactive mode" )
425
429
}
426
430
431
+ var language string
432
+ if err := survey .AskOne (& survey.Select {
433
+ Message : "Choose the language you'd like to use:" ,
434
+ Options : []string {"Nodejs" , "Golang" , "Python" },
435
+ Default : "Nodejs" ,
436
+ Help : "The generated code will be in the language you choose here." ,
437
+ }, & language ); err != nil {
438
+ return err
439
+ }
440
+
441
+ var samples []struct {
442
+ Name string `json:"name"`
443
+ Category string `json:"category"`
444
+ Readme string `json:"readme"`
445
+ }
446
+ req , err := http .NewRequestWithContext (cmd .Context (), http .MethodGet , "https://docs.defang.io/samples.json" , nil )
447
+ if err != nil {
448
+ return err
449
+ }
450
+ req .Header .Set ("Accept-Encoding" , "gzip" )
451
+ resp , err := httpClient .Do (req )
452
+ if err == nil {
453
+ defer resp .Body .Close ()
454
+ term .Debug (resp .Header )
455
+ // Check that the server actually sent compressed data
456
+ reader := resp .Body
457
+ if resp .Header .Get ("Content-Encoding" ) == "gzip" {
458
+ reader , err = gzip .NewReader (resp .Body )
459
+ if err != nil {
460
+ return err
461
+ }
462
+ defer reader .Close ()
463
+ }
464
+ if json .NewDecoder (reader ).Decode (& samples ) != nil {
465
+ term .Debug (" - unable to decode samples.json:" , err )
466
+ }
467
+ }
468
+
469
+ const generateWithAI = "Generate with AI"
470
+
471
+ language = strings .ToLower (language ) // make it match the category strings
472
+ sampleNames := []string {generateWithAI }
473
+ // sampleDescriptions := []string{"Generate a sample from scratch using a language prompt"}
474
+ for _ , sample := range samples {
475
+ if sample .Category == language {
476
+ sampleNames = append (sampleNames , sample .Name )
477
+ // sampleDescriptions = append(sampleDescriptions, sample.Readme)
478
+ }
479
+ }
480
+
481
+ var sample string
482
+ if err := survey .AskOne (& survey.Select {
483
+ Message : "Choose a sample service:" ,
484
+ Options : sampleNames ,
485
+ Help : "The generated code will be based on the sample you choose here." ,
486
+ }, & sample ); err != nil {
487
+ return err
488
+ }
489
+
427
490
var qs = []* survey.Question {
428
- {
429
- Name : "language" ,
430
- Prompt : & survey.Select {
431
- Message : "Choose the language you'd like to use:" ,
432
- Options : []string {"Nodejs" , "Golang" , "Python" },
433
- Default : "Nodejs" ,
434
- Help : "The generated code will be in the language you choose here." ,
435
- },
436
- },
437
491
{
438
492
Name : "description" ,
439
493
Prompt : & survey.Input {
@@ -457,14 +511,17 @@ Generate will write files in the current folder. You can edit them and then depl
457
511
},
458
512
}
459
513
514
+ if sample != generateWithAI {
515
+ qs = qs [1 :] // skip the description question
516
+ }
517
+
460
518
prompt := struct {
461
- Language string // or you can tag fields to match a specific name
462
- Description string
519
+ Description string // or you can tag fields to match a specific name
463
520
Folder string
464
521
}{}
465
522
466
- // ask the questions
467
- err : = survey .Ask (qs , & prompt )
523
+ // ask the remaining questions
524
+ err = survey .Ask (qs , & prompt )
468
525
if err != nil {
469
526
return err
470
527
}
@@ -479,7 +536,7 @@ Generate will write files in the current folder. You can edit them and then depl
479
536
}
480
537
}
481
538
482
- Track ("Generate Started" , P {"language" , prompt . Language }, P {"description" , prompt .Description }, P {"folder" , prompt .Folder })
539
+ Track ("Generate Started" , P {"language" , language }, P { "sample" , sample }, P {"description" , prompt .Description }, P {"folder" , prompt .Folder })
483
540
484
541
// create the folder if needed
485
542
cd := ""
@@ -496,10 +553,51 @@ Generate will write files in the current folder. You can edit them and then depl
496
553
term .Warn (" ! The folder is not empty. Files may be overwritten. Press Ctrl+C to abort." )
497
554
}
498
555
499
- term .Info (" * Working on it. This may take 1 or 2 minutes..." )
500
- _ , err = cli .Generate (cmd .Context (), client , prompt .Language , prompt .Description )
501
- if err != nil {
502
- return err
556
+ if prompt .Description != "" {
557
+ term .Info (" * Working on it. This may take 1 or 2 minutes..." )
558
+ _ , err = cli .Generate (cmd .Context (), client , language , prompt .Description )
559
+ if err != nil {
560
+ return err
561
+ }
562
+ } else {
563
+ term .Info (" * Fetching sample from the Defang repository..." )
564
+ resp , err := http .Get ("https://github.com/defang-io/defang/archive/refs/heads/main.tar.gz" )
565
+ if err != nil {
566
+ return err
567
+ }
568
+ defer resp .Body .Close ()
569
+ term .Debug (resp .Header )
570
+ body , err := gzip .NewReader (resp .Body )
571
+ if err != nil {
572
+ return err
573
+ }
574
+ defer body .Close ()
575
+ tarReader := tar .NewReader (body )
576
+ term .Info (" * Writing files to disk..." )
577
+ for {
578
+ h , err := tarReader .Next ()
579
+ if err != nil {
580
+ if err == io .EOF {
581
+ break
582
+ }
583
+ return err
584
+ }
585
+
586
+ if base , ok := strings .CutPrefix (h .Name , "defang-main/samples/" + language + "/" + sample + "/" ); ok && len (base ) > 0 {
587
+ fmt .Println (" -" , base )
588
+ if h .FileInfo ().IsDir () {
589
+ os .MkdirAll (base , 0755 )
590
+ continue
591
+ }
592
+ f , err := os .Create (base )
593
+ if err != nil {
594
+ return err
595
+ }
596
+ if _ , err := io .Copy (f , tarReader ); err != nil {
597
+ return err
598
+ }
599
+ }
600
+ }
503
601
}
504
602
505
603
term .Info (" * Code generated successfully in folder" , prompt .Folder )
0 commit comments