use crate::paths::checkpoint_path;
pub fn run() {
let new_token = match std::process::Command::new("gh")
.args(["auth", "token"])
.output()
{
Err(e) => {
println!("vibestats: auth failed — could not run 'gh': {e}");
println!("Ensure 'gh' CLI is installed and accessible in PATH.");
return;
}
Ok(out) if !out.status.success() => {
let stderr = String::from_utf8_lossy(&out.stderr);
println!(
"vibestats: auth failed — 'gh auth token' returned non-zero: {}",
stderr.trim()
);
println!("Run 'gh auth login' first, then retry 'vibestats auth'.");
return;
}
Ok(out) => {
let token = String::from_utf8_lossy(&out.stdout).trim().to_string();
if token.is_empty() {
println!("vibestats: auth failed — 'gh auth token' returned empty token.");
println!("Run 'gh auth login' first, then retry 'vibestats auth'.");
return;
}
token
}
};
let mut config = match crate::config::Config::load() {
Ok(c) => c,
Err(e) => {
println!("vibestats: auth failed — could not load config: {e}");
return;
}
};
config.oauth_token = new_token;
if let Err(e) = config.save() {
println!("vibestats: auth failed — could not save config: {e}");
return;
}
let secret_result = (|| -> std::io::Result<std::process::Output> {
use std::io::Write;
let mut child = std::process::Command::new("gh")
.args([
"secret",
"set",
"VIBESTATS_TOKEN",
"--repo",
&config.vibestats_data_repo,
])
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.spawn()?;
child
.stdin
.as_mut()
.ok_or_else(|| std::io::Error::new(std::io::ErrorKind::BrokenPipe, "stdin not piped"))?
.write_all(config.oauth_token.as_bytes())?;
drop(child.stdin.take());
child.wait_with_output()
})();
match secret_result {
Err(e) => {
println!(
"vibestats: token saved locally but could not update VIBESTATS_TOKEN secret: {e}"
);
println!("Run manually (feeds token via stdin to avoid leaking it in argv):");
println!(
" gh auth token | gh secret set VIBESTATS_TOKEN --repo {}",
config.vibestats_data_repo
);
}
Ok(out) if !out.status.success() => {
let stderr = String::from_utf8_lossy(&out.stderr);
println!("vibestats: token saved locally but 'gh secret set' failed: {stderr}");
println!("Run manually (feeds token via stdin to avoid leaking it in argv):");
println!(
" gh auth token | gh secret set VIBESTATS_TOKEN --repo {}",
config.vibestats_data_repo
);
}
Ok(_) => {} }
if let Some(cp_path) = checkpoint_path() {
let mut cp = crate::checkpoint::Checkpoint::load(&cp_path);
cp.clear_auth_error();
if let Err(e) = cp.save(&cp_path) {
println!("vibestats: auth complete (note: could not clear auth_error flag: {e})");
return;
}
}
println!("vibestats: auth complete");
}
#[cfg(test)]
mod tests {
#[test]
fn nonexistent_gh_binary_causes_error() {
let result = std::process::Command::new("/nonexistent/gh")
.args(["auth", "token"])
.output();
assert!(
result.is_err(),
"Expected Err for non-existent binary, got Ok"
);
}
}