feat: support project-level profile binding#1506
Conversation
|
Note Currently processing new changes in this PR. This may take a few minutes, please wait... ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (22)
📝 WalkthroughWalkthroughAdds project-local CLI profile binding via a ChangesProject-level profile binding
Sequence Diagram(s)sequenceDiagram
participant CLI as User / CLI args
participant Bootstrap as BootstrapInvocationContext
participant Core as core.ResolveProjectProfile
participant Factory as cmdutil.NewDefault
participant Provider as DefaultAccountProvider.ResolveAccount
participant MultiAppConfig
CLI->>Bootstrap: parse args + global flags
alt --profile flag set
Bootstrap-->>Factory: InvocationContext{ProfileSource: cli}
else no --profile flag
Bootstrap->>Core: ResolveProjectProfile (walk to Git root)
Core-->>Bootstrap: ProjectProfileBinding{profile, path} or nil
Bootstrap-->>Factory: InvocationContext{ProfileSource: project|global, ProfileConfigPath}
end
Factory->>Provider: NewDefaultAccountProviderWithSource(source, configPath)
Provider->>MultiAppConfig: LoadMultiAppConfig
alt ProfileSourceProject and profile missing
Provider-->>Factory: ProjectProfileNotFoundError
else profile found
Provider-->>Factory: Account
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds project-local profile binding support and improves error classification around “active profile” resolution so commands fail closed (with actionable hints) when a project-selected profile doesn’t exist in the global config.
Changes:
- Introduces project config discovery + read/write helpers (
.lark-cli/config.json) and corresponding tests. - Adds
profile current,profile bind, andprofile unbindcommands plus invocation context fields (ProfileSource,ProfileConfigPath). - Updates credential/config/auth flows to return project-specific “profile not found” errors early.
Reviewed changes
Copilot reviewed 22 out of 22 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| internal/credential/integration_test.go | Adds an integration test asserting “missing project profile” fails closed with *core.ConfigError. |
| internal/credential/default_provider.go | Adds ProfileSource/ProfileConfigPath to the default account provider and returns a project-specific error when the bound profile is missing. |
| internal/core/project_config_test.go | Adds unit tests for locating, reading, writing, and removing project profile bindings. |
| internal/core/project_config.go | Implements project config discovery (bounded by Git root), read/write, removal, and a typed “project profile not found” error. |
| internal/core/config.go | Adds ProfileSource enum to track where a profile selection originates (cli/project/global). |
| internal/cmdutil/factory_default.go | Threads ProfileSource and ProfileConfigPath into credential provider construction. |
| internal/cmdutil/factory.go | Extends InvocationContext with ProfileSource and ProfileConfigPath. |
| internal/cmdutil/active_profile.go | Centralizes “active profile missing” classification, including project-bound missing profile errors. |
| cmd/profile/project_bind.go | Adds profile bind/profile unbind commands and writes/removes .lark-cli/config.json. |
| cmd/profile/profile_test.go | Adds tests for bind/unbind/current behavior and for error typing/hints across sources. |
| cmd/profile/profile.go | Registers the new profile subcommands. |
| cmd/profile/current.go | Adds profile current command outputting effective profile + source + config path as JSON. |
| cmd/config/strict_mode.go | Switches to the unified noActiveProfileError (source-aware) flow. |
| cmd/config/show.go | Switches to the unified noActiveProfileError (source-aware) flow. |
| cmd/config/default_as.go | Switches to the unified noActiveProfileError (source-aware) flow. |
| cmd/config/active_profile.go | Adds helper delegating to cmdutil.ActiveProfileError. |
| cmd/bootstrap_test.go | Adds tests for bootstrap resolving project profiles, overrides, and malformed-project-config skip rules. |
| cmd/bootstrap.go | Bootstraps invocation context from CLI flag or project binding (unless explicitly skipped). |
| cmd/auth/logout_test.go | Adds a test ensuring project-bound missing profiles fail closed even when global config is missing. |
| cmd/auth/logout.go | Adds early project-profile validation for auth logout. |
| cmd/auth/list_test.go | Adds a test ensuring project-bound missing profiles fail closed even when global config is missing. |
| cmd/auth/list.go | Adds early project-profile validation for auth list. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| Use: "bind <profile>", | ||
| Short: "Bind the current project to a profile", | ||
| Args: cobra.ExactArgs(1), |
| func profileBindRun(f *cmdutil.Factory, profile string) error { | ||
| profile = strings.TrimSpace(profile) | ||
| if err := core.ValidateProfileName(profile); err != nil { | ||
| return errs.NewValidationError(errs.SubtypeInvalidArgument, "%v", err).WithParam("<profile>").WithCause(err) | ||
| } | ||
| multi, err := core.LoadOrNotConfigured() | ||
| if err != nil { | ||
| return err | ||
| } | ||
| app := multi.FindApp(profile) | ||
| if app == nil { | ||
| return errs.NewValidationError(errs.SubtypeInvalidArgument, "profile %q not found", profile). | ||
| WithHint("available profiles: %s", formatProfileNameList(multi.ProfileNames())). | ||
| WithParam("<profile>") |
| func ProjectConfigWritePath(startDir string) (string, error) { | ||
| if path, ok, err := FindProjectConfigPath(startDir); err != nil || ok { | ||
| return path, err | ||
| } | ||
| root := findGitRoot(startDir) | ||
| if root == "" { | ||
| root = filepath.Clean(startDir) | ||
| } | ||
| return ProjectConfigPath(root), nil | ||
| } |
| wantMsg := `profile "missing" is configured by project but not found` | ||
| wantHint := "project config: " + projectPath + "; run: lark-cli profile list; available profiles: bytedance, team-prod, lark-boe" | ||
| if cfgErr.Code != 3 || cfgErr.Type != "config" || cfgErr.Message != wantMsg || cfgErr.Hint != wantHint { | ||
| t.Fatalf("ConfigError = %#v", cfgErr) | ||
| } |
| func writeBootstrapProjectConfig(t *testing.T, dir, body string) string { | ||
| t.Helper() | ||
| path := core.ProjectConfigPath(dir) | ||
| if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil { | ||
| t.Fatalf("MkdirAll(project config dir): %v", err) | ||
| } | ||
| if err := os.WriteFile(path, []byte(body), 0600); err != nil { | ||
| t.Fatalf("WriteFile(project config): %v", err) | ||
| } | ||
| return path | ||
| } |
02da6d9 to
5045e0b
Compare
Summary
Adds project-local profile binding via
.lark-cli/config.json, so commands can automatically use the profile selected for the current repository without requiring--profileon every invocation. Explicit--profilestill takes precedence, and the project file stores only the profile name, not credentials or login state.Changes
profile bind <profile>,profile unbind, andprofile current..lark-cli/config.jsonduring bootstrap, stopping at the Git root.bytedance,team-prod, andlark-boeprofile names.Test Plan
go test ./cmd ./cmd/auth ./cmd/profile ./cmd/config ./internal/cmdutil ./internal/credential ./internal/corego vet ./...gofmt -l .go mod tidy && git diff --exit-code -- go.mod go.sumgo run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.1.6 run --new-from-rev=origin/main(0 issues; existing gocritic disabled-check warning only)make buildprofile bind team-prod,profile current,--profile bytedance profile current, andprofile unbindmake unit-testcurrently fails in existingshortcuts/minutesdownload tests becauseexample.compresigned download URLs are blocked as local/internal hosts; related packages and all new project-profile tests pass.Related Issues
中文说明
这个 PR 增加项目级 profile 绑定能力。项目可以通过
.lark-cli/config.json记录当前仓库默认使用的 profile,之后普通命令不需要重复传--profile;显式传入--profile时仍然优先使用命令行参数。项目配置文件只保存 profile 名称,不保存 app secret、token 或登录态。示例里包含
bytedance、team-prod、lark-boe这类租户/环境命名,用来说明 profile 名称本身可以承载团队内部环境语义。Summary by CodeRabbit
New Features
profile bindcommand to associate a profile with a project repository.profile unbindcommand to remove project profile bindings.profile currentcommand to display the currently active profile configuration..lark-cli/config.json.Improvements