use anyhow::{Context, Error, bail};
use chrono::DateTime;
use serde_json::Value;
use crate::common::{
local_git_repo,
vault::http_client::fetch_shasta_vcs_token,
};
use crate::manta_backend_dispatcher::StaticBackendDispatcher;
pub async fn exec(
backend: &StaticBackendDispatcher,
site_name: &str,
token: &str,
shasta_root_cert: &[u8],
vault_base_url: Option<&str>,
gitea_base_url: &str,
repo_path: &str,
) -> Result<(), Error> {
let gitea_token = fetch_shasta_vcs_token(
token,
vault_base_url.context("vault base url is mandatory")?,
site_name,
)
.await
.context("Failed to fetch VCS token from vault")?;
let mut exit_code = 0;
println!("Validate local repo {}", repo_path);
let repo = local_git_repo::get_repo(repo_path)
.with_context(|| format!("Could not open git repo in {}", repo_path))?;
log::info!("Repo '{}' found", repo_path);
let local_last_commit = local_git_repo::get_last_commit(&repo)
.context("Failed to get last commit")?;
log::info!("Checking local repo status ({})", &repo.path().display());
let repo_name = local_git_repo::parse_repo_name_from_remote(&repo)?;
println!("Repository name: {}", repo_name);
let head_commit_id = local_last_commit.id();
let head_commit_summary = local_last_commit.summary();
println!(
"HEAD commit summary: {}",
head_commit_summary.unwrap_or_default()
);
println!(
"HEAD commit time: {}",
DateTime::from_timestamp(local_last_commit.time().seconds(), 0,)
.context("Failed to parse commit timestamp")?
);
if !local_git_repo::untracked_changed_local_files(&repo)
.map_err(|e| anyhow::anyhow!("{e}"))
.context("Failed to check for untracked/changed files")?
{
println!("Local changes committed: ❌");
exit_code = 1;
} else {
println!("Local changes committed: ✅");
}
let remote_ref_value_vec = csm_rs::common::gitea::http_client::get_all_refs(
gitea_base_url,
&gitea_token,
&repo_name,
shasta_root_cert,
)
.await
.unwrap_or_default();
let remote_ref_vec: Vec<&str> = remote_ref_value_vec
.iter()
.filter_map(|ref_value| ref_value.get("ref").and_then(Value::as_str))
.collect();
let branches = repo
.branches(Some(git2::BranchType::Local))
.context("Failed to list local branches")?;
for branch_rslt in branches.into_iter() {
let (branch, _) = match branch_rslt {
Ok(b) => b,
Err(e) => {
log::warn!("Failed to read branch: {}", e);
continue;
}
};
if branch.is_head() {
let branch_name = match branch.name() {
Ok(Some(name)) => name,
_ => {
log::warn!("Failed to read branch name");
continue;
}
};
if remote_ref_vec
.iter()
.any(|remote_ref| remote_ref.contains(branch_name))
{
println!("HEAD branch '{}': ✅", branch_name);
} else {
exit_code = 1;
println!("HEAD branch '{}': ❌", branch_name);
}
}
}
let gitea_commit_details =
csm_rs::common::gitea::http_client::get_commit_details(
gitea_base_url,
&repo_name,
&head_commit_id.to_string(),
&gitea_token,
shasta_root_cert,
)
.await;
if gitea_commit_details.is_ok() {
println!("HEAD commit id {}: ✅", head_commit_id);
} else {
println!("HEAD commit id {}: ❌", head_commit_id);
exit_code = 1;
}
let local_tags = repo.tag_names(None).context("Failed to list local tags")?;
for local_tag_rslt in local_tags.iter() {
let tag = match local_tag_rslt {
Some(t) => t,
None => {
log::warn!("Failed to read tag name");
continue;
}
};
if remote_ref_vec
.iter()
.any(|remote_tag| remote_tag.contains(tag))
{
println!("tag {}: ✅", tag);
} else {
exit_code = 1;
println!("tag {}: ❌", tag);
}
}
println!("Repo synced? {}", exit_code == 0);
if exit_code != 0 {
bail!(
"Local repository is not in sync with \
remote repository",
);
}
Ok(())
}