use std::sync::Arc;
use anyhow::Context as _;
use crate::forge_resolution::GitLabHandles;
#[cfg_attr(coverage_nightly, coverage(off))]
#[mutants::skip]
pub(crate) async fn build_git(
inner: Arc<cursus::git::GitWorkdir>,
filesystem: Arc<dyn cursus::filesystem::Filesystem>,
runner: Arc<dyn cursus::command::CommandRunner>,
config: &Option<cursus::model::config::Config>,
dry_run: bool,
octocrab: Option<Arc<octocrab::Octocrab>>,
gitlab_handles: Option<&GitLabHandles>,
) -> anyhow::Result<Arc<dyn cursus::git::Git>> {
let mode = config
.as_ref()
.map(|c| c.git.signed_commits)
.unwrap_or_default();
let github_enabled = config.as_ref().is_some_and(|c| c.github.enabled);
let gitlab_enabled = config.as_ref().is_some_and(|c| c.gitlab.enabled);
if gitlab_enabled
&& resolve_signed_commits_mode_gitlab(
mode,
gitlab_handles.is_some(),
std::env::var("GITLAB_CI").as_deref() == Ok("true"),
) {
return build_gitlab_signed(inner, filesystem, runner, gitlab_handles, dry_run);
}
if github_enabled
&& resolve_signed_commits_mode(
mode,
octocrab.is_some(),
std::env::var("GITHUB_ACTIONS").as_deref() == Ok("true"),
) {
return build_github_signed(inner, filesystem, runner, config, octocrab, dry_run).await;
}
let g: Arc<dyn cursus::git::Git> = inner;
Ok(g)
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[mutants::skip]
async fn build_github_signed(
inner: Arc<cursus::git::GitWorkdir>,
filesystem: Arc<dyn cursus::filesystem::Filesystem>,
runner: Arc<dyn cursus::command::CommandRunner>,
config: &Option<cursus::model::config::Config>,
octocrab: Option<Arc<octocrab::Octocrab>>,
dry_run: bool,
) -> anyhow::Result<Arc<dyn cursus::git::Git>> {
let octocrab = octocrab.context(
"GitHub token required for signed commits but none found (GH_TOKEN / GITHUB_TOKEN)",
)?;
let github_config = config
.as_ref()
.map(|c| c.github.clone())
.unwrap_or_default();
let repo = cursus::forge::github::remote::GitHubRepo::resolve(&github_config, &*inner)
.await
.context("cannot enable signed commits: failed to determine GitHub repository")?;
log::info!(
"Routing git commit and push operations through the GitHub API for verified commits."
);
Ok(Arc::new(cursus::git::GitHubSignedCommit::new(
inner, filesystem, octocrab, runner, repo.owner, repo.repo, dry_run,
)))
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[mutants::skip]
fn build_gitlab_signed(
inner: Arc<cursus::git::GitWorkdir>,
filesystem: Arc<dyn cursus::filesystem::Filesystem>,
runner: Arc<dyn cursus::command::CommandRunner>,
gitlab_handles: Option<&GitLabHandles>,
dry_run: bool,
) -> anyhow::Result<Arc<dyn cursus::git::Git>> {
let handles = gitlab_handles.context(
"GitLab token required for signed commits but none found (GITLAB_TOKEN / CI_JOB_TOKEN)",
)?;
log::info!(
"Routing git commit and push operations through the GitLab API for verified commits."
);
Ok(Arc::new(cursus::git::GitLabSignedCommit::new(
inner,
filesystem,
Arc::clone(&handles.client),
runner,
handles.project.clone(),
dry_run,
)))
}
pub(crate) fn resolve_signed_commits_mode(
mode: cursus::model::config::SignedCommitsMode,
token_present: bool,
on_gha: bool,
) -> bool {
use cursus::model::config::SignedCommitsMode;
match mode {
SignedCommitsMode::Off => false,
SignedCommitsMode::Force => token_present,
SignedCommitsMode::Auto => on_gha && token_present,
}
}
pub(crate) fn resolve_signed_commits_mode_gitlab(
mode: cursus::model::config::SignedCommitsMode,
token_present: bool,
on_gitlab_ci: bool,
) -> bool {
use cursus::model::config::SignedCommitsMode;
match mode {
SignedCommitsMode::Off => false,
SignedCommitsMode::Force => token_present,
SignedCommitsMode::Auto => on_gitlab_ci && token_present,
}
}