Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds dynamic API key retrieval functionality to CodeGPT, enabling users to fetch API keys from password managers or secret services via shell commands instead of storing them in plain text. The implementation includes file-based caching with configurable refresh intervals, comprehensive tests, and documentation updates.
Key Changes:
- New API key helper utility with shell command execution and file-based caching (15-minute default)
- Provider integration for OpenAI, Gemini, and Anthropic with fallback logic
- Configuration flags for helper commands and refresh intervals
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 13 comments.
Show a summary per file
| File | Description |
|---|---|
util/api_key_helper.go |
Core implementation for executing shell commands to retrieve API keys with timeout handling and secure file-based caching |
util/api_key_helper_test.go |
Comprehensive test suite covering command execution, caching, timeout, error handling, and file permissions |
cmd/provider.go |
Integration of API key helpers into OpenAI, Gemini, and Anthropic provider initialization with fallback logic |
cmd/config_set.go |
New configuration flags for api_key_helper and refresh_interval settings for OpenAI and Gemini |
cmd/config_list.go |
Documentation strings for the new configuration options |
README.md |
English documentation for the dynamic API key feature with usage examples and priority order |
README.zh-tw.md |
Traditional Chinese translation of the feature documentation |
README.zh-cn.md |
Simplified Chinese translation of the feature documentation |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| "gemini.backend": "Gemini backend (BackendGeminiAPI or BackendVertexAI)", | ||
| "gemini.api_key": "API key for Gemini provider", | ||
| "gemini.api_key_helper": "Shell command to dynamically generate Gemini API key", | ||
| "gemini.api_key_helper_refresh_interval": "Interval in seconds to refresh Gemini credentials from apiKeyHelper (default: 900)", |
There was a problem hiding this comment.
Inconsistent terminology: the description uses "apiKeyHelper" (camelCase) while the actual config key is "api_key_helper" (snake_case). This could confuse users. Consider using the exact config key name in the description for clarity.
| originalHome := os.Getenv("HOME") | ||
| os.Setenv("HOME", tmpDir) | ||
| defer os.Setenv("HOME", originalHome) |
There was a problem hiding this comment.
The test uses the HOME environment variable which is Unix/Linux specific. On Windows, the user home directory is typically determined by USERPROFILE or HOMEDRIVE/HOMEPATH. Since os.UserHomeDir() is used in the implementation, consider using t.Setenv() instead of manual os.Setenv/defer pattern (available in Go 1.17+), or skip these tests on Windows.
| defer cancel() | ||
|
|
||
| // Execute command in /bin/sh | ||
| cmd := exec.CommandContext(ctx, "/bin/sh", "-c", helperCmd) |
There was a problem hiding this comment.
The command is executed using /bin/sh which is Unix/Linux specific and will fail on Windows. Consider using a platform-agnostic approach or detecting the operating system and using "cmd.exe" with "/c" on Windows.
This could be handled by checking runtime.GOOS and selecting the appropriate shell, or using a cross-platform shell detection mechanism.
| func TestGetAPIKeyFromHelper_Timeout(t *testing.T) { | ||
| // Command that sleeps longer than the timeout | ||
| command := "sleep 15" | ||
|
|
||
| start := time.Now() | ||
| _, err := GetAPIKeyFromHelper(command) | ||
| duration := time.Since(start) | ||
|
|
||
| if err == nil { | ||
| t.Fatal("GetAPIKeyFromHelper() should return timeout error") | ||
| } | ||
| if !strings.Contains(err.Error(), "timed out") { | ||
| t.Errorf("error message should mention timeout, got: %v", err) | ||
| } | ||
|
|
||
| // Verify it actually timed out around the expected timeout duration | ||
| if duration < HelperTimeout || duration > HelperTimeout+2*time.Second { | ||
| t.Errorf("timeout duration = %v, want around %v", duration, HelperTimeout) | ||
| } | ||
| } |
There was a problem hiding this comment.
These tests use Unix-specific commands (sleep, echo, printf with Unix syntax) that will fail on Windows. Since the implementation uses /bin/sh (Unix-specific), the tests should either be skipped on Windows or the feature should be documented as Unix/Linux only.
Consider adding a build tag (e.g., //go:build !windows) or checking runtime.GOOS to skip these tests on Windows.
cmd/provider.go
Outdated
| // Try to get API key from helper first, fallback to static config | ||
| apiKey := viper.GetString("openai.api_key") | ||
| if helper := viper.GetString("openai.api_key_helper"); helper != "" { | ||
| refreshInterval := time.Duration(viper.GetInt("openai.api_key_helper_refresh_interval")) * time.Second |
There was a problem hiding this comment.
When viper.GetInt returns 0 (either because the config is unset or explicitly set to 0), the refresh interval becomes 0 seconds, which disables caching. However, the default is supposed to be 900 seconds (15 minutes) according to the config flags and documentation.
This means if users don't explicitly set the refresh interval, they'll get no caching by default, which contradicts the documented behavior. Consider using viper.GetInt with a default value or checking if the value is 0 and applying DefaultRefreshInterval from the util package.
| // Check that file has restrictive permissions (0600) | ||
| perm := info.Mode().Perm() | ||
| if perm != 0600 { | ||
| t.Errorf("Cache file should have 0600 permissions, got %o", perm) | ||
| } |
There was a problem hiding this comment.
File permissions (0600) are Unix-specific and won't work as expected on Windows. Windows uses a different permission model (ACLs). This test will likely fail or behave unpredictably on Windows. Consider skipping this test on Windows or using a platform-specific approach to verify file security (e.g., checking ACLs on Windows).
- Add support for dynamic API key retrieval using shell commands, with optional file-based caching and refresh intervals. - Document the new API key helper feature in English, Simplified Chinese, and Traditional Chinese, including setup instructions and security considerations. - Register new config options for OpenAI and Gemini API key helpers and their refresh intervals. - Update provider initialization to prioritize dynamic API key retrieval, with fallback to static config and environment variables. - Implement a utility for securely executing helper commands, caching API keys, and handling process timeouts. - Add comprehensive tests for the API key helper utility, covering command execution, caching, cache expiration, error handling, and file permissions. Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Uh oh!
There was an error while loading. Please reload this page.