From 98a94ea855f9f7e9543cc99d1e9987677335f63a Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Wed, 10 Apr 2024 15:18:48 +0100 Subject: [PATCH] Implement advancing next branch to next commit on dev branch Uses gix to invoke git commands as there is not API to directly update the position of a branch. Closes kemitix/git-next#14 --- Cargo.toml | 3 ++ src/server/actors/repo/branch.rs | 81 +++++++++++++++++++++++++++++--- src/server/actors/repo/mod.rs | 25 +++++++--- src/server/actors/repo/status.rs | 2 - src/server/forge/mod.rs | 8 ++++ 5 files changed, 104 insertions(+), 15 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b9035a7..1fc942a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,9 @@ tracing-subscriber = "0.3" # base64 decoding base64 = "0.22" +# git +gix = "0.61" + # fs/network kxio = "0.1" # { git = "https://git.kemitix.net/kemitix/kxio.git", branch = "main" } diff --git a/src/server/actors/repo/branch.rs b/src/server/actors/repo/branch.rs index b604865..7196e62 100644 --- a/src/server/actors/repo/branch.rs +++ b/src/server/actors/repo/branch.rs @@ -67,20 +67,69 @@ pub async fn validate_positions( return; } let dev = commit_histories.dev[0].clone(); - addr.do_send(StartMonitoring { main, next, dev }); + 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 -#[allow(dead_code)] pub async fn advance_next( - _next: forge::Commit, - _repo_details: config::RepoDetails, + next: forge::Commit, + dev_commit_history: Vec, + repo_details: config::RepoDetails, + repo_config: config::RepoConfig, addr: Addr, - _net: network::Network, ) { - // TODO: (#14) advance next one commit towards dev info!("Advance Next"); - addr.do_send(StartRepo); + 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' @@ -95,3 +144,21 @@ pub async fn advance_main( 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"))); + } +} diff --git a/src/server/actors/repo/mod.rs b/src/server/actors/repo/mod.rs index 41cc55f..6f64b5b 100644 --- a/src/server/actors/repo/mod.rs +++ b/src/server/actors/repo/mod.rs @@ -5,7 +5,7 @@ mod webhook; use actix::prelude::*; use kxio::network::Network; -use tracing::info; +use tracing::{info, warn}; use crate::server::{ config::{RepoConfig, RepoDetails}, @@ -86,24 +86,37 @@ pub struct StartMonitoring { pub main: forge::Commit, pub next: forge::Commit, pub dev: forge::Commit, + pub dev_commit_history: Vec, } impl Handler for RepoActor { type Result = (); fn handle(&mut self, msg: StartMonitoring, ctx: &mut Self::Context) -> Self::Result { info!("Monitoring started"); - let next_ahead_of_main = msg.main != msg.next; - let dev_ahead_of_next = msg.next != msg.dev; + let Some(repo_config) = self.config.clone() else { + warn!("No config loaded"); + return; + }; + let repo_details = self.details.clone(); let addr = ctx.address(); let net = self.net.clone(); + + let next_ahead_of_main = msg.main != msg.next; + let dev_ahead_of_next = msg.next != msg.dev; if next_ahead_of_main { status::check_next(msg.next, repo_details, addr, net) .into_actor(self) .wait(ctx); } else if dev_ahead_of_next { - branch::advance_next(msg.next, repo_details, addr, net) - .into_actor(self) - .wait(ctx); + branch::advance_next( + msg.next, + msg.dev_commit_history, + repo_details, + repo_config, + addr, + ) + .into_actor(self) + .wait(ctx); } else if self.webhook_id.is_none() { webhook::register(repo_details, addr, net) .into_actor(self) diff --git a/src/server/actors/repo/status.rs b/src/server/actors/repo/status.rs index 2548ea8..0594f4c 100644 --- a/src/server/actors/repo/status.rs +++ b/src/server/actors/repo/status.rs @@ -34,9 +34,7 @@ pub async fn check_next( #[derive(Debug)] pub enum Status { - #[allow(dead_code)] // TODO: (#13) remove this when we have results from the forge Pass, - #[allow(dead_code)] // TODO: (#13) remove this when we have results from the forge Fail, Pending, } diff --git a/src/server/forge/mod.rs b/src/server/forge/mod.rs index 55a9da2..050d1a1 100644 --- a/src/server/forge/mod.rs +++ b/src/server/forge/mod.rs @@ -13,6 +13,14 @@ pub struct CommitHistories { pub struct Commit { pub sha: String, } +impl Commit { + #[cfg(test)] + pub fn new(sha: &str) -> Self { + Self { + sha: sha.to_string(), + } + } +} impl Display for Commit { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { write!(f, "{}", self.sha)