use trusty_mpm::core::gh_identity::{GhEnv, GhIdentityError, select_github_config};
use trusty_mpm::core::trusty_tools_config::TrustyToolsConfig;
use trusty_mpm::project::record::repo_url_matches;
pub(crate) use trusty_mpm::core::gh_identity::clone_url;
fn resolve_gh_env_anyhow(
config: Option<&trusty_mpm::core::trusty_tools_config::GithubConfig>,
) -> anyhow::Result<GhEnv> {
trusty_mpm::core::gh_identity::resolve_gh_env(config)
.map_err(|e: GhIdentityError| anyhow::anyhow!(e))
}
pub(crate) fn resolve_project_aware(
config: &TrustyToolsConfig,
origin_url: Option<&str>,
) -> anyhow::Result<GhEnv> {
let project_github = origin_url
.and_then(|url| {
config
.projects
.iter()
.find(|p| repo_url_matches(&p.repo_url, url))
})
.and_then(|p| p.github.as_ref());
let selected = select_github_config(project_github, config.github.as_ref());
resolve_gh_env_anyhow(selected)
}
pub(crate) fn load_gh_env() -> anyhow::Result<GhEnv> {
let config = TrustyToolsConfig::load();
let origin_url = std::env::current_dir()
.ok()
.and_then(|cwd| trusty_mpm::daemon::managed_routes::inproject::get_origin_url(&cwd));
let env = resolve_project_aware(&config, origin_url.as_deref())?;
if !env.is_empty() {
let names: Vec<&str> = env.vars().iter().map(|(k, _)| k.as_str()).collect();
tracing::debug!(overrides = ?names, "applying per-project GitHub identity binding to gh calls");
}
Ok(env)
}
#[cfg(test)]
mod tests {
use super::*;
use trusty_mpm::core::trusty_tools_config::{GithubConfig, ProjectConfig};
fn project(repo_url: &str, github: Option<GithubConfig>) -> ProjectConfig {
ProjectConfig {
name: "p".into(),
repo_url: repo_url.into(),
default_branch: None,
stack_hint: None,
tags: None,
description: None,
gh_user: None,
github,
commit_name: None,
commit_email: None,
untracked_sync: None,
}
}
fn gh(config_dir: &str) -> GithubConfig {
GithubConfig {
config_dir: Some(config_dir.into()),
token_env: None,
account: None,
host: None,
}
}
#[test]
fn resolve_project_aware_project_binding_wins() {
let config = TrustyToolsConfig {
github: Some(gh("/cfg/global")),
projects: vec![project(
"https://github.com/acme/widget",
Some(gh("/cfg/project")),
)],
..Default::default()
};
let env =
resolve_project_aware(&config, Some("https://github.com/acme/widget.git")).expect("ok");
assert_eq!(
env.vars(),
&[("GH_CONFIG_DIR".to_string(), "/cfg/project".to_string())]
);
}
#[test]
fn resolve_project_aware_falls_back_to_global() {
let config = TrustyToolsConfig {
github: Some(gh("/cfg/global")),
projects: vec![project("https://github.com/acme/widget", None)],
..Default::default()
};
let env =
resolve_project_aware(&config, Some("https://github.com/acme/widget")).expect("ok");
assert_eq!(
env.vars(),
&[("GH_CONFIG_DIR".to_string(), "/cfg/global".to_string())]
);
}
#[test]
fn resolve_project_aware_no_origin_uses_global() {
let config = TrustyToolsConfig {
github: Some(gh("/cfg/global")),
..Default::default()
};
let env = resolve_project_aware(&config, None).expect("ok");
assert_eq!(
env.vars(),
&[("GH_CONFIG_DIR".to_string(), "/cfg/global".to_string())]
);
}
#[test]
fn resolve_project_aware_no_match_no_global_is_ambient() {
let config = TrustyToolsConfig::default();
let env =
resolve_project_aware(&config, Some("https://github.com/someone/else")).expect("ok");
assert!(env.is_empty());
}
}