use actix::prelude::*; use kxio::network; use tracing::{error, info, warn}; use crate::server::{config, forge}; use super::{RepoActor, StartMonitoring, StartRepo}; #[tracing::instrument(fields(forge_name = %repo_details.forge.name, repo_name = %repo_details.name))] pub async fn validate_positions( repo_details: config::RepoDetails, config: config::RepoConfig, addr: Addr, net: network::Network, ) { let commit_histories = match repo_details.forge.forge_type { config::ForgeType::ForgeJo => { forge::forgejo::get_commit_histories(&repo_details, &config, &net).await } }; let commit_histories = match commit_histories { Ok(commit_histories) => commit_histories, Err(err) => { error!(?err, "Failed to get commit histories"); return; } }; let Some(main) = commit_histories.main.first().cloned() else { warn!("No commits on main branch '{}'", config.branches().main()); return; }; let next_commits = commit_histories .next .into_iter() .take(2) .collect::>(); if !next_commits.contains(&main) { warn!( "Main branch '{}' is not on the same commit as next branch '{}', or it's parent", config.branches().main(), config.branches().next() ); return; } let next = next_commits[0].clone(); let dev_has_next = commit_histories .dev .iter() .any(|commit| commit == &next_commits[0]); if !dev_has_next { warn!( "Dev branch '{}' is not based on next branch '{}'", config.branches().dev(), config.branches().next() ); return; // dev is not based on next } let dev_has_main = commit_histories.dev.iter().any(|commit| commit == &main); if !dev_has_main { warn!( "Dev branch '{}' is not based on main branch '{}'", config.branches().dev(), config.branches().main() ); return; } let dev = commit_histories.dev[0].clone(); addr.do_send(StartMonitoring { main, next, dev, dev_commit_history: commit_histories.dev, }); } // advance next to the next commit towards the head of the dev branch pub async fn advance_next( next: forge::Commit, dev_commit_history: Vec, repo_details: config::RepoDetails, repo_config: config::RepoConfig, addr: Addr, ) { info!("Advance Next"); let user = repo_details.forge.user; let token = repo_details.forge.token; let hostname = repo_details.forge.hostname; let path = repo_details.repo; let next_commit = find_next_commit_on_dev(next, dev_commit_history); let Some(commit) = next_commit else { warn!("No commits to advance next to"); return; }; let next = repo_config.branches().next(); let command = format!("/usr/bin/git push https://{user}:{token}@{hostname}/{path}.git {commit}:{next}"); info!("Running command: {}", command); match gix::command::prepare(command) .with_shell_allow_argument_splitting() .spawn() { Ok(mut child) => { match child.wait() { Ok(exit_status) => { info!(%exit_status, "Advance Next Success"); addr.do_send(StartRepo); } Err(err) => { warn!(?err, "Advance Next Failed (wait)") } }; } Err(err) => { warn!(?err, "Advance Next Failed (spawn)") } }; } fn find_next_commit_on_dev( next: forge::Commit, dev_commit_history: Vec, ) -> Option { let mut next_commit: Option = None; for commit in dev_commit_history.into_iter() { if commit == next { break; }; next_commit.replace(commit); } next_commit } // advance main branch to the commit 'next' #[allow(dead_code)] pub async fn advance_main( _next: forge::Commit, _repo_details: config::RepoDetails, addr: Addr, _net: network::Network, ) { // TODO: (#19) advance main to next info!("Advance Main"); addr.do_send(StartRepo); } #[cfg(test)] mod tests { use super::*; #[actix_rt::test] async fn test_find_next_commit_on_dev() { let next = forge::Commit::new("current-next"); let dev_commit_history = vec![ forge::Commit::new("dev"), forge::Commit::new("dev-next"), forge::Commit::new("current-next"), forge::Commit::new("current-main"), ]; let next_commit = find_next_commit_on_dev(next, dev_commit_history); assert_eq!(next_commit, Some(forge::Commit::new("dev-next"))); } }