Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
7e79df1
refactor printing existing deployments
jordanstephens Dec 9, 2025
4301b65
factor out WatchServiceState
jordanstephens Dec 9, 2025
a1a6e42
factor out Monitor from TailAndMonitor
jordanstephens Dec 9, 2025
3ec871d
fix Monitor function not returning when cd task exits
jordanstephens Dec 19, 2025
9aa504f
call Monitor after ComposeUp
jordanstephens Dec 9, 2025
7f699ec
print logs on deployment failures
jordanstephens Dec 9, 2025
1e33d89
print targeted build logs after failure
jordanstephens Dec 19, 2025
23e5be6
preserve the ability to tail live
jordanstephens Dec 19, 2025
379d1f4
start with DEPLOYMENT_QUEUED
jordanstephens Dec 19, 2025
b70bcdc
print live service status w/ bubbletea
jordanstephens Dec 19, 2025
045bb3b
update nix vendor hash
jordanstephens Jan 16, 2026
e91db51
fix: resolve flaky TestWaitServiceState race condition
jordanstephens Jan 6, 2026
8502bd2
refactor
jordanstephens Jan 8, 2026
c55a479
use adaptive colors for light and dark backgrounds
jordanstephens Jan 8, 2026
3a18250
avoid new --tail flag, use --verbose
jordanstephens Jan 8, 2026
99bdbc5
include managed services
jordanstephens Jan 8, 2026
b9409c2
avoid printing unspecified service states
jordanstephens Jan 8, 2026
24c5127
check healthchecks when summarizing deployments
jordanstephens Jan 8, 2026
f51cce4
avoid printing service "Status", only print "State"
jordanstephens Jan 8, 2026
cc1df35
fix references to session provider and stack
jordanstephens Jan 15, 2026
cc2cafd
merge down main
jordanstephens Jan 16, 2026
d776f04
update nix vendor hash
jordanstephens Jan 16, 2026
248e2fd
handle UploadModeDefault
jordanstephens Jan 16, 2026
4adc041
Merge branch 'main' into jordan/deployment-status-updates
jordanstephens Jan 16, 2026
9f1c005
report dns errors with healthcheck
jordanstephens Jan 16, 2026
390af2a
removing fqdn from service table
jordanstephens Jan 16, 2026
9083a55
suggest defang cert generate
jordanstephens Jan 16, 2026
7a1647b
warn about timeout
jordanstephens Jan 16, 2026
06c1bac
detect tls errors
jordanstephens Jan 16, 2026
a207708
s/NewServiceFromServiceInfo/ServiceLineItemsFromServiceInfos/
jordanstephens Jan 16, 2026
903a762
factor out ServiceLineItemFromServiceInfo
jordanstephens Jan 16, 2026
84d12a5
s/ServiceLineItem/ServiceEndpoint/
jordanstephens Jan 16, 2026
01aee7c
endpoint-oriented service listing
jordanstephens Jan 17, 2026
17d8cbf
add space after check or "x" to preserve alignment
jordanstephens Jan 19, 2026
9175a8e
avoid printing repeated service row text
jordanstephens Jan 19, 2026
3577c52
print final tui update before quitting
jordanstephens Jan 19, 2026
921e530
move tui to cli pkg
jordanstephens Jan 19, 2026
7b53dbc
update all services states together
jordanstephens Jan 19, 2026
ced032e
Revert "update all services states together"
jordanstephens Jan 19, 2026
9286b7b
use typed service states
jordanstephens Jan 19, 2026
07e4a61
assume pending deployments have completed
jordanstephens Jan 19, 2026
b6fe1f8
Merge branch 'main' into jordan/deployment-status-updates
lionello Jan 20, 2026
f4a01a6
Apply suggestions from code review
lionello Jan 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"permissions": {
"allow": [
"Bash(go test:*)",
"Bash(make:*)",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is potentially destructive, so perhaps we shouldn't include it here

"Bash(golangci-lint run:*)",
"Bash(go build:*)",
"Bash(find:*)",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also potentially destructive because it has -remove and -exec

"Bash(go:*)",
"Bash(git add:*)",
"Bash(git commit:*)"
],
"deny": [],
"ask": []
}
}
28 changes: 28 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch Package",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}/src/cmd/cli",
"console": "integratedTerminal",
"args": [
"-C",
"/Users/jordan/wk/samples/samples/html-css-js",
"deploy",
"-s",
"beta",
],
Comment on lines +14 to +20
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Developer-specific configuration should not be committed.

