From 8abd39b18e9956aebb2f4a4bc53d700967d7ec1a Mon Sep 17 00:00:00 2001 From: luozhixiong Date: Mon, 6 Jul 2026 17:06:12 +0800 Subject: [PATCH] feat: add LARKSUITE_CLI_PROFILE session env with flag precedence Add LARKSUITE_CLI_PROFILE env var and make BootstrapInvocationContext fall back to it when --profile is empty, so downstream credential resolution sees the correct profile. Also track whether the resolved profile came from the flag or the env fallback via a new InvocationContext.ProfileFromFlag field, needed by a later task to report the correct credential source. --- cmd/bootstrap.go | 12 +++++++++- cmd/bootstrap_test.go | 48 ++++++++++++++++++++++++++++++++++++- internal/cmdutil/factory.go | 5 ++++ internal/envvars/envvars.go | 1 + 4 files changed, 64 insertions(+), 2 deletions(-) diff --git a/cmd/bootstrap.go b/cmd/bootstrap.go index 841a88409..2ad447bf1 100644 --- a/cmd/bootstrap.go +++ b/cmd/bootstrap.go @@ -6,8 +6,10 @@ package cmd import ( "errors" "io" + "os" "github.com/larksuite/cli/internal/cmdutil" + "github.com/larksuite/cli/internal/envvars" "github.com/spf13/pflag" ) @@ -26,5 +28,13 @@ func BootstrapInvocationContext(args []string) (cmdutil.InvocationContext, error if err := fs.Parse(args); err != nil && !errors.Is(err, pflag.ErrHelp) { return cmdutil.InvocationContext{}, err } - return cmdutil.InvocationContext{Profile: globals.Profile}, nil + + profileFromFlag := globals.Profile != "" + if !profileFromFlag { + globals.Profile = os.Getenv(envvars.CliProfile) + } + return cmdutil.InvocationContext{ + Profile: globals.Profile, + ProfileFromFlag: profileFromFlag, + }, nil } diff --git a/cmd/bootstrap_test.go b/cmd/bootstrap_test.go index aa5fd3de7..659714ec3 100644 --- a/cmd/bootstrap_test.go +++ b/cmd/bootstrap_test.go @@ -3,7 +3,11 @@ package cmd -import "testing" +import ( + "testing" + + "github.com/larksuite/cli/internal/envvars" +) func TestBootstrapInvocationContext_ProfileFlag(t *testing.T) { inv, err := BootstrapInvocationContext([]string{"--profile", "target", "auth", "status"}) @@ -70,3 +74,45 @@ func TestBootstrapInvocationContext_HelpWithProfile(t *testing.T) { t.Fatalf("profile = %q, want %q", inv.Profile, "target") } } + +func TestBootstrapProfileEnvFallback(t *testing.T) { + t.Run("flag wins over env", func(t *testing.T) { + t.Setenv(envvars.CliProfile, "tenant_env") + inv, err := BootstrapInvocationContext([]string{"--profile", "tenant_flag", "whoami"}) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + if inv.Profile != "tenant_flag" { + t.Errorf("got %q, want tenant_flag", inv.Profile) + } + if !inv.ProfileFromFlag { + t.Errorf("ProfileFromFlag = false, want true") + } + }) + t.Run("env used when flag absent", func(t *testing.T) { + t.Setenv(envvars.CliProfile, "tenant_env") + inv, err := BootstrapInvocationContext([]string{"whoami"}) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + if inv.Profile != "tenant_env" { + t.Errorf("got %q, want tenant_env", inv.Profile) + } + if inv.ProfileFromFlag { + t.Errorf("ProfileFromFlag = true, want false") + } + }) + t.Run("empty when neither set", func(t *testing.T) { + t.Setenv(envvars.CliProfile, "") + inv, err := BootstrapInvocationContext([]string{"whoami"}) + if err != nil { + t.Fatalf("unexpected err: %v", err) + } + if inv.Profile != "" { + t.Errorf("got %q, want empty", inv.Profile) + } + if inv.ProfileFromFlag { + t.Errorf("ProfileFromFlag = true, want false") + } + }) +} diff --git a/internal/cmdutil/factory.go b/internal/cmdutil/factory.go index 1b167b786..c90fa1d4a 100644 --- a/internal/cmdutil/factory.go +++ b/internal/cmdutil/factory.go @@ -27,6 +27,11 @@ import ( // In tests, replace any field to stub out external dependencies. type InvocationContext struct { Profile string + // ProfileFromFlag is true when Profile was set via the --profile flag, + // and false when it came from the LARKSUITE_CLI_PROFILE env fallback + // (or neither was set). Downstream credential resolution uses this to + // report the correct profile source. + ProfileFromFlag bool } type Factory struct { diff --git a/internal/envvars/envvars.go b/internal/envvars/envvars.go index 7b4a23464..8b80c1685 100644 --- a/internal/envvars/envvars.go +++ b/internal/envvars/envvars.go @@ -10,6 +10,7 @@ const ( CliUserAccessToken = "LARKSUITE_CLI_USER_ACCESS_TOKEN" CliTenantAccessToken = "LARKSUITE_CLI_TENANT_ACCESS_TOKEN" CliDefaultAs = "LARKSUITE_CLI_DEFAULT_AS" + CliProfile = "LARKSUITE_CLI_PROFILE" CliStrictMode = "LARKSUITE_CLI_STRICT_MODE" // Sidecar proxy (auth proxy mode)