Skip to content

Commit 3561903

Browse files
committed
wip
1 parent b7f2dcf commit 3561903

File tree

11 files changed

+99
-136
lines changed

11 files changed

+99
-136
lines changed

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,13 +143,12 @@ The Defang CLI recognizes the following environment variables:
143143
- `DEFANG_ISSUER` - The OAuth2 issuer to use for authentication; defaults to `https://auth.defang.io`
144144
- `DEFANG_MODEL_ID` - The model ID of the LLM to use for the generate/debug AI integration (Pro users only)
145145
- `DEFANG_NO_CACHE` - If set to `true`, disables pull-through caching of container images; defaults to `false`
146-
- `DEFANG_ORG` - The name of the organization to use; defaults to the user's GitHub name
146+
- `DEFANG_ORG` - The name of the organization to use; defaults to the user's personal org
147147
- `DEFANG_PREFIX` - The prefix to use for all BYOC resources; defaults to `Defang`
148148
- `DEFANG_PROVIDER` - The name of the cloud provider to use, `auto` (default), `aws`, `digitalocean`, `gcp`, or `defang`
149149
- `DEFANG_PULUMI_BACKEND` - The Pulumi backend URL or `"pulumi-cloud"`; defaults to a self-hosted backend
150150
- `DEFANG_PULUMI_DIR` - Run Pulumi from this folder, instead of spawning a cloud task; requires `--debug` (BYOC only)
151151
- `DEFANG_PULUMI_VERSION` - Override the version of the Pulumi image to use (`aws` provider only)
152-
- `DEFANG_TENANT` - The name of the tenant to use.
153152
- `NO_COLOR` - If set to any value, disables color output; by default, color output is enabled depending on the terminal
154153
- `PULUMI_ACCESS_TOKEN` - The Pulumi access token to use for authentication to Pulumi Cloud; see `DEFANG_PULUMI_BACKEND`
155154
- `PULUMI_CONFIG_PASSPHRASE` - Passphrase used to generate a unique key for your stack, and configuration and encrypted state values

