use crate::config::resolve_github_oauth2_key;
use crate::utils::{
command_exists, git_remote_url_from_metadata, parse_github_repo_from_remote_url,
};
use std::path::Path;
use tokio::process::Command;
pub async fn resolve_repository(
project_root: &Path,
repo_override: Option<&str>,
) -> Result<(String, String), String> {
if let Some(value) = repo_override {
return parse_repo_override(value);
}
if let Some(url) = git_remote_url_from_metadata(project_root, "origin")? {
return parse_detected_repo_url(&url);
}
if !command_exists("git") {
return Err(
"Failed to detect GitHub repository. Is 'origin' remote configured?".to_string(),
);
}
let output = Command::new("git")
.arg("remote")
.arg("get-url")
.arg("origin")
.current_dir(project_root)
.output()
.await
.map_err(|error| format!("Failed to run git: {}", error))?;
if !output.status.success() {
return Err(
"Failed to detect GitHub repository. Is 'origin' remote configured?".to_string(),
);
}
let url = String::from_utf8_lossy(&output.stdout).trim().to_string();
parse_detected_repo_url(&url)
}
pub fn needs_repo_setup(error: &str) -> bool {
error.contains("origin' remote configured")
|| error.contains("Git remote URL is empty")
|| error.contains("Origin remote is not a GitHub repository URL")
|| error.contains("Unexpected GitHub remote format")
}
pub async fn resolve_token(token_override: Option<&str>) -> Result<String, String> {
if let Some(token) = token_override {
let token = token.trim();
if !token.is_empty() {
return Ok(token.to_string());
}
}
if let Some(token) = resolve_github_oauth2_key() {
return Ok(token);
}
if command_exists("gh") {
let output = Command::new("gh")
.arg("auth")
.arg("token")
.output()
.await
.map_err(|error| format!("Failed to run `gh auth token`: {}", error))?;
if output.status.success() {
let token = String::from_utf8_lossy(&output.stdout).trim().to_string();
if !token.is_empty() {
return Ok(token);
}
}
}
Err("No GitHub token found. Use `--token`, export `GITHUB_TOKEN`, run `xbp config github set-key`, or authenticate `gh`.".to_string())
}
fn parse_repo_override(value: &str) -> Result<(String, String), String> {
let trimmed = value.trim().trim_end_matches(".git");
let parts = trimmed
.split('/')
.filter(|segment| !segment.is_empty())
.collect::<Vec<_>>();
if parts.len() != 2 {
return Err(format!(
"Invalid repository override `{}`; expected owner/repo",
value
));
}
Ok((parts[0].to_string(), parts[1].to_string()))
}
fn parse_detected_repo_url(url: &str) -> Result<(String, String), String> {
let trimmed = url.trim();
if trimmed.is_empty() {
return Err("Git remote URL is empty.".to_string());
}
if let Some((owner, repo)) = parse_github_repo_from_remote_url(trimmed) {
return Ok((owner, repo));
}
if trimmed.contains("github.com/") || trimmed.contains("github.com:") {
return Err(format!("Unexpected GitHub remote format: {}", trimmed));
}
Err("Origin remote is not a GitHub repository URL.".to_string())
}