Skip to content

Commit 043c85e

Browse files
committed
Merge branch 'main' into eric-add-postgres-support
2 parents 80df68e + cd53acf commit 043c85e

File tree

10 files changed

+742
-721
lines changed

10 files changed

+742
-721
lines changed

pkgs/defang/default.nix

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,22 @@ system ? builtins.currentSystem
1010
}:
1111
let
1212
shaMap = {
13-
x86_64-linux = "0zxd1hg10867j15j538hxx8iwh7ld3g9krsdw7ajz9h2b2dsa3pz";
14-
aarch64-linux = "0334q80rg1945ymkq4r6a4bpmfiyxapgn82v9cgn9n9r5z688ymm";
15-
x86_64-darwin = "00i3hyal33fgr0vgvwvxv4n1k0zdr2dml74qzd6ndhbnl4nv96cj";
16-
aarch64-darwin = "00i3hyal33fgr0vgvwvxv4n1k0zdr2dml74qzd6ndhbnl4nv96cj";
13+
x86_64-linux = "1z3n27w6vgc0xwncdnpi8lz44247k4k0c20a52mdwcwd7aj70xhj";
14+
aarch64-linux = "0lfv1s9hx521dc1469p5myb4rffaifk6rnmhsgln7fg96kgjjjim";
15+
x86_64-darwin = "18jfx4561p2ghrn74wp53xcbbc3xi3zxpdbl2f4npyip936snng7";
16+
aarch64-darwin = "18jfx4561p2ghrn74wp53xcbbc3xi3zxpdbl2f4npyip936snng7";
1717
};
1818

1919
urlMap = {
20-
x86_64-linux = "https://github.com/DefangLabs/defang/releases/download/v0.5.32/defang_0.5.32_linux_amd64.tar.gz";
21-
aarch64-linux = "https://github.com/DefangLabs/defang/releases/download/v0.5.32/defang_0.5.32_linux_arm64.tar.gz";
22-
x86_64-darwin = "https://github.com/DefangLabs/defang/releases/download/v0.5.32/defang_0.5.32_macOS.zip";
23-
aarch64-darwin = "https://github.com/DefangLabs/defang/releases/download/v0.5.32/defang_0.5.32_macOS.zip";
20+
x86_64-linux = "https://github.com/DefangLabs/defang/releases/download/v0.5.36/defang_0.5.36_linux_amd64.tar.gz";
21+
aarch64-linux = "https://github.com/DefangLabs/defang/releases/download/v0.5.36/defang_0.5.36_linux_arm64.tar.gz";
22+
x86_64-darwin = "https://github.com/DefangLabs/defang/releases/download/v0.5.36/defang_0.5.36_macOS.zip";
23+
aarch64-darwin = "https://github.com/DefangLabs/defang/releases/download/v0.5.36/defang_0.5.36_macOS.zip";
2424
};
2525
in
2626
stdenvNoCC.mkDerivation {
2727
pname = "defang";
28-
version = "0.5.32";
28+
version = "0.5.36";
2929
src = fetchurl {
3030
url = urlMap.${system};
3131
sha256 = shaMap.${system};

src/cmd/cli/command/commands.go

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,14 @@ func Execute(ctx context.Context) error {
7979
printDefangHint("Fix the error and try again. To validate the compose file, use:", compose+" config")
8080
}
8181

82-
if strings.Contains(err.Error(), "secret") {
82+
if strings.Contains(err.Error(), "config") {
8383
printDefangHint("To manage sensitive service config, use:", "config")
8484
}
8585

86+
if err.Error() == "resource_exhausted: maximum number of projects reached" {
87+
printDefangHint("To deactivate a project, do:", "compose down")
88+
}
89+
8690
var cerr *cli.CancelError
8791
if errors.As(err, &cerr) {
8892
printDefangHint("Detached. The process will keep running.\nTo continue the logs from where you left off, do:", cerr.Error())
@@ -820,7 +824,6 @@ var composeUpCmd = &cobra.Command{
820824
var force, _ = cmd.Flags().GetBool("force")
821825
var detach, _ = cmd.Flags().GetBool("detach")
822826

823-
targetState := defangv1.ServiceState_SERVICE_COMPLETED
824827
since := time.Now()
825828
deploy, project, err := cli.ComposeUp(cmd.Context(), client, force)
826829
if err != nil {
@@ -839,35 +842,38 @@ var composeUpCmd = &cobra.Command{
839842
}
840843

841844
tailCtx, cancelTail := context.WithCancelCause(cmd.Context())
842-
defer cancelTail(nil) // to cancel waitServiceState
845+
defer cancelTail(nil) // to cancel WaitServiceState and clean-up context
843846

844-
errSuccess := errors.New("deployment succeeded")
847+
errCompleted := errors.New("deployment succeeded") // tail canceled because of deployment completion
848+
const targetState = defangv1.ServiceState_DEPLOYMENT_COMPLETED
845849
targetStateReached := false
846850

847851
go func() {
848852
services := make([]string, len(deploy.Services))
849853
for i, serviceInfo := range deploy.Services {
850854
services[i] = serviceInfo.Service.Name
851855
}
852-
if err := waitServiceState(tailCtx, targetState, deploy.Etag, services); err != nil {
853-
if errors.Is(err, errDeploymentFailed) {
856+
857+
if err := cli.WaitServiceState(tailCtx, client, targetState, deploy.Etag, services); err != nil {
858+
var errDeploymentFailed cli.ErrDeploymentFailed
859+
if errors.As(err, &errDeploymentFailed) {
854860
cancelTail(err)
855861
} else if !errors.Is(err, context.Canceled) {
856-
term.Warnf("failed to wait for service status: %v", err)
862+
term.Warnf("failed to wait for service status: %v", err) // TODO: don't print in Go-routine
857863
}
858864
} else {
859865
targetStateReached = true
860-
cancelTail(errSuccess)
866+
cancelTail(errCompleted)
861867
}
862868
}()
863869

864870
// show users the current streaming logs
865-
services := "all services"
871+
tailSource := "all services"
866872
if deploy.Etag != "" {
867-
services = "deployment ID " + deploy.Etag
873+
tailSource = "deployment ID " + deploy.Etag
868874
}
869875

870-
term.Info("Tailing logs for", services, "; press Ctrl+C to detach:")
876+
term.Info("Tailing logs for", tailSource, "; press Ctrl+C to detach:")
871877
tailParams := cli.TailOptions{
872878
Etag: deploy.Etag,
873879
Since: since,
@@ -876,29 +882,42 @@ var composeUpCmd = &cobra.Command{
876882

877883
// blocking call to tail
878884
if err := cli.Tail(tailCtx, client, tailParams); err != nil {
879-
if errors.Is(context.Cause(tailCtx), errDeploymentFailed) {
880-
term.Warn("Deployment FAILED. Service(s) not running.")
885+
term.Debugf("Tail failed with %v", err)
886+
if connect.CodeOf(err) == connect.CodePermissionDenied {
887+
// If tail fails because of missing permission, we wait for the deployment to finish
888+
term.Warn("Unable to tail logs. Waiting for the deployment to finish.")
889+
<-tailCtx.Done()
890+
} else if !errors.Is(tailCtx.Err(), context.Canceled) {
891+
return err // any error other than cancelation
892+
}
881893

882-
_, isPlayground := client.(*cliClient.PlaygroundClient)
883-
if !nonInteractive && isPlayground {
894+
// Tail got canceled; if it was by anything other than completion, prompt to show debugger
895+
if !errors.Is(context.Cause(tailCtx), errCompleted) {
896+
var failedServices []string
897+
var errDeploymentFailed cli.ErrDeploymentFailed
898+
if errors.As(context.Cause(tailCtx), &errDeploymentFailed) {
899+
term.Warn(errDeploymentFailed)
900+
failedServices = []string{errDeploymentFailed.Service, errDeploymentFailed.Service + "-image"} // HACK: also grab Kaniko logs
901+
} else {
902+
term.Warn("Deployment is not finished. Service(s) might not be running.")
903+
// TODO: some services might be OK and we should only debug the ones that are not
904+
}
905+
906+
if _, isPlayground := client.(*cliClient.PlaygroundClient); !nonInteractive && isPlayground {
884907
var aiDebug bool
885908
if err := survey.AskOne(&survey.Confirm{
886909
Message: "Would you like to debug the deployment with AI?",
887910
Help: "This will send logs and artifacts to our backend and attempt to diagnose the issue and provide a solution.",
888911
}, &aiDebug); err != nil {
889912
term.Debugf("failed to ask for AI debug: %v", err)
890913
} else if aiDebug {
891-
// Call the AI debug endpoint using the original command context (not the tailCtx which is canceled)
892-
if err := cli.Debug(cmd.Context(), client, deploy.Etag, project.WorkingDir); err != nil {
914+
// Call the AI debug endpoint using the original command context (not the tailCtx which is canceled); HACK: cmd might be canceled too
915+
// TODO: use the WorkingDir of the failed service, might not be the project's root
916+
if err := cli.Debug(context.TODO(), client, deploy.Etag, project.WorkingDir, failedServices); err != nil {
893917
term.Warnf("failed to debug deployment: %v", err)
894918
}
895919
}
896920
}
897-
} else if connect.CodeOf(err) == connect.CodePermissionDenied {
898-
// If tail fails because of missing permission, we wait for the deployment to finish
899-
term.Warn("Failed to tail logs. Waiting for the deployment to finish.")
900-
<-tailCtx.Done()
901-
} else if !errors.Is(context.Cause(tailCtx), errSuccess) {
902921
return err
903922
}
904923
}
@@ -952,7 +971,8 @@ var debugCmd = &cobra.Command{
952971
RunE: func(cmd *cobra.Command, args []string) error {
953972
etag, _ := cmd.Flags().GetString("etag")
954973

955-
return cli.Debug(cmd.Context(), client, etag, ".")
974+
// TODO: use the WorkingDir of the current project instead of current folder
975+
return cli.Debug(cmd.Context(), client, etag, ".", nil)
956976
},
957977
}
958978

src/cmd/cli/command/servicemonitor.go

Lines changed: 0 additions & 60 deletions
This file was deleted.

src/pkg/cli/compose/validation.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,14 @@ func ValidateProject(project *compose.Project) error {
262262
}
263263
}
264264

265+
if _, ok := svccfg.Extensions["x-defang-postgres"]; ok {
266+
// Ensure the image is a valid Postgres image
267+
repo := strings.SplitN(svccfg.Image, ":", 2)[0]
268+
if !strings.HasSuffix(repo, "postgres") {
269+
term.Warnf("service %q: managed Postgres service should use a postgres image", svccfg.Name)
270+
}
271+
}
272+
265273
for k := range svccfg.Extensions {
266274
switch k {
267275
case "x-defang-dns-role", "x-defang-static-files", "x-defang-redis", "x-defang-postgres":

src/pkg/cli/debug.go

Lines changed: 64 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package cli
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"os"
78
"path/filepath"
@@ -11,39 +12,29 @@ import (
1112
defangv1 "github.com/DefangLabs/defang/src/protos/io/defang/v1"
1213
)
1314

14-
func Debug(ctx context.Context, c client.Client, etag, folder string) error {
15+
// Arbitrary limit on the maximum number of files to process to avoid walking the entire drive and we have limited
16+
// context window for the LLM also.
17+
// FIXME: Find a better way to handle files.
18+
const maxFiles = 20
19+
20+
var ErrFileLimitReached = errors.New("file limit reached")
21+
22+
func Debug(ctx context.Context, c client.Client, etag, folder string, services []string) error {
1523
term.Debug("Invoking AI debugger for deployment", etag)
1624

1725
// FIXME: use the project information to determine which files to send
1826
patterns := []string{"Dockerfile", "*compose.yaml", "*compose.yml", "*.js", "*.ts", "*.py", "*.go", "requirements.txt", "package.json", "go.mod"}
19-
var files []*defangv1.File
20-
for _, pattern := range patterns {
21-
fullPattern := filepath.Join(folder, pattern)
22-
matchingFiles, err := filepath.Glob(fullPattern)
23-
if err != nil {
24-
term.Debug("failed to find matching files:", err)
25-
continue
26-
}
27-
for _, fullPath := range matchingFiles {
28-
b, err := os.ReadFile(fullPath)
29-
if err != nil {
30-
term.Debug("failed to read file:", err)
31-
continue
32-
}
33-
files = append(files, &defangv1.File{
34-
Name: filepath.Base(fullPath),
35-
Content: string(b),
36-
})
37-
}
38-
}
27+
28+
files := findMatchingFiles(folder, patterns)
3929

4030
if DoDryRun {
4131
return ErrDryRun
4232
}
4333

4434
resp, err := c.Debug(ctx, &defangv1.DebugRequest{
45-
Etag: etag,
46-
Files: files,
35+
Etag: etag,
36+
Files: files,
37+
Services: services,
4738
})
4839
if err != nil {
4940
return err
@@ -67,7 +58,7 @@ func Debug(ctx context.Context, c client.Client, etag, folder string) error {
6758

6859
if (len(service.CodeChanges)) > 0 {
6960
for _, changes := range service.CodeChanges {
70-
term.Println(fmt.Sprintf("File %s:", changes.File))
61+
term.Println(fmt.Sprintf("Updated %s:", changes.File))
7162
term.Println("-------------------")
7263
term.Println(changes.Change)
7364
term.Println("")
@@ -80,3 +71,52 @@ func Debug(ctx context.Context, c client.Client, etag, folder string) error {
8071
// }
8172
return nil
8273
}
74+
75+
func findMatchingFiles(folder string, patterns []string) []*defangv1.File {
76+
var files []*defangv1.File
77+
fileCount := 0
78+
79+
err := filepath.Walk(folder, func(path string, info os.FileInfo, err error) error {
80+
if err != nil {
81+
term.Debug("error accessing path:", err)
82+
return nil // continue walking
83+
}
84+
85+
if info.IsDir() {
86+
return nil // continue to next file/directory
87+
}
88+
89+
if fileCount >= maxFiles {
90+
term.Debug("file limit reached, stopping search")
91+
return ErrFileLimitReached
92+
}
93+
94+
for _, pattern := range patterns {
95+
matched, err := filepath.Match(pattern, info.Name())
96+
if err != nil {
97+
term.Debug("error matching pattern:", err)
98+
continue
99+
}
100+
if matched {
101+
b, err := os.ReadFile(path)
102+
if err != nil {
103+
term.Debug("failed to read file:", err)
104+
continue
105+
}
106+
files = append(files, &defangv1.File{
107+
Name: filepath.Base(path),
108+
Content: string(b),
109+
})
110+
fileCount++
111+
break // file matched, no need to check other patterns
112+
}
113+
}
114+
return nil
115+
})
116+
117+
if err != nil && err != ErrFileLimitReached {
118+
term.Debug("error walking the path:", err)
119+
}
120+
121+
return files
122+
}

0 commit comments

Comments
 (0)