1use crate::auth::AuthSource;
4use crate::error::{Error, Result};
5use std::env;
6use tokio::process::Command;
7use tracing::debug;
8
9#[derive(Debug, Clone)]
11pub struct GitHubAuthConfig {
12 pub token: String,
14 pub source: AuthSource,
16}
17
18pub async fn get_github_auth() -> Result<GitHubAuthConfig> {
25 debug!("attempting to get GitHub token via gh CLI");
27 if let Some(token) = get_gh_cli_token().await {
28 debug!("obtained GitHub token from gh CLI");
29 return Ok(GitHubAuthConfig {
30 token,
31 source: AuthSource::Cli,
32 });
33 }
34
35 debug!("gh CLI token not available, checking env vars");
37 if let Ok(token) = env::var("GITHUB_TOKEN") {
38 debug!("obtained GitHub token from GITHUB_TOKEN env var");
39 return Ok(GitHubAuthConfig {
40 token,
41 source: AuthSource::EnvVar,
42 });
43 }
44
45 if let Ok(token) = env::var("GH_TOKEN") {
46 debug!("obtained GitHub token from GH_TOKEN env var");
47 return Ok(GitHubAuthConfig {
48 token,
49 source: AuthSource::EnvVar,
50 });
51 }
52
53 debug!("no GitHub authentication found");
54 Err(Error::Auth(
55 "No GitHub authentication found. Run `gh auth login` or set GITHUB_TOKEN".to_string(),
56 ))
57}
58
59async fn get_gh_cli_token() -> Option<String> {
60 Command::new("gh").arg("--version").output().await.ok()?;
62
63 let status = Command::new("gh")
65 .args(["auth", "status"])
66 .output()
67 .await
68 .ok()?;
69
70 if !status.status.success() {
71 return None;
72 }
73
74 let output = Command::new("gh")
76 .args(["auth", "token"])
77 .output()
78 .await
79 .ok()?;
80
81 if !output.status.success() {
82 return None;
83 }
84
85 let token = String::from_utf8_lossy(&output.stdout).trim().to_string();
86 if token.is_empty() { None } else { Some(token) }
87}
88
89pub async fn test_github_auth(config: &GitHubAuthConfig) -> Result<String> {
91 let octocrab = octocrab::Octocrab::builder()
92 .personal_token(config.token.clone())
93 .build()
94 .map_err(|e| Error::GitHubApi(e.to_string()))?;
95
96 let user = octocrab
97 .current()
98 .user()
99 .await
100 .map_err(|e| Error::Auth(format!("Invalid token: {e}")))?;
101
102 Ok(user.login)
103}