refactor: abstract keyring logic to better enable DI#3262
refactor: abstract keyring logic to better enable DI#3262cloud-on-prem merged 13 commits intomainfrom
Conversation
ea8f3d6 to
4234f40
Compare
| # Run comprehensive linting checks (includes tests, examples, benchmarks) | ||
| lint-all: | ||
| @echo "Running comprehensive linting checks..." | ||
| cargo clippy --workspace --all-targets --all-features -- -D warnings |
There was a problem hiding this comment.
is there a reason that lint doesn't do all-targets too? I feel like having distinct lint commands means we won't use one of them.
There was a problem hiding this comment.
yeah - currently lint-all fails; we should fix them up in another PR. I left it there for future use. Currently just lint is equivalent of what's run in CI
| impl From<KeyringError> for StorageError { | ||
| fn from(err: KeyringError) -> Self { | ||
| match err { | ||
| KeyringError::NotFound { .. } => StorageError::NotFound, |
There was a problem hiding this comment.
Is this losing information that might be useful for debugging, can it be retained when converting to StorageError?
There was a problem hiding this comment.
Possibly, although the only thing NotFound contains is NotFound { service: String, username: String }.
| // Read should succeed with mock keyring | ||
| let read_result = manager.read_credentials::<TestCredentials>(); | ||
| assert!(read_result.is_ok(), "Read should succeed with fallback"); | ||
| assert!(read_result.is_ok(), "Read should succeed with mock keyring"); |
There was a problem hiding this comment.
I think this test is still about using the 'fallback_to_disk'=true rather than about using a mock keyring.
crates/goose/src/config/base.rs
Outdated
| .expect("goose requires a home dir") | ||
| .config_dir(); | ||
|
|
||
| let keyring: Arc<dyn KeyringBackend> = if env::var("GOOSE_DISABLE_KEYRING").is_ok() { |
There was a problem hiding this comment.
Is it it worth explicitly checking the value inside the GOOSE_DISABLE_KEYRING rather than just checking the var is present? Most vars can be true or false depending on the value.
There was a problem hiding this comment.
yep, worth making it more explicit is a good idea; fixed it.
crates/goose/src/config/base.rs
Outdated
| .config_dir(); | ||
|
|
||
| let keyring: Arc<dyn KeyringBackend> = if env::var("GOOSE_DISABLE_KEYRING").is_ok() { | ||
| Arc::new(FileKeyringBackend::new(config_dir.join("secrets.yaml"))) |
There was a problem hiding this comment.
Bonus points if "secrets.yaml" is a constant.
There was a problem hiding this comment.
yep good call; added it now
| } | ||
|
|
||
| #[test] | ||
| fn test_multiple_secrets_with_mock() -> Result<(), ConfigError> { |
There was a problem hiding this comment.
Can we remove the test_secret_management_with_mock test and make this cover it all?
| secrets_path: PathBuf, | ||
| } | ||
|
|
||
| impl FileKeyringBackend { |
There was a problem hiding this comment.
Is this backwards compatible with the existing 'secrets.yaml' that was previously used?
There was a problem hiding this comment.
good catch; I missed the old secrets.yaml flow here. Adding it now.
| let backend = FileKeyringBackend::new(temp_file.path().to_path_buf()); | ||
|
|
||
| // Test setting a password | ||
| backend.set_password("test_service", "test_user", "test_password")?; |
There was a problem hiding this comment.
These comments don't add anything compared to just the code on the next line, please remove and check for similar issues throughout.
| // If it succeeds with fake keys, the logic worked | ||
| } | ||
| Err(error) => { | ||
| // If it fails, it should be due to missing API keys, confirming we tried to create providers |
There was a problem hiding this comment.
Maybe some of these original comments are useful context?
There was a problem hiding this comment.
This comment I thought was more relevant: // Should not fail due to missing secrets, but may fail due to invalid fake keys and // Since we provided fake API keys, the creation should proceed to the provider instantiation
// It may still fail at the provider level, but we should not get keychain-related errors
| @@ -149,6 +149,39 @@ mod tests { | |||
| use mcp_core::{content::TextContent, Role}; | |||
| use std::env; | |||
|
|
|||
There was a problem hiding this comment.
At a glance it seems like all the changes in this file are unrelated to the PR focus which is using a new keyring abstraction. Maybe it crept in from some other work and we should keep it separate?
There was a problem hiding this comment.
Yep agree - it was an opportunistic refactor while I was removing the need for touching the user's keyring when running tests. Originally, I was setting the ENV in the tests to disable the keyring access. But I could not resist the temptation to use Drop trait :)
jsibbison-square
left a comment
There was a problem hiding this comment.
Left some comments but overall I think this looks ok. Double check the file implementation is backwards compatible. Would appreciate a second set of eyes from someone with history on keychain implementation (maybe @michaelneale ?) but looks ok to me.
|
|
||
| // Create the appropriate keyring backend based on environment | ||
| let keyring: Arc<dyn goose::keyring::KeyringBackend> = | ||
| if std::env::var("GOOSE_DISABLE_KEYRING").is_ok() { |
There was a problem hiding this comment.
Since it will create a MockKeyringBackend, just curious about: MockKeyringBackend condition only hit while running test? or any chances thatGOOSE_DISABLE_KEYRING is set when user runs goose?
There was a problem hiding this comment.
Good catch; fixed it by using a better abstraction.
1ba4ce8 to
838bf76
Compare
|
I still dont' quite follow this - was this tested once compiled, and also run as desktop bundle? as it seems a very risky change |
|
(I did test it with it bundled and signed, seemed ok) |
|
main concern is that goose-mcp has a goose crate dependency (don't really know what that means in the end, but feels potentially circular) |
This reverts commit 7e4a98d.
* main: (51 commits) docs: reflecting benefits of CLI providers (block#3399) feat: fetch openrouter supported models in `goose configure` (block#3347) Add the ability to configure rustyline to use a different edit mode (e.g. vi) (block#2769) docs: update CLI provider guide (block#3397) Streamable HTTP CLI flag (block#3394) docs: Show both remote options for extensions in CLI (block#3392) docs: fix YouTube Transcript MCP package manager (block#3390) docs: simplify alby mcp (block#3379) docs: add max turns (block#3372) feat(cli): add cost estimation per provider for Goose CLI (block#3330) feat: Allow Ollama for non-tool models for chat only (block#3308) [cli] Add --provider and --model CLI options to run command (block#3295) Docs: Lead/worker model in Goose Desktop (block#3342) revert: refactor: abstract keyring logic to better enable DI (block#3358) Drop temporal-service binary (block#3340) docs: add fuzzy search (block#3357) Fix name of GPT-4.1 System Prompt (block#3348) (block#3351) docs: add goose-mobile (block#3315) refactor: abstract keyring logic to better enable DI (block#3262) fix: correct tool use for anthropic (block#3311) ...
Signed-off-by: Adam Tarantino <tarantino.adam@gmail.com>
Signed-off-by: Soroosh <soroosh.sarabadani@gmail.com>
Signed-off-by: Kyle Santiago <kyle@privkey.io>
This change introduces a clean keyring abstraction layer to replace global keyring state with dependency injection, addressing critical runtime errors and improving testability.
The implementation creates a KeyringBackend trait with concrete implementations for system keyring (SystemKeyringBackend) and file-based storage (FileKeyringBackend), allowing the configuration system to use different keyring backends based on the GOOSE_DISABLE_KEYRING environment variable.
This architectural improvement eliminates the need to touch the user's real OS keyring and thus enables comprehensive unit testing without keychain popups. The refactoring maintains full backward compatibility while establishing a foundation for reliable testing and development workflows