This launch configuration contains a hardcoded user-specific path (/Users/jordan/wk/samples/...) that won't work for other developers. Additionally, the static analysis correctly identifies invalid JSON syntax - trailing commas on lines 20 and 25 are not valid in standard JSON.

Consider:

  1. Adding .vscode/launch.json to .gitignore
  2. Or using workspace-relative variables like ${workspaceFolder}/samples
🐛 Proposed fix for JSON syntax
       "args": [
         "-C",
-        "/Users/jordan/wk/samples/samples/html-css-js",
+        "${workspaceFolder}/samples/html-css-js",
         "deploy",
         "-s",
-        "beta",
+        "beta"
       ],
       "env": {
         // "GCP_PROJECT_ID": "jordan-project-463223"
         "DEFANG_PROVIDER": "aws",
-        "AWS_REGION": "us-west-2",
+        "AWS_REGION": "us-west-2"
       }
-    },
+    }
   ]
 }
🧰 Tools
🪛 Biome (2.1.2)

[error] 20-20: Expected an array, an object, or a literal but instead found ']'.

Expected an array, an object, or a literal here.

(parse)

🤖 Prompt for AI Agents
In @.vscode/launch.json around lines 14 - 20, The launch.json contains a
developer-specific hardcoded path in the "args" array and invalid JSON trailing
commas; replace the absolute path entry with a workspace-relative variable
(e.g., use ${workspaceFolder}/samples/... in the "args" array where the
hardcoded path appears), remove trailing commas after the last array/object
entries to produce valid JSON, and either add .vscode/launch.json to .gitignore
or move this config to a workspace-specific settings file so other developers
aren't forced to use the same path.

