Skip to content

feat: add multi-profile credential support#7

Merged
Palbahngmiyine merged 5 commits intomainfrom
worktree-kind-pondering-tide
Apr 15, 2026
Merged

feat: add multi-profile credential support#7
Palbahngmiyine merged 5 commits intomainfrom
worktree-kind-pondering-tide

Conversation

@Palbahngmiyine
Copy link
Copy Markdown
Member

Summary

  • nurigo/solapi-crm-core CLI와 동일한 멀티 프로필 credential 옵션 지원 추가
  • --profile persistent flag, configure list/use/delete 서브커맨드 구현
  • 기존 flat format({api_key, api_secret}) → 멀티프로필 format 자동 마이그레이션 (하위 호환)

Changes

New files

  • cmd/configure_list.gosolactl configure list 프로필 목록 표시
  • cmd/configure_use.gosolactl configure use <profile> 활성 프로필 전환
  • cmd/configure_delete.gosolactl configure delete <profile> 프로필 삭제

Modified files

  • pkg/config/config.goCredentialsFile, LoadOptions, ProfileInfo 구조체 추가, Load/Save 시그니처 변경, 프로필 관리 함수(SetActiveProfile, DeleteProfile, ListProfiles, ActiveProfileName), detectAndLoad 하위 호환 마이그레이션
  • cmd/root.go--profile persistent flag 추가, loadConfig()LoadOptions 사용
  • cmd/configure.go — 프로필 인식 runConfigure/saveConfigure, 진단 메시지 errOut() 준수
  • cmd/configure_show.go — Profile 이름 표시, LoadOptions 전달
  • pkg/config/config_test.go — 기존 테스트 시그니처 업데이트 + 신규 프로필 테스트 20개 + fuzz 테스트
  • cmd/configure_test.go — 신규 서브커맨드 테스트 12개

Test plan

  • go build ./... 컴파일 성공
  • go test ./... 전체 테스트 통과
  • go test -race ./... race condition 없음
  • go vet ./... 경고 없음
  • solactl configure --profile staging --api-key K --api-secret S 프로필 저장
  • solactl configure list 프로필 목록 확인
  • solactl configure use staging 활성 프로필 전환
  • solactl configure delete staging 프로필 삭제
  • old format 파일 → solactl configure show 자동 마이그레이션 확인

🤖 Generated with Claude Code

Add multi-profile credential management matching solapi-crm-core CLI
options. Users can now store and switch between multiple API key/secret
pairs using named profiles.

New features:
- `--profile` persistent flag for all commands
- `solactl configure list` to show saved profiles
- `solactl configure use <profile>` to switch active profile
- `solactl configure delete <profile>` to remove a profile
- Backward-compatible auto-migration from old flat credential format
- Profile-aware `configure` and `configure show` commands

File format changed from flat `{api_key, api_secret}` to multi-profile
`{profiles: {...}, active_profile: "..."}` with automatic detection
and migration of the old format.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces multi-profile support for the CLI configuration, allowing users to manage multiple sets of API credentials. Key changes include the addition of list, use, and delete subcommands under configure, a new persistent --profile flag, and an updated storage format in credentials.json that maintains backward compatibility with the previous flat structure. Feedback focuses on strengthening error handling: specifically, preventing potential data loss in the Save function when the configuration file is corrupted and improving the specificity of error messages in the list command to distinguish between a missing file and other I/O or parsing errors.

Comment thread pkg/config/config.go Outdated
profileName = DefaultProfile
}

cf, _ := loadCredentialsFile()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The error from loadCredentialsFile() is being ignored. If the credentials file exists but is corrupted (e.g., invalid JSON), loadCredentialsFile() will return an error and a nil configuration. By ignoring this error, the Save function will proceed to initialize a fresh CredentialsFile and overwrite the existing file, potentially causing data loss of all other stored profiles. You should check if the error is something other than 'file not found' before proceeding.

Suggested change
cf, _ := loadCredentialsFile()
cf, err := loadCredentialsFile()
if err != nil && !os.IsNotExist(err) {
return fmt.Errorf("설정 파일 로드 실패: %w", err)
}

Comment thread cmd/configure_list.go
package cmd

import (
"fmt"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The os package needs to be imported to check for file existence errors using os.IsNotExist.

Suggested change
"fmt"
"fmt"
"os"

Comment thread cmd/configure_list.go
Comment on lines +17 to +20
if err != nil {
_, _ = fmt.Fprintf(errOut(), "프로필이 없습니다. 'solactl configure'를 실행하세요.\n")
return nil
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The current implementation swallows all errors from config.ListProfiles() and provides a generic "No profiles" message. If the configuration file is corrupted or unreadable due to permission issues, this message is misleading and the command incorrectly returns a success exit code (0). It is better to specifically check for the 'file not found' case and return other errors to the caller.

if err != nil {
	if os.IsNotExist(err) {
		_, _ = fmt.Fprintf(errOut(), "프로필이 없습니다. 'solactl configure'를 실행하세요.\n")
		return nil
	}
	return err
}

Palbahngmiyine and others added 4 commits April 15, 2026 17:57
- Fix nil profile pointer dereference (null value in JSON map)
- Fix Load precedence: don't short-circuit on missing profile before
  applying env vars and CLI flag overrides
- Fix Save error handling: propagate parse/permission errors instead of
  silently overwriting (only treat file-not-exist as empty state)
- Fix detectAndLoad: infer active profile from available profiles
  instead of hardcoding "default" when active_profile is missing
- Add atomic file writes (temp file + rename) for crash safety
- Add profile name validation (alphanumeric, _, -, max 64 chars)
- Fix configure list: distinguish file-not-found from other errors
- Add nil profile guard in ListProfiles and mergeConfig
- Add tests: nil profile, missing profile with env/flag overrides,
  corrupt file rejection, profile name validation, inferActiveProfile

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix inferActiveProfile to skip nil profile values (not just missing keys)
- Fix temp file leak when os.Rename fails in saveCredentialsFile
- Fix gofmt formatting in config_test.go

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add stderr warning when --profile references a non-existent profile
  so users are not silently redirected to env/flag credentials
- Document the unlocked read-modify-write limitation in Save godoc

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prevent setting a null profile value as active or failing to reject
deletion of a null profile entry. Consistent with inferActiveProfile
nil-value handling from round 2.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Palbahngmiyine Palbahngmiyine merged commit e11b0cc into main Apr 15, 2026
2 checks passed
@Palbahngmiyine Palbahngmiyine deleted the worktree-kind-pondering-tide branch April 15, 2026 09:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant