Skip to content

Commit 0ea5e64

Browse files
committed
cleanup
1 parent 5d804d4 commit 0ea5e64

File tree

4 files changed

+136
-89
lines changed

4 files changed

+136
-89
lines changed

src/cmd/cli/command/commands.go

Lines changed: 30 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
11
package command
22

33
import (
4-
"archive/tar"
5-
"compress/gzip"
64
"context"
7-
"encoding/json"
85
"errors"
96
"fmt"
107
"io"
118
"math/rand"
12-
"net/http"
139
"os"
1410
"os/exec"
1511
"regexp"
@@ -433,58 +429,39 @@ var generateCmd = &cobra.Command{
433429
Message: "Choose the language you'd like to use:",
434430
Options: []string{"Nodejs", "Golang", "Python"},
435431
Default: "Nodejs",
436-
Help: "The generated code will be in the language you choose here.",
432+
Help: "The project code will be in the language you choose here.",
437433
}, &language); err != nil {
438434
return err
439435
}
440436

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-
}
437+
var category, sample string
468438

469-
const generateWithAI = "Generate with AI"
439+
// Fetch the list of samples from the Defang repository
440+
if samples, err := cli.FetchSamples(cmd.Context()); err != nil {
441+
term.Debug(" - unable to fetch samples:", err)
442+
} else if len(samples) > 0 {
443+
const generateWithAI = "Generate with AI"
470444

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)
445+
category = strings.ToLower(language)
446+
sampleNames := []string{generateWithAI}
447+
// sampleDescriptions := []string{"Generate a sample from scratch using a language prompt"}
448+
for _, sample := range samples {
449+
if sample.Category == category {
450+
sampleNames = append(sampleNames, sample.Name)
451+
// sampleDescriptions = append(sampleDescriptions, sample.Readme)
452+
}
478453
}
479-
}
480454

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
455+
if err := survey.AskOne(&survey.Select{
456+
Message: "Choose a sample service:",
457+
Options: sampleNames,
458+
Help: "The project code will be based on the sample you choose here.",
459+
}, &sample); err != nil {
460+
return err
461+
}
462+
if sample == generateWithAI {
463+
sample = ""
464+
}
488465
}
489466

490467
var qs = []*survey.Question{
@@ -511,8 +488,8 @@ Generate will write files in the current folder. You can edit them and then depl
511488
},
512489
}
513490

514-
if sample != generateWithAI {
515-
qs = qs[1:] // skip the description question
491+
if sample != "" {
492+
qs = qs[1:] // user picked a sample, so we skip the description question
516493
}
517494

518495
prompt := struct {
@@ -521,7 +498,7 @@ Generate will write files in the current folder. You can edit them and then depl
521498
}{}
522499

523500
// ask the remaining questions
524-
err = survey.Ask(qs, &prompt)
501+
err := survey.Ask(qs, &prompt)
525502
if err != nil {
526503
return err
527504
}
@@ -555,49 +532,16 @@ Generate will write files in the current folder. You can edit them and then depl
555532

556533
if prompt.Description != "" {
557534
term.Info(" * Working on it. This may take 1 or 2 minutes...")
558-
_, err = cli.Generate(cmd.Context(), client, language, prompt.Description)
535+
_, err := cli.GenerateWithAI(cmd.Context(), client, language, prompt.Description)
559536
if err != nil {
560537
return err
561538
}
562539
} else {
563540
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)
541+
err := cli.InitFromSample(cmd.Context(), category, sample)
571542
if err != nil {
572543
return err
573544
}
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-
}
601545
}
602546

603547
term.Info(" * Code generated successfully in folder", prompt.Folder)

src/pkg/cli/generate.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
defangv1 "github.com/defang-io/defang/src/protos/io/defang/v1"
1111
)
1212

