Implement advancing next branch to next commit on dev branch
All checks were successful
ci/woodpecker/push/cron-docker-builder Pipeline was successful
ci/woodpecker/push/tag-created Pipeline was successful
ci/woodpecker/push/push-next Pipeline was successful

Uses gix to invoke git commands as there is not API to directly update
the position of a branch.

Closes kemitix/git-next#14
This commit is contained in:
Paul Campbell 2024-04-10 15:18:48 +01:00
parent a5e9421405
commit 98a94ea855
5 changed files with 104 additions and 15 deletions

View file

@ -17,6 +17,9 @@ tracing-subscriber = "0.3"
# base64 decoding # base64 decoding
base64 = "0.22" base64 = "0.22"
# git
gix = "0.61"
# fs/network # fs/network
kxio = "0.1" # { git = "https://git.kemitix.net/kemitix/kxio.git", branch = "main" } kxio = "0.1" # { git = "https://git.kemitix.net/kemitix/kxio.git", branch = "main" }

View file

@ -67,20 +67,69 @@ pub async fn validate_positions(
return; return;
} }
let dev = commit_histories.dev[0].clone(); 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 // advance next to the next commit towards the head of the dev branch
#[allow(dead_code)]
pub async fn advance_next( pub async fn advance_next(
_next: forge::Commit, next: forge::Commit,
_repo_details: config::RepoDetails, dev_commit_history: Vec<forge::Commit>,
repo_details: config::RepoDetails,
repo_config: config::RepoConfig,
addr: Addr<RepoActor>, addr: Addr<RepoActor>,
_net: network::Network,
) { ) {
// TODO: (#14) advance next one commit towards dev
info!("Advance Next"); 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<forge::Commit>,
) -> Option<forge::Commit> {
let mut next_commit: Option<forge::Commit> = 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' // advance main branch to the commit 'next'
@ -95,3 +144,21 @@ pub async fn advance_main(
info!("Advance Main"); info!("Advance Main");
addr.do_send(StartRepo); 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")));
}
}

View file

@ -5,7 +5,7 @@ mod webhook;
use actix::prelude::*; use actix::prelude::*;
use kxio::network::Network; use kxio::network::Network;
use tracing::info; use tracing::{info, warn};
use crate::server::{ use crate::server::{
config::{RepoConfig, RepoDetails}, config::{RepoConfig, RepoDetails},
@ -86,24 +86,37 @@ pub struct StartMonitoring {
pub main: forge::Commit, pub main: forge::Commit,
pub next: forge::Commit, pub next: forge::Commit,
pub dev: forge::Commit, pub dev: forge::Commit,
pub dev_commit_history: Vec<forge::Commit>,
} }
impl Handler<StartMonitoring> for RepoActor { impl Handler<StartMonitoring> for RepoActor {
type Result = (); type Result = ();
fn handle(&mut self, msg: StartMonitoring, ctx: &mut Self::Context) -> Self::Result { fn handle(&mut self, msg: StartMonitoring, ctx: &mut Self::Context) -> Self::Result {
info!("Monitoring started"); info!("Monitoring started");
let next_ahead_of_main = msg.main != msg.next; let Some(repo_config) = self.config.clone() else {
let dev_ahead_of_next = msg.next != msg.dev; warn!("No config loaded");
return;
};
let repo_details = self.details.clone(); let repo_details = self.details.clone();
let addr = ctx.address(); let addr = ctx.address();
let net = self.net.clone(); 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 { if next_ahead_of_main {
status::check_next(msg.next, repo_details, addr, net) status::check_next(msg.next, repo_details, addr, net)
.into_actor(self) .into_actor(self)
.wait(ctx); .wait(ctx);
} else if dev_ahead_of_next { } else if dev_ahead_of_next {
branch::advance_next(msg.next, repo_details, addr, net) branch::advance_next(
.into_actor(self) msg.next,
.wait(ctx); msg.dev_commit_history,
repo_details,
repo_config,
addr,
)
.into_actor(self)
.wait(ctx);
} else if self.webhook_id.is_none() { } else if self.webhook_id.is_none() {
webhook::register(repo_details, addr, net) webhook::register(repo_details, addr, net)
.into_actor(self) .into_actor(self)

View file

@ -34,9 +34,7 @@ pub async fn check_next(
#[derive(Debug)] #[derive(Debug)]
pub enum Status { pub enum Status {
#[allow(dead_code)] // TODO: (#13) remove this when we have results from the forge
Pass, Pass,
#[allow(dead_code)] // TODO: (#13) remove this when we have results from the forge
Fail, Fail,
Pending, Pending,
} }

View file

@ -13,6 +13,14 @@ pub struct CommitHistories {
pub struct Commit { pub struct Commit {
pub sha: String, pub sha: String,
} }
impl Commit {
#[cfg(test)]
pub fn new(sha: &str) -> Self {
Self {
sha: sha.to_string(),
}
}
}
impl Display for Commit { impl Display for Commit {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
write!(f, "{}", self.sha) write!(f, "{}", self.sha)