pkgs/defang/cli.nix

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ buildGoModule {
77
pname = "defang-cli";
88
version = "git";
99
src = ../../src;
10-
vendorHash = "sha256-0M0jtBaPE1jSx4nrOR4XMw1Im1tMYTKYCkcKiZ1bj8M="; # TODO: use fetchFromGitHub
10+
vendorHash = "sha256-Yods9AZKO/JYbLUnnrOqhS/P6sA/2zq82ZIzuLYo/EM="; # TODO: use fetchFromGitHub
1111

1212
subPackages = [ "cmd/cli" ];
1313

pkgs/npm/README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ The Defang CLI recognizes the following environment variables:
4444
- `DEFANG_PULUMI_BACKEND` - The Pulumi backend URL or `"pulumi-cloud"`; defaults to a self-hosted backend
4545
- `DEFANG_PULUMI_DIR` - Run Pulumi from this folder, instead of spawning a cloud task; requires `--debug` (BYOC only)
4646
- `DEFANG_PULUMI_VERSION` - Override the version of the Pulumi image to use (`aws` provider only)
47-
- `DEFANG_TENANT` - The name of the tenant to use.
4847
- `NO_COLOR` - If set to any value, disables color output; by default, color output is enabled depending on the terminal
4948
- `PULUMI_ACCESS_TOKEN` - The Pulumi access token to use for authentication to Pulumi Cloud; see `DEFANG_PULUMI_BACKEND`
5049
- `PULUMI_CONFIG_PASSPHRASE` - Passphrase used to generate a unique key for your stack, and configuration and encrypted state values

src/README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ The Defang CLI recognizes the following environment variables:
4444
- `DEFANG_PULUMI_BACKEND` - The Pulumi backend URL or `"pulumi-cloud"`; defaults to a self-hosted backend
4545
- `DEFANG_PULUMI_DIR` - Run Pulumi from this folder, instead of spawning a cloud task; requires `--debug` (BYOC only)
4646
- `DEFANG_PULUMI_VERSION` - Override the version of the Pulumi image to use (`aws` provider only)
47-
- `DEFANG_TENANT` - The name of the tenant to use.
4847
- `NO_COLOR` - If set to any value, disables color output; by default, color output is enabled depending on the terminal
4948
- `PULUMI_ACCESS_TOKEN` - The Pulumi access token to use for authentication to Pulumi Cloud; see `DEFANG_PULUMI_BACKEND`
5049
- `PULUMI_CONFIG_PASSPHRASE` - Passphrase used to generate a unique key for your stack, and configuration and encrypted state values

src/cmd/cli/command/commands.go

Lines changed: 36 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -52,19 +52,18 @@ var (
5252
cluster string
5353
colorMode = ColorAuto
5454
sourcePlatform = migrate.SourcePlatformUnspecified // default to auto-detecting the source platform
55-
doDebug = false
55+
doDebug = pkg.GetenvBool("DEFANG_DEBUG")
5656
hasTty = term.IsTerminal() && !pkg.GetenvBool("CI")
5757
hideUpdate = pkg.GetenvBool("DEFANG_HIDE_UPDATE")
5858
mode = Mode(defangv1.DeploymentMode_MODE_UNSPECIFIED)
5959
modelId = os.Getenv("DEFANG_MODEL_ID") // for Pro users only
6060
nonInteractive = !hasTty
61-
org string
62-
tenantFlag string
61+
org = os.Getenv("DEFANG_ORG")
6362
providerID = cliClient.ProviderID(pkg.Getenv("DEFANG_PROVIDER", "auto"))
64-
verbose = false
63+
verbose = pkg.GetenvBool("DEFANG_VERBOSE")
6564
)
6665

67-
func getCluster() string {
66+
func getOrgCluster() string {
6867
if org == "" {
6968
return cluster
7069
}
@@ -165,12 +164,11 @@ func SetupCommands(ctx context.Context, version string) {
165164
RootCmd.PersistentFlags().Var(&colorMode, "color", fmt.Sprintf(`colorize output; one of %v`, allColorModes))
166165
RootCmd.PersistentFlags().StringVarP(&cluster, "cluster", "s", pcluster.DefangFabric, "Defang cluster to connect to")
167166
RootCmd.PersistentFlags().MarkHidden("cluster")
168-
RootCmd.PersistentFlags().StringVar(&org, "org", os.Getenv("DEFANG_ORG"), "override GitHub organization name (tenant)")
169-
RootCmd.PersistentFlags().StringVar(&tenantFlag, "tenant", "", "select tenant by name")
167+
RootCmd.PersistentFlags().StringVar(&org, "org", "", "override organization name (tenant)")
170168
RootCmd.PersistentFlags().VarP(&providerID, "provider", "P", fmt.Sprintf(`bring-your-own-cloud provider; one of %v`, cliClient.AllProviders()))
171169
// RootCmd.Flag("provider").NoOptDefVal = "auto" NO this will break the "--provider aws"
172170
RootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "verbose logging") // backwards compat: only used by tail
173-
RootCmd.PersistentFlags().BoolVar(&doDebug, "debug", pkg.GetenvBool("DEFANG_DEBUG"), "debug logging for troubleshooting the CLI")
171+
RootCmd.PersistentFlags().BoolVar(&doDebug, "debug", false, "debug logging for troubleshooting the CLI")
174172
RootCmd.PersistentFlags().BoolVar(&dryrun.DoDryRun, "dry-run", false, "dry run (don't actually change anything)")
175173
RootCmd.PersistentFlags().BoolVarP(&nonInteractive, "non-interactive", "T", !hasTty, "disable interactive prompts / no TTY")
176174
RootCmd.PersistentFlags().StringP("project-name", "p", "", "project name")
@@ -372,19 +370,10 @@ var RootCmd = &cobra.Command{
372370
}
373371
}
374372

375-
// Configure tenant selection based on --tenant flag
376-
if f := cmd.Root().Flag("tenant"); f != nil && f.Changed {
377-
// Highest precedence: explicit --tenant flag
378-
auth.SetSelectedTenantName(tenantFlag)
379-
} else if envTenant := os.Getenv("DEFANG_TENANT"); strings.TrimSpace(envTenant) != "" {
380-
// Next precedence: DEFANG_TENANT environment variable
381-
auth.SetSelectedTenantName(envTenant)
382-
} else {
383-
// Default behavior: auto-select tenant by JWT subject if no explicit name is provided
384-
auth.SetAutoSelectBySub(true)
385-
}
373+
// Configure tenant selection based on --org flag
374+
auth.SetSelectedTenantName(org)
386375

387-
client, err = cli.Connect(ctx, getCluster())
376+
client, err = cli.Connect(ctx, getOrgCluster())
388377

389378
if v, err := client.GetVersions(ctx); err == nil {
390379
version := cmd.Root().Version // HACK to avoid circular dependency with RootCmd
@@ -405,20 +394,20 @@ var RootCmd = &cobra.Command{
405394
if nonInteractive {
406395
err = client.CheckLoginAndToS(ctx)
407396
} else {
408-
err = login.InteractiveRequireLoginAndToS(ctx, client, getCluster())
397+
err = login.InteractiveRequireLoginAndToS(ctx, client, getOrgCluster())
409398
}
410399

411400
if err != nil {
412401
return err
413402
}
414403

415404
// Ensure tenant is resolved post-login as we now have a token
416-
if tok := pcluster.GetExistingToken(getCluster()); tok != "" {
405+
if tok := pcluster.GetExistingToken(getOrgCluster()); tok != "" {
417406
if err2 := auth.ResolveAndSetTenantFromToken(ctx, tok); err2 != nil {
418407
return err2
419408
}
420409
// log the tenant name and id
421-
term.Debug("Selected tenant:", auth.GetSelectedTenantName(), "(", auth.GetSelectedTenantID(), ")")
410+
term.Debugf("Selected tenant: %q (%s)", auth.GetSelectedTenantName(), auth.GetSelectedTenantID())
422411
}
423412

424413
return nil
@@ -427,63 +416,49 @@ var RootCmd = &cobra.Command{
427416

428417
var tenantsCmd = &cobra.Command{
429418
Use: "tenants",
419+
Aliases: []string{"tenant", "orgs", "org"},
430420
Args: cobra.NoArgs,
431421
Annotations: authNeededAnnotation,
432422
Short: "List tenants available to the logged-in user",
433423
RunE: func(cmd *cobra.Command, args []string) error {
434424
ctx := cmd.Context()
435-
tok := pcluster.GetExistingToken(getCluster())
436-
if strings.TrimSpace(tok) == "" {
437-
return errors.New("not logged in; run 'defang login'")
438-
}
439425

426+
tok := pcluster.GetExistingToken(getOrgCluster())
440427
tenants, err := auth.ListTenantsFromToken(ctx, tok)
441428
if err != nil {
442429
return err
443430
}
444431

432+
if len(tenants) == 0 {
433+
term.Warn("No tenants found")
434+
return nil
435+
}
436+
445437
// Sort by name for stable output
446-
slices.SortFunc(tenants, func(a, b auth.Tenant) int {
438+
slices.SortStableFunc(tenants, func(a, b auth.Tenant) int {
447439
return strings.Compare(strings.ToLower(a.Name), strings.ToLower(b.Name))
448440
})
449441

450-
if len(tenants) == 0 {
451-
term.Info("No tenants found")
452-
return nil
453-
}
442+
printTenants := make([]struct {
443+
Active string
444+
auth.Tenant
445+
}, len(tenants))
454446

455447
currentID := auth.GetSelectedTenantID()
456448
currentName := auth.GetSelectedTenantName()
457-
458-
// Compute longest name for aligned output
459-
maxNameLen := 0
460-
for _, t := range tenants {
461-
if l := len(t.Name); l > maxNameLen {
462-
maxNameLen = l
463-
}
464-
}
465-
466-
for _, t := range tenants {
449+
for i, t := range tenants {
450+
printTenants[i].Tenant = t
467451
selected := t.ID == currentID || (currentID == "" && t.Name == currentName && strings.TrimSpace(currentName) != "")
468-
marker := "-"
469452
if selected {
470-
marker = "*" // highlight selected
471-
}
472-
473-
var line string
474-
if verbose {
475-
line = fmt.Sprintf("%s %-*s (%s)\n", marker, maxNameLen, t.Name, t.ID)
476-
} else {
477-
line = fmt.Sprintf("%s %s\n", marker, t.Name)
453+
printTenants[i].Active = "*" // highlight selected
478454
}
455+
}
479456

480-
if selected {
481-
term.Printc(term.BrightCyan, line)
482-
} else {
483-
term.Printc(term.InfoColor, line)
484-
}
457+
attrs := []string{"Active", "Name"}
458+
if verbose {
459+
attrs = append(attrs, "ID")
485460
}
486-
return nil
461+
return term.Table(printTenants, attrs)
487462
},
488463
}
489464

@@ -495,11 +470,11 @@ var loginCmd = &cobra.Command{
495470
trainingOptOut, _ := cmd.Flags().GetBool("training-opt-out")
496471

497472
if nonInteractive {
498-
if err := login.NonInteractiveGitHubLogin(cmd.Context(), client, getCluster()); err != nil {
473+
if err := login.NonInteractiveGitHubLogin(cmd.Context(), client, getOrgCluster()); err != nil {
499474
return err
500475
}
501476
} else {
502-
err := login.InteractiveLogin(cmd.Context(), client, getCluster())
477+
err := login.InteractiveLogin(cmd.Context(), client, getOrgCluster())
503478
if err != nil {
504479
return err
505480
}
@@ -616,7 +591,7 @@ var generateCmd = &cobra.Command{
616591
Heroku: migrate.NewHerokuClient(),
617592
ModelID: modelId,
618593
Fabric: client,
619-
Cluster: getCluster(),
594+
Cluster: getOrgCluster(),
620595
}
621596

622597
sample := ""
@@ -644,7 +619,7 @@ var initCmd = &cobra.Command{
644619
Heroku: migrate.NewHerokuClient(),
645620
ModelID: modelId,
646621
Fabric: client,
647-
Cluster: getCluster(),
622+
Cluster: getOrgCluster(),
648623
}
649624

650625
if len(args) > 0 {

src/cmd/cli/command/mcp.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,15 +76,15 @@ set_config - This tool sets or updates configuration variables for a deployed ap
7676

7777
// Setup tools
7878
term.Debug("Setting up tools")
79-
tools.SetupTools(s, getCluster(), authPort, providerID)
79+
tools.SetupTools(s, getOrgCluster(), authPort, providerID)
8080

8181
// Start auth server for docker login flow
8282
if authPort != 0 {
8383
term.Debug("Starting Auth Server for MCP-in-Docker login flow")
8484
term.Debug("Function invoked: cli.InteractiveLoginInsideDocker")
8585

8686
go func() {
87-
if err := login.InteractiveLoginInsideDocker(cmd.Context(), getCluster(), authPort); err != nil {
87+
if err := login.InteractiveLoginInsideDocker(cmd.Context(), getOrgCluster(), authPort); err != nil {
8888
term.Error("Failed to start auth server", "error", err)
8989
}
9090
}()

src/go.mod

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ go 1.24
44

55
toolchain go1.24.5
66

7-
replace github.com/spf13/cobra v1.8.0 => github.com/DefangLabs/cobra v1.8.0-defang
7+
replace github.com/spf13/cobra v1.10.1 => github.com/DefangLabs/cobra v1.10.1-defang
88

99
require (
1010
cloud.google.com/go/artifactregistry v1.16.1
@@ -50,8 +50,8 @@ require (
5050
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
5151
github.com/ross96D/cancelreader v0.2.6
5252
github.com/sirupsen/logrus v1.9.3
53-
github.com/spf13/cobra v1.8.0
54-
github.com/spf13/pflag v1.0.6
53+
github.com/spf13/cobra v1.10.1
54+
github.com/spf13/pflag v1.0.9
5555
github.com/stretchr/testify v1.10.0
5656
go.yaml.in/yaml/v3 v3.0.4
5757
golang.org/x/mod v0.18.0

src/go.sum

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkk
3636
github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
3737
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
3838
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
39-
github.com/DefangLabs/cobra v1.8.0-defang h1:rTzAg1XbEk3yXUmQPumcwkLgi8iNCby5CjyG3sCwzKk=
40-
github.com/DefangLabs/cobra v1.8.0-defang/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
39+
github.com/DefangLabs/cobra v1.10.1-defang h1:Jsj/7J/hcEVnOnRB/qyNQgZY8pjAONfhHntw3w+UwQA=
40+
github.com/DefangLabs/cobra v1.10.1-defang/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
4141
github.com/DefangLabs/secret-detector v0.0.0-20250811234530-d4b4214cd679 h1:qNT7R4qrN+5u5ajSbqSW1opHP4LA8lzA+ASyw5MQZjs=
4242
github.com/DefangLabs/secret-detector v0.0.0-20250811234530-d4b4214cd679/go.mod h1:blbwPQh4DTlCZEfk1BLU4oMIhLda2U+A840Uag9DsZw=
4343
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.26.0 h1:f2Qw/Ehhimh5uO1fayV0QIW7DShEQqhtUfhYc+cBPlw=
@@ -292,8 +292,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ
292292
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
293293
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
294294
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
295-
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
296-
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
295+
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
296+
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
297297
github.com/spiffe/go-spiffe/v2 v2.5.0 h1:N2I01KCUkv1FAjZXJMwh95KK1ZIQLYbPfhaxw8WS0hE=
298298
github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g=
299299
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=

0 commit comments

Comments
 (0)