13-
func Generate(ctx context.Context, client client.Client, language string, description string) ([]string, error) {
13+
func GenerateWithAI(ctx context.Context, client client.Client, language string, description string) ([]string, error) {
1414
if DoDryRun {
1515
term.Warn(" ! Dry run, not generating files")
1616
return nil, ErrDryRun

src/pkg/cli/init.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package cli
2+
3+
import (
4+
"archive/tar"
5+
"compress/gzip"
6+
"context"
7+
"encoding/json"
8+
"fmt"
9+
"io"
10+
"os"
11+
"strings"
12+
13+
"github.com/defang-io/defang/src/pkg/http"
14+
"github.com/defang-io/defang/src/pkg/term"
15+
)
16+
17+
type Sample struct {
18+
Name string `json:"name"`
19+
Category string `json:"category"` // language
20+
Readme string `json:"readme"`
21+
}
22+
23+
func FetchSamples(ctx context.Context) ([]Sample, error) {
24+
resp, err := http.GetWithHeader(ctx, "https://docs.defang.io/samples.json", http.Header{"Accept-Encoding": []string{"gzip"}})
25+
if err != nil {
26+
return nil, err
27+
}
28+
defer resp.Body.Close()
29+
term.Debug(resp.Header)
30+
reader := resp.Body
31+
if resp.Header.Get("Content-Encoding") == "gzip" {
32+
reader, err = gzip.NewReader(resp.Body)
33+
if err != nil {
34+
return nil, err
35+
}
36+
defer reader.Close()
37+
}
38+
var samples []Sample
39+
err = json.NewDecoder(reader).Decode(&samples)
40+
return samples, err
41+
}
42+
43+
func InitFromSample(ctx context.Context, category, sample string) error {
44+
const repo = "defang"
45+
const branch = "main"
46+
47+
prefix := fmt.Sprintf("%s-%s/samples/%s/%s/", repo, branch, category, sample)
48+
resp, err := http.GetWithContext(ctx, "https://github.com/defang-io/"+repo+"/archive/refs/heads/"+branch+".tar.gz")
49+
if err != nil {
50+
return err
51+
}
52+
defer resp.Body.Close()
53+
term.Debug(resp.Header)
54+
body, err := gzip.NewReader(resp.Body)
55+
if err != nil {
56+
return err
57+
}
58+
defer body.Close()
59+
tarReader := tar.NewReader(body)
60+
term.Info(" * Writing files to disk...")
61+
for {
62+
h, err := tarReader.Next()
63+
if err != nil {
64+
if err == io.EOF {
65+
break
66+
}
67+
return err
68+
}
69+
70+
if base, ok := strings.CutPrefix(h.Name, prefix); ok && len(base) > 0 {
71+
fmt.Println(" -", base)
72+
if h.FileInfo().IsDir() {
73+
if err := os.MkdirAll(base, 0755); err != nil {
74+
return err
75+
}
76+
continue
77+
}
78+
// Like os.Create, but with the same mode as the original file (so scripts are executable, etc.)
79+
file, err := os.OpenFile(base, os.O_RDWR|os.O_CREATE|os.O_TRUNC, h.FileInfo().Mode())
80+
if err != nil {
81+
return err
82+
}
83+
if _, err := io.Copy(file, tarReader); err != nil {
84+
return err
85+
}
86+
}
87+
}
88+
return nil
89+
}

src/pkg/http/get.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,25 @@ import (
55
"net/http"
66
)
77

8-
func GetWithAuth(ctx context.Context, url, auth string) (*http.Response, error) {
8+
type Header = http.Header
9+
10+
func GetWithContext(ctx context.Context, url string) (*http.Response, error) {
11+
hreq, err := http.NewRequestWithContext(ctx, "GET", url, nil)
12+
if err != nil {
13+
return nil, err
14+
}
15+
return http.DefaultClient.Do(hreq)
16+
}
17+
18+
func GetWithHeader(ctx context.Context, url string, header http.Header) (*http.Response, error) {
919
hreq, err := http.NewRequestWithContext(ctx, "GET", url, nil)
1020
if err != nil {
1121
return nil, err
1222
}
13-
hreq.Header.Set("Authorization", auth)
23+
hreq.Header = header
1424
return http.DefaultClient.Do(hreq)
1525
}
26+
27+
func GetWithAuth(ctx context.Context, url, auth string) (*http.Response, error) {
28+
return GetWithHeader(ctx, url, http.Header{"Authorization": []string{auth}})
29+
}

0 commit comments

Comments
 (0)