use std::process::Command;
pub fn load_token() -> anyhow::Result<String> {
for var in ["GITHUB_TOKEN", "GH_TOKEN"] {
if let Ok(val) = std::env::var(var) {
let trimmed = val.trim().to_owned();
if !trimmed.is_empty() {
return Ok(trimmed);
}
}
}
let output =
Command::new("gh").args(["auth", "token"]).output().ok().filter(|o| o.status.success());
if let Some(out) = output {
let token = std::str::from_utf8(&out.stdout)
.map_err(|e| anyhow::anyhow!("gh auth token stdout is not valid UTF-8: {e}"))?
.trim()
.to_owned();
if !token.is_empty() {
return Ok(token);
}
}
anyhow::bail!("No GitHub token found. Run `gh auth login` or set GITHUB_TOKEN.")
}
#[cfg(test)]
mod tests {
use super::*;
fn load_token_from(token: &str) -> anyhow::Result<String> {
let trimmed = token.trim().to_owned();
if trimmed.is_empty() {
anyhow::bail!("empty token");
}
Ok(trimmed)
}
#[test]
fn non_empty_token_is_returned() {
let result = load_token_from(" ghp_test123 ");
assert!(result.is_ok(), "expected Ok, got {result:?}");
if let Ok(token) = result {
assert_eq!(token, "ghp_test123", "whitespace must be trimmed");
}
}
#[test]
fn empty_token_is_rejected() {
let result = load_token_from(" ");
assert!(result.is_err(), "empty token must return Err");
}
#[test]
fn env_path_succeeds_when_token_present() {
if std::env::var("GITHUB_TOKEN").is_ok() || std::env::var("GH_TOKEN").is_ok() {
let result = load_token();
assert!(result.is_ok(), "expected Ok when env var set: {result:?}");
}
}
}