use std::process::Command;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum SecretsError {
#[error("Token not found in env or gh")]
NotFound,
#[error("gh command failed: {0}")]
CommandFailed(String),
#[error(transparent)]
Io(#[from] std::io::Error),
}
pub fn resolve_github_token() -> Result<String, SecretsError> {
if let Ok(v) = std::env::var("GITHUB_TOKEN")
&& !v.trim().is_empty()
{
return Ok(v.trim().to_string());
}
if let Ok(v) = std::env::var("GH_TOKEN")
&& !v.trim().is_empty()
{
return Ok(v.trim().to_string());
}
let out = Command::new("gh").args(["auth", "token"]).output();
match out {
Ok(o) if o.status.success() => {
let s = String::from_utf8_lossy(&o.stdout).trim().to_string();
if s.is_empty() {
Err(SecretsError::NotFound)
} else {
Ok(s)
}
}
Ok(o) => Err(SecretsError::CommandFailed(
String::from_utf8_lossy(&o.stderr).to_string(),
)),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
Err(SecretsError::NotFound)
}
Err(e) => Err(SecretsError::Io(e)),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn secrets_error_display() {
let e = SecretsError::NotFound;
assert!(e.to_string().contains("not found"));
let e = SecretsError::CommandFailed("bad things".into());
assert!(e.to_string().contains("bad things"));
}
}