use anyhow::{Context, Result};
use std::path::Path;
use std::process::Command;
#[cfg(test)]
use std::sync::OnceLock;
pub(crate) fn load_secret(env_name: &str) -> Option<String> {
crate::secrets::load_secret(env_name, &crate::secrets::sync_secrets_dir())
}
pub(crate) fn load_secret_or_legacy_pat(env_name: &str) -> Option<String> {
load_secret(env_name).or_else(|| {
crate::secrets::load_secret(env_name, &crate::secrets::legacy_pat_secrets_dir())
})
}
pub(crate) fn gh_cmd() -> Command {
let mut cmd = Command::new("gh");
if let Some(token) = load_secret("GH_TOKEN") {
cmd.env("GH_TOKEN", token);
}
cmd.env("GH_PROMPT_DISABLED", "1");
cmd
}
pub(crate) fn detect_orphan_origin(repo: &Path) -> Option<(String, String)> {
let current = crate::git::multi_remote::get_remote_url(repo, "origin")?;
let path_part = current.rsplit('/').next()?;
let (repo_part, suffix) = if let Some(dot) = path_part.rfind('.') {
(&path_part[..dot], &path_part[dot..])
} else {
(path_part, "")
};
if let Some(dash) = repo_part.rfind('-') {
let suffix_num = &repo_part[dash + 1..];
if suffix_num.len() == 1
&& suffix_num
.chars()
.next()
.map(|c| c.is_ascii_digit())
.unwrap_or(false)
{
let prefix = ¤t[..current.len() - path_part.len()];
let canonical_repo = &repo_part[..dash];
let canonical = format!("{}{}{}", prefix, canonical_repo, suffix);
return Some((current, canonical));
}
}
None
}
pub(crate) fn fix_orphan_origin(repo: &Path, canonical_url: &str) -> Result<()> {
crate::policy::std_git_command()
.args(["remote", "set-url", "origin", canonical_url])
.current_dir(repo)
.status()
.with_context(|| format!("failed to set origin URL in {}", repo.display()))?;
Ok(())
}
#[cfg(test)]
pub(crate) fn acquire_path_lock() -> parking_lot::MutexGuard<'static, ()> {
static PATH_LOCK: OnceLock<parking_lot::Mutex<()>> = OnceLock::new();
let lock = PATH_LOCK.get_or_init(|| parking_lot::Mutex::new(()));
loop {
if let Some(guard) = lock.try_lock() {
return guard;
}
std::thread::sleep(std::time::Duration::from_millis(10));
}
}