Skip to content

feat: support project-level profile binding#1506

Open
ennann wants to merge 1 commit into
larksuite:mainfrom
ennann:feat/project-profile-config
Open

feat: support project-level profile binding#1506
ennann wants to merge 1 commit into
larksuite:mainfrom
ennann:feat/project-profile-config

Conversation

@ennann

@ennann ennann commented Jun 17, 2026

Copy link
Copy Markdown

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 --profile on every invocation. Explicit --profile still takes precedence, and the project file stores only the profile name, not credentials or login state.

Changes

  • Add profile bind <profile>, profile unbind, and profile current.
  • Resolve the nearest .lark-cli/config.json during bootstrap, stopping at the Git root.
  • Fail closed with typed config errors for malformed project config files and missing project profiles.
  • Surface project-profile-not-found errors through auth/config/credential paths instead of silently falling back.
  • Keep project config minimal and future-extensible; examples cover bytedance, team-prod, and lark-boe profile names.

Test Plan

  • go test ./cmd ./cmd/auth ./cmd/profile ./cmd/config ./internal/cmdutil ./internal/credential ./internal/core
  • go vet ./...
  • gofmt -l .
  • go mod tidy && git diff --exit-code -- go.mod go.sum
  • go 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 build
  • Manual local verification with a temporary config and Git repo: profile bind team-prod, profile current, --profile bytedance profile current, and profile unbind
  • make unit-test currently fails in existing shortcuts/minutes download tests because example.com presigned download URLs are blocked as local/internal hosts; related packages and all new project-profile tests pass.

Related Issues

  • None

中文说明

这个 PR 增加项目级 profile 绑定能力。项目可以通过 .lark-cli/config.json 记录当前仓库默认使用的 profile,之后普通命令不需要重复传 --profile;显式传入 --profile 时仍然优先使用命令行参数。

项目配置文件只保存 profile 名称,不保存 app secret、token 或登录态。示例里包含 bytedanceteam-prodlark-boe 这类租户/环境命名,用来说明 profile 名称本身可以承载团队内部环境语义。

Summary by CodeRabbit

  • New Features

    • Added profile bind command to associate a profile with a project repository.
    • Added profile unbind command to remove project profile bindings.
    • Added profile current command to display the currently active profile configuration.
    • Project-level profile configuration now supported via .lark-cli/config.json.
  • Improvements

    • Enhanced profile selection to automatically use project-scoped profiles when available.
    • Improved error messages for missing or misconfigured profiles with actionable hints.

@ennann ennann requested a review from liangshuo-1 as a code owner June 17, 2026 11:10
Copilot AI review requested due to automatic review settings June 17, 2026 11:10
@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d0f95dce-eb55-495c-8ad0-005b30b94990

📥 Commits

Reviewing files that changed from the base of the PR and between 02da6d9 and 5045e0b.

📒 Files selected for processing (22)
  • cmd/auth/list.go
  • cmd/auth/list_test.go
  • cmd/auth/logout.go
  • cmd/auth/logout_test.go
  • cmd/bootstrap.go
  • cmd/bootstrap_test.go
  • cmd/config/active_profile.go
  • cmd/config/default_as.go
  • cmd/config/show.go
  • cmd/config/strict_mode.go
  • cmd/profile/current.go
  • cmd/profile/profile.go
  • cmd/profile/profile_test.go
  • cmd/profile/project_bind.go
  • internal/cmdutil/active_profile.go
  • internal/cmdutil/factory.go
  • internal/cmdutil/factory_default.go
  • internal/core/config.go
  • internal/core/project_config.go
  • internal/core/project_config_test.go
  • internal/credential/default_provider.go
  • internal/credential/integration_test.go
 _____________________
< Git gud or git out. >
 ---------------------
  \
   \   \
        \ /\
        ( )
      .( o ).
📝 Walkthrough

Walkthrough

Adds project-local CLI profile binding via a .lark-cli/config.json file anchored at the nearest Git root. Introduces ProfileSource tracking through InvocationContext, the credential provider, and a new cmdutil error-classification layer. Adds profile bind, profile unbind, and profile current subcommands, and updates config and auth commands to emit context-aware profile errors.

