diff --git a/src/model/credentials.rs b/src/model/credentials.rs index 98a1424..e81a017 100644 --- a/src/model/credentials.rs +++ b/src/model/credentials.rs @@ -132,10 +132,34 @@ impl PakkerCompatCredentialsFile { )) })?; - serde_json::from_str(&content).map_err(|e| { - PakkerError::InternalError(format!( - "Failed to parse Pakku credentials file: {e}" - )) + // Try JSON first (for compatibility with future format changes) + if let Ok(parsed) = serde_json::from_str::(&content) { + return Ok(parsed); + } + + // Fall back to .env-style key=value parsing (format written by pakker init) + let mut curseforge_api_key = None; + let mut github_access_token = None; + + for line in content.lines() { + let line = line.trim(); + if line.is_empty() || line.starts_with('#') { + continue; + } + if let Some((key, value)) = line.split_once('=') { + let key = key.trim(); + let value = value.trim().to_string(); + match key { + "CURSEFORGE_API_KEY" => curseforge_api_key = Some(value), + "GITHUB_TOKEN" => github_access_token = Some(value), + _ => {}, + } + } + } + + Ok(Self { + curseforge_api_key, + github_access_token, }) } } @@ -160,8 +184,9 @@ impl ResolvedCredentials { let pakku_file = PakkerCompatCredentialsFile::load().ok(); Self { - curseforge_api_key: resolve_secret( + curseforge_api_key: resolve_secret_with_fallback( "PAKKER_CURSEFORGE_API_KEY", + "CURSEFORGE_API_KEY", "curseforge_api_key", pakker_file .as_ref() @@ -176,8 +201,9 @@ impl ResolvedCredentials { pakker_file.as_ref().and_then(|f| f.modrinth_token.clone()), None, ), - github_access_token: resolve_secret( + github_access_token: resolve_secret_with_fallback( "PAKKER_GITHUB_TOKEN", + "GITHUB_TOKEN", "github_access_token", pakker_file .as_ref() @@ -248,6 +274,40 @@ fn resolve_secret( .map(|v| (v, CredentialsSource::PakkerFile)) } +fn resolve_secret_with_fallback( + env_key: &str, + fallback_env_key: &str, + keyring_entry: &str, + pakker_file_value: Option, + pakku_file_value: Option, +) -> Option<(String, CredentialsSource)> { + if let Ok(v) = std::env::var(env_key) + && !v.trim().is_empty() + { + return Some((v.trim().to_string(), CredentialsSource::Env)); + } + + if let Ok(v) = std::env::var(fallback_env_key) + && !v.trim().is_empty() + { + return Some((v.trim().to_string(), CredentialsSource::Env)); + } + + if let Ok(v) = get_keyring_secret(keyring_entry) + && !v.trim().is_empty() + { + return Some((v.trim().to_string(), CredentialsSource::Keyring)); + } + + if let Some(v) = pakker_file_value.filter(|v| !v.trim().is_empty()) { + return Some((v, CredentialsSource::PakkerFile)); + } + + pakku_file_value + .filter(|v| !v.trim().is_empty()) + .map(|v| (v, CredentialsSource::PakkerFile)) +} + fn get_keyring_secret( entry: &str, ) -> std::result::Result {