"env": {
// "GCP_PROJECT_ID": "jordan-project-463223"
"DEFANG_PROVIDER": "aws",
"AWS_REGION": "us-west-2",
}
},
]
}
2 changes: 1 addition & 1 deletion pkgs/defang/cli.nix
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ buildGo124Module {
pname = "defang-cli";
version = "git";
src = lib.cleanSource ../../src;
vendorHash = "sha256-A+DwCvfNUKY8TjxyAe+abiT9xIyy5p7VIh5T5ofeZIg="; # TODO: use fetchFromGitHub
vendorHash = "sha256-saGEuoB8Eeh/4SASPeyxW/xWArC7+oW88wUg6EJ39Fc="; # TODO: use fetchFromGitHub
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment was false:

Suggested change
vendorHash = "sha256-saGEuoB8Eeh/4SASPeyxW/xWArC7+oW88wUg6EJ39Fc="; # TODO: use fetchFromGitHub
vendorHash = "sha256-saGEuoB8Eeh/4SASPeyxW/xWArC7+oW88wUg6EJ39Fc=";


subPackages = [ "cmd/cli" ];

Expand Down
64 changes: 43 additions & 21 deletions src/cmd/cli/command/compose.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,18 +177,46 @@ func makeComposeUpCmd() *cobra.Command {
term.Info("Detached.")
return nil
}

// show users the current streaming logs
tailSource := "all services"
if deploy.Etag != "" {
tailSource = "deployment ID " + deploy.Etag
tailOptions := cli.TailOptions{
Deployment: deploy.Etag,
LogType: logs.LogTypeAll,
Since: since,
Verbose: true,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Verbose: true,
Verbose: true,
Stack: session.Stack.Name,

}

waitTimeoutDuration := time.Duration(waitTimeout) * time.Second
var serviceStates map[string]defangv1.ServiceState
if global.Verbose || global.NonInteractive {
tailOptions.Follow = true
serviceStates, err = cli.TailAndMonitor(ctx, project, session.Provider, waitTimeoutDuration, tailOptions)
if err != nil {
return err
}
} else {
term.Info("Live tail logs with `defang tail --deployment=" + deploy.Etag + "`")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
term.Info("Live tail logs with `defang tail --deployment=" + deploy.Etag + "`")
term.Infof("See live tail logs with `defang tail %v`", tailOptions)

to ensure the command line works as-is.

serviceStates, err = cli.MonitorWithUI(ctx, project, session.Provider, waitTimeoutDuration, deploy.Etag)
}
Comment on lines +187 to 198
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

rg -nP '\bGetBool\("tail"\)' --type=go

Repository: DefangLabs/defang

Length of output: 43


🏁 Script executed:

rg -nA5 -nB5 'tail.*Flag\|Flag.*tail' src/cmd/cli/command/compose.go

Repository: DefangLabs/defang

Length of output: 483


🏁 Script executed:

grep -n "tail" src/cmd/cli/command/compose.go | head -20

Repository: DefangLabs/defang

Length of output: 1694


🏁 Script executed:

sed -n '170,260p' src/cmd/cli/command/compose.go

Repository: DefangLabs/defang

Length of output: 3581


🏁 Script executed:

sed -n '1,50p' src/cmd/cli/command/compose.go | grep -A20 "func.*compose"

Repository: DefangLabs/defang

Length of output: 43


🏁 Script executed:

sed -n '130,180p' src/cmd/cli/command/compose.go

Repository: DefangLabs/defang

Length of output: 1764


🏁 Script executed:

sed -n '100,140p' src/cmd/cli/command/compose.go

Repository: DefangLabs/defang

Length of output: 1645


🏁 Script executed:

sed -n '50,100p' src/cmd/cli/command/compose.go

Repository: DefangLabs/defang

Length of output: 1582


🏁 Script executed:

rg -n "composeUpCmd.*&cobra.Command" src/cmd/cli/command/compose.go

Repository: DefangLabs/defang

Length of output: 96


🏁 Script executed:

sed -n '60,75p' src/cmd/cli/command/compose.go

Repository: DefangLabs/defang

Length of output: 432


🏁 Script executed:

sed -n '60,68p' src/cmd/cli/command/compose.go

Repository: DefangLabs/defang

Length of output: 293


Enable the --tail flag to opt into live log tailing in interactive mode.

The --tail flag is currently defined as a no-op but never checked in the code, so defang compose up --tail behaves like the UI path regardless of the flag. Per the PR objective, this should enable live-tail behavior. The condition at lines 189–198 gates only on global.Verbose / global.NonInteractive, so add a check for the --tail flag and route to TailAndMonitor when it's set.

✅ Suggested update
 waitTimeoutDuration := time.Duration(waitTimeout) * time.Second
 var serviceStates map[string]defangv1.ServiceState
-if global.Verbose || global.NonInteractive {
+tail, _ := cmd.Flags().GetBool("tail")
+if global.Verbose || global.NonInteractive || tail {
 	tailOptions.Follow = true
 	serviceStates, err = cli.TailAndMonitor(ctx, project, session.Provider, waitTimeoutDuration, tailOptions)
 	if err != nil {
 		return err
 	}
 } else {
 	term.Info("Live tail logs with `defang tail --deployment=" + deploy.Etag + "`")
 	serviceStates, err = cli.MonitorWithUI(ctx, project, session.Provider, waitTimeoutDuration, deploy.Etag)
 }
🤖 Prompt for AI Agents
In `@src/cmd/cli/command/compose.go` around lines 187 - 198, The code currently
only routes to TailAndMonitor when global.Verbose or global.NonInteractive are
true; update the conditional to also check the CLI --tail flag so interactive
users can opt into live tailing. Modify the if that decides between
TailAndMonitor and MonitorWithUI to include the tail flag (the parsed boolean
flag for "--tail") alongside global.Verbose/global.NonInteractive, and when true
set tailOptions.Follow = true and call cli.TailAndMonitor(ctx, project,
session.Provider, waitTimeoutDuration, tailOptions) (handling err as already
done); otherwise call cli.MonitorWithUI(ctx, project, session.Provider,
waitTimeoutDuration, deploy.Etag). Ensure you reference and use the existing
symbols tailOptions, TailAndMonitor, MonitorWithUI, global.Verbose,
global.NonInteractive, and deploy.Etag.

term.Info("Tailing logs for", tailSource, "; press Ctrl+C to detach:")

tailOptions := newTailOptionsForDeploy(deploy.Etag, since, global.Verbose)
serviceStates, err := cli.TailAndMonitor(ctx, project, session.Provider, time.Duration(waitTimeout)*time.Second, tailOptions)
if err != nil {
if err != nil && !errors.Is(err, context.Canceled) {
deploymentErr := err

// if any services failed to build, only show build logs for those
// services
var unbuiltServices = make([]string, 0, len(project.Services))
for service, state := range serviceStates {
if state <= defangv1.ServiceState_BUILD_STOPPING {
unbuiltServices = append(unbuiltServices, service)
}
}
Comment on lines +205 to +209
Copy link
Contributor

@coderabbitai coderabbitai bot Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Include BUILD_FAILED state when identifying services for build log output.

The condition state <= defangv1.ServiceState_BUILD_STOPPING (value 6) excludes BUILD_FAILED (value 11). Services that failed during the build phase should also be included to properly surface build logs on deployment failure.

🐛 Proposed fix
 var unbuiltServices = make([]string, 0, len(project.Services))
 for service, state := range serviceStates {
-	if state <= defangv1.ServiceState_BUILD_STOPPING {
+	if state <= defangv1.ServiceState_BUILD_STOPPING || state == defangv1.ServiceState_BUILD_FAILED {
 		unbuiltServices = append(unbuiltServices, service)
 	}
 }
🤖 Prompt for AI Agents
In @src/cmd/cli/command/compose.go around lines 368 - 372, The loop that
collects unbuiltServices currently checks if state <=
defangv1.ServiceState_BUILD_STOPPING which misses
defangv1.ServiceState_BUILD_FAILED; update the condition in the serviceStates
iteration (the block that appends to unbuiltServices) to also include
BUILD_FAILED (e.g., state <= defangv1.ServiceState_BUILD_STOPPING || state ==
defangv1.ServiceState_BUILD_FAILED) so services that failed during build are
captured for build log output.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we show any service with state != DEPLOYMENT_COMPLETED?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find the ServiceState enum definition to understand all states and their values
rg -n -A 30 'enum ServiceState' --type=proto

Length of output: 29


🏁 Script executed:

#!/bin/bash
# Find the ServiceState enum definition
rg -n -A 30 'ServiceState.*=.*iota|const.*ServiceState_' --type=go

# Also search in proto files
fd -e proto -x cat {} | head -200

Length of output: 7507


🏁 Script executed:

#!/bin/bash
# Search for ServiceState enum in proto files
rg -n 'enum ServiceState' src/protos/ -A 30

# Also check the generated Go file for ServiceState constants
rg -n 'ServiceState_BUILD|ServiceState_DEPLOYMENT' src/protos/ | head -40

Length of output: 3191


@lionello Thanks for the question! I think the narrower condition is actually more appropriate here. Let me explain based on the ServiceState enum values:

State progression:

  • Build phase: BUILD_QUEUED (1) → BUILD_PROVISIONING (2) → ... → BUILD_STOPPING (6)
  • Build outcome: Either BUILD_FAILED (11) or moves to deployment phase
  • Deployment phase: DEPLOYMENT_PENDING (8) → DEPLOYMENT_COMPLETED (9) or DEPLOYMENT_FAILED (10)

Why state != DEPLOYMENT_COMPLETED would be too broad:

The comment says "if any services failed to build, only show build logs for those services". The intent is to show BUILD logs specifically for services that failed during the BUILD phase.

Using state != DEPLOYMENT_COMPLETED would include:

  • DEPLOYMENT_FAILED (10): These services successfully built but failed during deployment. Showing their build logs wouldn't help debug the deployment failure - we'd need Pulumi/deployment logs instead.
  • DEPLOYMENT_PENDING (8): These might not have failed at all, just pending.
  • UPDATE_QUEUED (7): Using existing image, no build occurred.

The correct condition should be:

Services that are in the build phase (BUILD_QUEUED through BUILD_STOPPING) OR explicitly failed during build (BUILD_FAILED):

if state <= defangv1.ServiceState_BUILD_STOPPING || state == defangv1.ServiceState_BUILD_FAILED {
    unbuiltServices = append(unbuiltServices, service)
}

This way, if a service reaches DEPLOYMENT_FAILED, we'll show all logs (including Pulumi logs), not just build logs, which is what you'd need to debug deployment issues.

Does that make sense?


🧬 Code Graph Analysis Results

[src/cmd/cli/command/compose.go] (summary)

  • Summary: The file prints the current service states after deployment via cli.PrintServices(project.Name, provider). The user question about showing services with state != DEPLOYMENT_COMPLETED relates to how states are presented and possibly filtered in the code path that monitors/tails deployment progress and displays statuses. The file also uses a UI path (MonitorWithUI) that can adjust service states (eg, marking DEPLOYMENT_COMPLETED) during monitoring. No separate filtering logic is present in this file snippet; any filtering would be in underlying PrintServices/monitoring logic.

[src/protos/io/defang/v1/fabric.pb.go] 2571-2594

  • Snippet: ServiceInfo struct

  • Content:
    ServiceInfo struct {
    state protoimpl.MessageState protogen:"open.v1"
    Service *Service protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"
    Endpoints []string protobuf:"bytes,2,rep,name=endpoints,proto3" json:"endpoints,omitempty" // list of endpoints, one for each port
    Project string protobuf:"bytes,3,opt,name=project,proto3" json:"project,omitempty" // was: tenant; defaults to tenant ID
    Etag string protobuf:"bytes,4,opt,name=etag,proto3" json:"etag,omitempty"
    Status string protobuf:"bytes,5,opt,name=status,proto3" json:"status,omitempty"
    NatIps []string protobuf:"bytes,6,rep,name=nat_ips,json=natIps,proto3" json:"nat_ips,omitempty" // comma-separated list of NAT IPs
    LbIps []string protobuf:"bytes,7,rep,name=lb_ips,json=lbIps,proto3" json:"lb_ips,omitempty" // comma-separated list of internal CIDRs for the load-balancer
    PrivateFqdn string protobuf:"bytes,8,opt,name=private_fqdn,json=privateFqdn,proto3" json:"private_fqdn,omitempty" // fully qualified domain name (host)
    PublicFqdn string protobuf:"bytes,9,opt,name=public_fqdn,json=publicFqdn,proto3" json:"public_fqdn,omitempty" // fully qualified domain name (ingress)
    CreatedAt *timestamppb.Timestamp protobuf:"bytes,10,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"
    UpdatedAt *timestamppb.Timestamp protobuf:"bytes,11,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"
    ZoneId string protobuf:"bytes,12,opt,name=zone_id,json=zoneId,proto3" json:"zone_id,omitempty" // zone ID for byod domain
    UseAcmeCert bool protobuf:"varint,13,opt,name=use_acme_cert,json=useAcmeCert,proto3" json:"use_acme_cert,omitempty" // If we should setup the facilities to use ACME(let's encrypt) certs
    State ServiceState protobuf:"varint,15,opt,name=state,proto3,enum=io.defang.v1.ServiceState" json:"state,omitempty" // enumerated status of the service
    Domainname string protobuf:"bytes,16,opt,name=domainname,proto3" json:"domainname,omitempty" // domain name for the service
    LbDnsName string protobuf:"bytes,17,opt,name=lb_dns_name,json=lbDnsName,proto3" json:"lb_dns_name,omitempty" // fully qualified domain name for the load-balancer
    AllowScaling bool protobuf:"varint,18,opt,name=allow_scaling,json=allowScaling,proto3" json:"allow_scaling,omitempty" // true if service is allowed to autoscale
    HealthcheckPath string protobuf:"bytes,19,opt,name=healthcheck_path,json=healthcheckPath,proto3" json:"healthcheck_path,omitempty"
    Type ResourceType protobuf:"varint,21,opt,name=type,proto3,enum=io.defang.v1.ResourceType" json:"type,omitempty"
    unknownFields protoimpl.UnknownFields
    sizeCache protoimpl.SizeCache
    }

  • Relevance: This shows that a ServiceInfo has a State field of type ServiceState, which is the source of the status values (including DEPLOYMENT_COMPLETED) that the user comment concerns.

[src/cmd/cli/command/composeUpTui.go] 142-201

  • Snippet: MonitorWithUI usage of DEPLOYMENT_COMPLETED

  • Content (abridged to key parts):
    func MonitorWithUI(ctx context.Context, project compose.Project, provider client.Provider, waitTimeout time.Duration, deploymentID string) (map[string]defangv1.ServiceState, error) {
    // ... setup ...
    go func() {
    // UI runs in a goroutine
    if _, err := p.Run(); err != nil { /
    ... */ }
    }()

    // Start monitoring in a goroutine
    go func() {
    // Monitor and update UI with service state changes
    serviceStates, monitorErr = Monitor(ctx, project, provider, waitTimeout, deploymentID, func(msg *defangv1.SubscribeResponse, states *ServiceStates) error {
    for name, state := range *states {
    p.Send(serviceUpdate{name: name, status: state})
    }
    return nil
    })

    if monitorErr == nil {
      // When monitoring completes, mark any still-DEPLOYMENT_PENDING as DEPLOYMENT_COMPLETED
      for _, serviceName := range servicesNames {
        if serviceStates[serviceName] == defangv1.ServiceState_DEPLOYMENT_PENDING {
          p.Send(serviceUpdate{name: serviceName, status: defangv1.ServiceState_DEPLOYMENT_COMPLETED})
        }
      }
    }
    p.Quit()
    

    }()

    // wait for UI/monitor goroutines
    wg.Wait()

    return serviceStates, monitorErr
    }

  • Relevance: This shows explicit handling of the DEPLOYMENT_COMPLETED state in the UI; it demonstrates how DEPLOYMENT_PENDING can be transformed into DEPLOYMENT_COMPLETED, which is directly relevant to the idea of filtering or deciding which services to show based on completion state.

Notes and how they relate to user comment

  • The user asks: "Shouldn't we show any service with state != DEPLOYMENT_COMPLETED?" The relevant code paths indicate:
    • Service state is represented by ServiceState, with DEPLOYMENT_COMPLETED being a possible state (as used in MonitorWithUI).
    • There is UI logic that explicitly updates any remaining DEPLOYMENT_PENDING to DEPLOYMENT_COMPLETED upon successful completion of monitoring.
    • Print/monitoring functions (PrintServices, Tail/Monitor) operate on a collection of service states but the provided snippets do not show an explicit filter to exclude non-COMPLETED services from being displayed; rather, the UI path actively updates statuses and shows them, including potentially non-COMPLETED states until completion.
  • If you want to enforce showing only services with state DEPLOYMENT_COMPLETED, you would likely implement a filter around the display paths (e.g., PrintObject/PrintServices) or in the monitor UI to skip services whose final state is not DEPLOYMENT_COMPLETED. The MonitorWithUI snippet demonstrates where such a logic point could be inserted (e.g., filter or hide entries with non-COMPLETED states, or add a flag to show only completed services).

🧠 Learnings used
Learnt from: edwardrf
Repo: DefangLabs/defang PR: 1747
File: src/pkg/clouds/gcp/cloudbuild.go:62-86
Timestamp: 2026-01-09T20:19:04.424Z
Learning: In src/pkg/clouds/gcp/cloudbuild.go, BuildTag.Parse should fail on unexpected tag formats (tags that don't have 3-4 underscore-separated parts or aren't DefangCDBuildTag) because build tags are strictly controlled and only created in two places: (1) running CD in cloudbuild by CLI, and (2) building images by CD. Unexpected tags indicate an error case.

Learnt from: edwardrf
Repo: DefangLabs/defang PR: 1747
File: src/pkg/cli/client/byoc/gcp/stream.go:497-512
Timestamp: 2026-01-09T20:31:23.614Z
Learning: In src/pkg/cli/client/byoc/gcp/stream.go, the getReadyServicesCompletedResps helper function intentionally uses variable shadowing. The loop variable `status` from `readyServices` map represents individual service statuses, while the function parameter (to be renamed `cdStatus`) represents only the CD service (defangCD) completion status. Each ready service should retain its original status from the map.

if len(unbuiltServices) > 0 {
tailOptions.LogType = logs.LogTypeBuild
tailOptions.Services = unbuiltServices
}
err := cli.Tail(ctx, session.Provider, project.Name, tailOptions)
if err != nil && !errors.Is(err, io.EOF) {
term.Warn("Failed to tail logs for deployment error", err)
return deploymentErr
}

debugger, err := debug.NewDebugger(ctx, global.Cluster, &global.Stack)
if err != nil {
term.Warn("Failed to initialize debugger:", err)
Expand All @@ -197,8 +225,8 @@ func makeComposeUpCmd() *cobra.Command {
handleTailAndMonitorErr(ctx, deploymentErr, debugger, debug.DebugConfig{
Deployment: deploy.Etag,
Project: project,
ProviderID: &global.Stack.Provider,
Stack: &global.Stack.Name,
ProviderID: &session.Stack.Provider,
Stack: &session.Stack.Name,
Since: since,
Until: time.Now(),
})
Expand All @@ -209,15 +237,9 @@ func makeComposeUpCmd() *cobra.Command {
service.State = serviceStates[service.Service.Name]
}

services, err := cli.NewServiceFromServiceInfo(deploy.Services)
if err != nil {
return err
}

// Print the current service states of the deployment
err = cli.PrintServiceStatesAndEndpoints(services)
if err != nil {
return err
if err := cli.PrintServices(cmd.Context(), project.Name, session.Provider); err != nil {
term.Warn(err)
}

term.Info("Done.")
Expand Down Expand Up @@ -274,7 +296,7 @@ func confirmDeployment(targetDirectory string, existingDeployments []*defangv1.D
}

func printExistingDeployments(existingDeployments []*defangv1.Deployment) {
term.Info("This project was previously deployed to the following locations:")
term.Info("This project has already deployed to the following locations:")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was?

Suggested change
term.Info("This project has already deployed to the following locations:")
term.Info("This project was already deployed to the following locations:")

deploymentStrings := make([]string, 0, len(existingDeployments))
for _, dep := range existingDeployments {
var providerId client.ProviderID
Expand Down
18 changes: 15 additions & 3 deletions src/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ require (
github.com/aws/smithy-go v1.24.0
github.com/awslabs/goformation/v7 v7.14.9
github.com/bufbuild/connect-go v1.10.0
github.com/charmbracelet/bubbles v0.21.0
github.com/charmbracelet/bubbletea v1.3.10
github.com/charmbracelet/lipgloss v1.1.0
github.com/compose-spec/compose-go/v2 v2.7.2-0.20250715094302-8da9902241f9
github.com/digitalocean/godo v1.131.1
github.com/docker/cli v27.3.1+incompatible
Expand All @@ -50,7 +53,7 @@ require (
github.com/miekg/dns v1.1.59
github.com/moby/buildkit v0.17.3
github.com/moby/patternmatcher v0.6.0
github.com/muesli/termenv v0.15.2
github.com/muesli/termenv v0.16.0
github.com/openai/openai-go v1.12.0
github.com/opencontainers/image-spec v1.1.0
github.com/pelletier/go-toml/v2 v2.2.2
Expand Down Expand Up @@ -89,6 +92,10 @@ require (
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
github.com/charmbracelet/x/ansi v0.10.1 // indirect
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
github.com/charmbracelet/x/term v0.2.1 // indirect
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f // indirect
github.com/containerd/typeurl/v2 v2.2.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
Expand All @@ -97,6 +104,7 @@ require (
github.com/docker/docker-credential-helpers v0.8.2 // indirect
github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect
github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/go-jose/go-jose/v4 v4.1.0 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/google/dotprompt/go v0.0.0-20251014011017-8d056e027254 // indirect
Expand All @@ -111,11 +119,14 @@ require (
github.com/invopop/jsonschema v0.13.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mbleigh/raymond v0.0.0-20250414171441-6b3a58ab9e0a // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
github.com/rivo/uniseg v0.4.2 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 // indirect
github.com/sergi/go-diff v1.3.1 // indirect
Expand All @@ -132,6 +143,7 @@ require (
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
github.com/zeebo/errs v1.4.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
Expand Down
39 changes: 33 additions & 6 deletions src/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,20 @@ github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqy
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs=
github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg=
github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
github.com/charmbracelet/x/ansi v0.10.1 h1:rL3Koar5XvX0pHGfovN03f5cxLbCF2YvLeyz7D2jVDQ=
github.com/charmbracelet/x/ansi v0.10.1/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f h1:C5bqEmzEPLsHm9Mv73lSE9e9bKV23aB1vxOsmZrkl3k=
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
github.com/compose-spec/compose-go/v2 v2.7.2-0.20250715094302-8da9902241f9 h1:kqvhWCmg3fVAPbfE8aJdV+qX1VqK4oK/DRI5yxeVd4E=
Expand Down Expand Up @@ -163,6 +177,8 @@ github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an
github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4=
github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8=
github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
Expand Down Expand Up @@ -262,8 +278,10 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
github.com/mbleigh/raymond v0.0.0-20250414171441-6b3a58ab9e0a h1:v2cBA3xWKv2cIOVhnzX/gNgkNXqiHfUgJtA3r61Hf7A=
Expand All @@ -283,8 +301,12 @@ github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
github.com/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8=
github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs=
github.com/onsi/gomega v1.33.0 h1:snPCflnZrpMsy94p4lXVEkHo12lmPnc3vY5XBbreexE=
Expand All @@ -307,8 +329,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8=
github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/ross96D/cancelreader v0.2.6 h1:XLPWassoMWRTlHvEoVKS3z0N0a7jHcIupGU0U1gNArw=
Expand Down Expand Up @@ -365,6 +387,8 @@ github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
Expand Down Expand Up @@ -408,6 +432,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
Expand Down Expand Up @@ -436,6 +462,7 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
Expand Down
2 changes: 1 addition & 1 deletion src/pkg/agent/tools/default_tool_cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func (DefaultToolCLI) ConfigDelete(ctx context.Context, projectName string, prov
return cli.ConfigDelete(ctx, projectName, provider, name)
}

func (DefaultToolCLI) GetServices(ctx context.Context, projectName string, provider client.Provider) ([]cli.ServiceLineItem, error) {
func (DefaultToolCLI) GetServices(ctx context.Context, projectName string, provider client.Provider) ([]cli.ServiceEndpoint, error) {
return cli.GetServices(ctx, projectName, provider)
}

Expand Down
2 changes: 1 addition & 1 deletion src/pkg/agent/tools/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type CLIInterface interface {
Connect(ctx context.Context, cluster string) (*client.GrpcClient, error)
CreatePlaygroundProvider(fabric *client.GrpcClient) client.Provider
GenerateAuthURL(authPort int) string
GetServices(ctx context.Context, projectName string, provider client.Provider) ([]cli.ServiceLineItem, error)
GetServices(ctx context.Context, projectName string, provider client.Provider) ([]cli.ServiceEndpoint, error)
InteractiveLoginMCP(ctx context.Context, cluster string, mcpClient string) error
ListConfig(ctx context.Context, provider client.Provider, projectName string) (*defangv1.Secrets, error)
LoadProject(ctx context.Context, loader client.Loader) (*compose.Project, error)
Expand Down
7 changes: 3 additions & 4 deletions src/pkg/agent/tools/services_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ type MockCLI struct {
MockProjectName string

GetServicesError error
MockServices []cli.ServiceLineItem
MockServices []cli.ServiceEndpoint
GetServicesCalled bool
GetServicesProject string
GetServicesProvider client.Provider
Expand All @@ -56,7 +56,7 @@ func (m *MockCLI) LoadProjectNameWithFallback(ctx context.Context, loader client
return "default-project", nil
}

func (m *MockCLI) GetServices(ctx context.Context, projectName string, provider client.Provider) ([]cli.ServiceLineItem, error) {
func (m *MockCLI) GetServices(ctx context.Context, projectName string, provider client.Provider) ([]cli.ServiceEndpoint, error) {
m.GetServicesCalled = true
m.GetServicesProject = projectName
m.GetServicesProvider = provider
Expand Down Expand Up @@ -227,11 +227,10 @@ func TestHandleServicesToolWithMockCLI(t *testing.T) {
MockClient: &client.GrpcClient{},
MockProvider: &client.PlaygroundProvider{},
MockProjectName: "test-project",
MockServices: []cli.ServiceLineItem{
MockServices: []cli.ServiceEndpoint{
{
Service: "test-service",
Deployment: "test-deployment",
Fqdn: "test.example.com",
Status: "running",
},
},
Expand Down
7 changes: 6 additions & 1 deletion src/pkg/cli/client/errors.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package client

import "fmt"
import (
"errors"
"fmt"
)

var ErrDeploymentSucceeded = errors.New("deployment succeeded")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels weird. It's OK if this is an internal (lowercase e) thing and never leaves the package.


type ErrDeploymentFailed struct {
Message string
Expand Down
Loading
Loading