Changes

Project-level profile binding

Layer / File(s) Summary
ProfileSource type and project config storage/resolution
internal/core/config.go, internal/core/project_config.go, internal/core/project_config_test.go
Defines ProfileSource string type with cli/project/global constants. Implements full project config CRUD in project_config.go: Git-root detection, upward-search path utilities (FindProjectConfigPath, ProjectConfigWritePath), LoadProjectConfig, SaveProjectConfig, RemoveProjectProfile, and ProjectProfileNotFoundError. Tests cover all storage behaviors including fail-closed JSON validation, field preservation, and directory cleanup on last-field removal.
InvocationContext expansion and active profile error helpers
internal/cmdutil/factory.go, internal/cmdutil/active_profile.go, cmd/config/active_profile.go
Adds ProfileSource and ProfileConfigPath fields to InvocationContext. Introduces ProjectProfileError and ActiveProfileError in cmdutil to classify missing-profile errors by source. Adds a thin noActiveProfileError wrapper for use in cmd/config.
Bootstrap: project profile resolution at startup
cmd/bootstrap.go, cmd/bootstrap_test.go
BootstrapInvocationContext resolves a project-backed profile from disk via core.ResolveProjectProfile, populates ProfileSource/ProfileConfigPath, and introduces skipProjectProfileLookup to bypass resolution for help/completion and specific subcommands. Tests cover project loading, CLI flag override, per-subcommand malformed config handling, and completion skip rules.
Credential provider: profile-source-aware resolution
internal/cmdutil/factory_default.go, internal/credential/default_provider.go, internal/credential/integration_test.go
Threads ProfileSource/ProfileConfigPath from InvocationContext through credentialDeps into buildCredentialProvider, which switches to NewDefaultAccountProviderWithSource. ResolveAccount gains a guard returning ProjectProfileNotFoundError when a project-sourced profile is absent from the loaded config. Integration test verifies fail-closed behavior.
profile bind, unbind, and current subcommands
cmd/profile/project_bind.go, cmd/profile/current.go, cmd/profile/profile.go, cmd/profile/profile_test.go
Adds profile bind <name> and profile unbind for writing/removing project config bindings with typed validation errors. Adds profile current outputting effective profile JSON (name, source, config path, app ID, brand). All three registered on the profile root. Tests cover filesystem effects, typed error shapes, and current-output for CLI vs project source.
config and auth command profile-aware error handling
cmd/config/default_as.go, cmd/config/show.go, cmd/config/strict_mode.go, cmd/auth/list.go, cmd/auth/logout.go, cmd/auth/list_test.go, cmd/auth/logout_test.go
config default-as, show, and strict-mode replace core.NoActiveProfileError() calls with noActiveProfileError(f, multi). auth list and auth logout add early ProjectProfileError guards. Tests assert *core.ConfigError fields for the project-profile-missing case.

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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested labels

domain/base, size/XL, feature

Suggested reviewers

  • liangshuo-1
  • evandance

Poem

🐇 A rabbit found a .lark-cli file,
Tucked at the git root, just a small profile.
bind, unbind, current — three commands appear,
So each project knows which profile is near.
No more "not configured" to muddle the day —
The source is now tracked all the way! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 24.36% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: support project-level profile binding' accurately and concisely describes the main feature added in this PR—the ability to bind profiles at the project level.
Description check ✅ Passed The PR description is comprehensive and follows the required template structure with Summary, Changes, Test Plan, and Related Issues sections. It includes detailed implementation notes and both English and Chinese explanations.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added the size/L Large or sensitive change across domains or core paths label Jun 17, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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, and profile unbind commands 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.

Comment on lines +22 to +24
Use: "bind <profile>",
Short: "Bind the current project to a profile",
Args: cobra.ExactArgs(1),
Comment on lines +47 to +60
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>")
Comment on lines +94 to +103
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
}
Comment on lines +265 to +269
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)
}
Comment thread cmd/bootstrap_test.go
Comment on lines +16 to +26
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
}
@ennann ennann force-pushed the feat/project-profile-config branch from 02da6d9 to 5045e0b Compare June 17, 2026 11:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/L Large or sensitive change across domains or core paths

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants