forked from kemitix/git-next
feat: add short git log graph to notifications
Closes kemitix/git-next#133
This commit is contained in:
parent
8c19680056
commit
ef24cb583c
13 changed files with 140 additions and 28 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -993,6 +993,7 @@ dependencies = [
|
|||
"secrecy",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"take-until",
|
||||
"test-log",
|
||||
"thiserror",
|
||||
"time",
|
||||
|
@ -3635,6 +3636,12 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "take-until"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8bdb6fa0dfa67b38c1e66b7041ba9dcf23b99d8121907cd31c807a332f7a0bbb"
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.10.1"
|
||||
|
|
|
@ -84,6 +84,9 @@ anyhow = "1.0"
|
|||
thiserror = "1.0"
|
||||
pike = "0.1"
|
||||
|
||||
# iters
|
||||
take-until = "0.2"
|
||||
|
||||
# file watcher
|
||||
notify = "6.1"
|
||||
|
||||
|
|
|
@ -297,7 +297,13 @@ Sample payload:
|
|||
"main": "main"
|
||||
},
|
||||
"forge_alias": "jo",
|
||||
"repo_alias": "kxio"
|
||||
"repo_alias": "kxio",
|
||||
"log": [
|
||||
"* 9bfce91 (origin/dev) fix: add log graph to notifications",
|
||||
"| * c37bd2c (origin/next, origin/main) feat: add log graph to notifications",
|
||||
"|/",
|
||||
"* 8c19680 refactor: macros use a more common syntax"
|
||||
]
|
||||
},
|
||||
"timestamp": "1721760933",
|
||||
"type": "branch.dev.not-on-main"
|
||||
|
@ -318,11 +324,16 @@ Sample payload:
|
|||
{
|
||||
"data": {
|
||||
"commit": {
|
||||
"sha": "98abef1af6825f9770d725a681e5cfc09d7fd4f2",
|
||||
"message": "feat: add foo to bar template"
|
||||
"sha": "c37bd2caf6825f9770d725a681e5cfc09d7fd4f2",
|
||||
"message": "feat: add log graph to notifications (1 of 2)"
|
||||
},
|
||||
"forge_alias": "jo",
|
||||
"repo_alias": "kxio"
|
||||
"repo_alias": "kxio",
|
||||
"log": [
|
||||
"* 9bfce91 (origin/dev) feat: add log graph to notifications (2 of 2)",
|
||||
"* c37bd2c (origin/next) feat: add log graph to notifications (1 of 2)",
|
||||
"* 8c19680 (origin/main) refactor: macros use a more common syntax"
|
||||
]
|
||||
},
|
||||
"timestamp": "1721760933",
|
||||
"type": "cicheck.failed"
|
||||
|
|
|
@ -35,6 +35,7 @@ fn short_message(user_notification: &UserNotification) -> String {
|
|||
forge_alias,
|
||||
repo_alias,
|
||||
commit,
|
||||
log: _,
|
||||
} => format!("CI Check Failed: {forge_alias}/{repo_alias}: {commit}"),
|
||||
UserNotification::RepoConfigLoadFailure {
|
||||
forge_alias,
|
||||
|
@ -53,6 +54,7 @@ fn short_message(user_notification: &UserNotification) -> String {
|
|||
main_branch: _,
|
||||
dev_commit: _,
|
||||
main_commit: _,
|
||||
log: _,
|
||||
} => format!("Dev not based on Main: {forge_alias}/{repo_alias}"),
|
||||
};
|
||||
format!("[git-next] {tail}")
|
||||
|
@ -64,6 +66,7 @@ fn full_message(user_notification: &UserNotification) -> String {
|
|||
forge_alias,
|
||||
repo_alias,
|
||||
commit,
|
||||
log,
|
||||
} => {
|
||||
let sha = commit.sha();
|
||||
let message = commit.message();
|
||||
|
@ -71,6 +74,8 @@ fn full_message(user_notification: &UserNotification) -> String {
|
|||
"CI Checks had Failed".to_string(),
|
||||
format!("Forge: {forge_alias}\nRepo : {repo_alias}"),
|
||||
format!("Commit:\n - {sha}\n - {message}"),
|
||||
"Log:".to_string(),
|
||||
log.join("\n"),
|
||||
]
|
||||
.join("\n\n")
|
||||
}
|
||||
|
@ -99,21 +104,16 @@ fn full_message(user_notification: &UserNotification) -> String {
|
|||
repo_alias,
|
||||
dev_branch,
|
||||
main_branch,
|
||||
dev_commit,
|
||||
main_commit,
|
||||
} => {
|
||||
let dev_sha = dev_commit.sha();
|
||||
let dev_message = dev_commit.message();
|
||||
let main_sha = main_commit.sha();
|
||||
let main_message = main_commit.message();
|
||||
[
|
||||
dev_commit: _,
|
||||
main_commit: _,
|
||||
log,
|
||||
} => [
|
||||
format!("The branch '{dev_branch}' is not based on the branch '{main_branch}'."),
|
||||
format!("TODO: Rebase '{dev_branch}' onto '{main_branch}'."),
|
||||
format!("Forge: {forge_alias}\nRepo : {repo_alias}"),
|
||||
format!("{dev_branch}:\n - {dev_sha}\n - {dev_message}"),
|
||||
format!("{main_branch}:\n - {main_sha}\n - {main_message}"),
|
||||
"Log:".to_string(),
|
||||
log.join("\n"),
|
||||
]
|
||||
.join("\n\n")
|
||||
}
|
||||
.join("\n\n"),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//
|
||||
use actix::prelude::*;
|
||||
|
||||
use git_next_core::git::{forge::commit::Status, UserNotification};
|
||||
use git_next_core::git::{forge::commit::Status, graph, UserNotification};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::repo::{
|
||||
|
@ -40,6 +40,7 @@ impl Handler<ReceiveCIStatus> for RepoActor {
|
|||
forge_alias,
|
||||
repo_alias,
|
||||
commit: next,
|
||||
log: graph::log(&self.repo_details),
|
||||
},
|
||||
log.as_ref(),
|
||||
);
|
||||
|
|
|
@ -14,6 +14,7 @@ impl NotifyUser {
|
|||
forge_alias,
|
||||
repo_alias,
|
||||
commit,
|
||||
log,
|
||||
} => json!({
|
||||
"type": "cicheck.failed",
|
||||
"timestamp": timestamp,
|
||||
|
@ -23,7 +24,8 @@ impl NotifyUser {
|
|||
"commit": {
|
||||
"sha": commit.sha(),
|
||||
"message": commit.message()
|
||||
}
|
||||
},
|
||||
"log": **log
|
||||
}
|
||||
}),
|
||||
UserNotification::RepoConfigLoadFailure {
|
||||
|
@ -59,6 +61,7 @@ impl NotifyUser {
|
|||
main_branch,
|
||||
dev_commit,
|
||||
main_commit,
|
||||
log,
|
||||
} => json!({
|
||||
"type": "branch.dev.not-on-main",
|
||||
"timestamp": timestamp,
|
||||
|
@ -78,7 +81,8 @@ impl NotifyUser {
|
|||
"sha": main_commit.sha(),
|
||||
"message": main_commit.message()
|
||||
}
|
||||
}
|
||||
},
|
||||
"log": **log
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
|
|
@ -57,6 +57,9 @@ serde_json = { workspace = true }
|
|||
|
||||
mockall = { workspace = true }
|
||||
|
||||
#iters
|
||||
take-until = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
# Testing
|
||||
assert2 = { workspace = true }
|
||||
|
|
3
crates/core/src/config/graphs.rs
Normal file
3
crates/core/src/config/graphs.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
use crate::newtype;
|
||||
|
||||
newtype!(LogGraph, Vec<String>, "Git log showing branch positions");
|
|
@ -7,6 +7,7 @@ mod forge_config;
|
|||
mod forge_details;
|
||||
mod forge_type;
|
||||
pub mod git_dir;
|
||||
mod graphs;
|
||||
mod host_name;
|
||||
mod registered_webhook;
|
||||
mod remote_url;
|
||||
|
|
69
crates/core/src/git/graph.rs
Normal file
69
crates/core/src/git/graph.rs
Normal file
|
@ -0,0 +1,69 @@
|
|||
//
|
||||
|
||||
use std::borrow::ToOwned;
|
||||
|
||||
use take_until::TakeUntilExt;
|
||||
|
||||
use crate::{newtype, GitDir, RepoBranches};
|
||||
|
||||
use super::RepoDetails;
|
||||
|
||||
newtype!(Log, Vec<String>, Default, Hash, "Git log of branches");
|
||||
|
||||
// create a graph to log relative positions
|
||||
//
|
||||
// ANCESTOR=$(git merge-base --octopus origin/main origin/next origin/dev)
|
||||
// SHORT_ANCESTOR=$(echo $ANCESTOR | cut -b -7)
|
||||
fn ancesstor(gitdir: &GitDir, branches: &RepoBranches) -> Option<String> {
|
||||
let result = std::process::Command::new("/usr/bin/git")
|
||||
.current_dir(gitdir.to_path_buf())
|
||||
.args([
|
||||
"merge-base",
|
||||
"--octopus",
|
||||
format!("origin/{}", branches.main()).as_str(),
|
||||
format!("origin/{}", branches.next()).as_str(),
|
||||
format!("origin/{}", branches.dev()).as_str(),
|
||||
])
|
||||
.output();
|
||||
if let Ok(output) = result {
|
||||
return String::from_utf8_lossy(output.stdout.as_slice())
|
||||
.split('\n')
|
||||
.take(1)
|
||||
.map(|line| line.chars().take(7).collect::<String>())
|
||||
.collect::<Vec<_>>()
|
||||
.first()
|
||||
.cloned();
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
// git log --oneline --graph --decorate origin/main origin/dev origin/next | awk "1;/$SHORT_ANCESTOR/{exit}"
|
||||
pub fn log(repo_details: &RepoDetails) -> Log {
|
||||
if let Some(repo_config) = &repo_details.repo_config {
|
||||
let branches = repo_config.branches();
|
||||
let result = std::process::Command::new("/usr/bin/git")
|
||||
.current_dir(repo_details.gitdir().to_path_buf())
|
||||
.args([
|
||||
"log",
|
||||
"--oneline",
|
||||
"--graph",
|
||||
"--decorate",
|
||||
format!("origin/{}", branches.main()).as_str(),
|
||||
format!("origin/{}", branches.next()).as_str(),
|
||||
format!("origin/{}", branches.dev()).as_str(),
|
||||
])
|
||||
.output();
|
||||
if let Ok(output) = result {
|
||||
if let Some(ancestor) = ancesstor(repo_details.gitdir(), branches) {
|
||||
return String::from_utf8_lossy(output.stdout.as_slice())
|
||||
.split('\n')
|
||||
.take_until(|line| line.contains(&ancestor))
|
||||
.map(str::trim)
|
||||
.map(ToOwned::to_owned)
|
||||
.collect::<Vec<_>>()
|
||||
.into();
|
||||
}
|
||||
}
|
||||
}
|
||||
Log::default()
|
||||
}
|
|
@ -6,6 +6,7 @@ pub mod forge;
|
|||
mod generation;
|
||||
mod git_ref;
|
||||
mod git_remote;
|
||||
pub mod graph;
|
||||
pub mod push;
|
||||
mod repo_details;
|
||||
pub mod repository;
|
||||
|
|
|
@ -2,12 +2,15 @@
|
|||
use crate::{git::Commit, BranchName, ForgeAlias, RepoAlias};
|
||||
use serde_json::json;
|
||||
|
||||
use super::graph::Log;
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub enum UserNotification {
|
||||
CICheckFailed {
|
||||
forge_alias: ForgeAlias,
|
||||
repo_alias: RepoAlias,
|
||||
commit: Commit,
|
||||
log: Log,
|
||||
},
|
||||
RepoConfigLoadFailure {
|
||||
forge_alias: ForgeAlias,
|
||||
|
@ -26,6 +29,7 @@ pub enum UserNotification {
|
|||
main_branch: BranchName,
|
||||
dev_commit: Commit,
|
||||
main_commit: Commit,
|
||||
log: Log,
|
||||
},
|
||||
}
|
||||
impl UserNotification {
|
||||
|
@ -37,6 +41,7 @@ impl UserNotification {
|
|||
forge_alias,
|
||||
repo_alias,
|
||||
commit,
|
||||
log,
|
||||
} => json!({
|
||||
"type": "cicheck.failed",
|
||||
"timestamp": timestamp,
|
||||
|
@ -46,7 +51,8 @@ impl UserNotification {
|
|||
"commit": {
|
||||
"sha": commit.sha(),
|
||||
"message": commit.message()
|
||||
}
|
||||
},
|
||||
"log": **log
|
||||
}
|
||||
}),
|
||||
Self::RepoConfigLoadFailure {
|
||||
|
@ -59,7 +65,7 @@ impl UserNotification {
|
|||
"data": {
|
||||
"forge_alias": forge_alias,
|
||||
"repo_alias": repo_alias,
|
||||
"reason": reason
|
||||
"reason": reason,
|
||||
}
|
||||
}),
|
||||
Self::WebhookRegistration {
|
||||
|
@ -72,7 +78,7 @@ impl UserNotification {
|
|||
"data": {
|
||||
"forge_alias": forge_alias,
|
||||
"repo_alias": repo_alias,
|
||||
"reason": reason
|
||||
"reason": reason,
|
||||
}
|
||||
}),
|
||||
Self::DevNotBasedOnMain {
|
||||
|
@ -82,6 +88,7 @@ impl UserNotification {
|
|||
main_branch,
|
||||
dev_commit,
|
||||
main_commit,
|
||||
log,
|
||||
} => json!({
|
||||
"type": "branch.dev.not-on-main",
|
||||
"timestamp": timestamp,
|
||||
|
@ -101,7 +108,8 @@ impl UserNotification {
|
|||
"sha": main_commit.sha(),
|
||||
"message": main_commit.message()
|
||||
}
|
||||
}
|
||||
},
|
||||
"log": **log
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//
|
||||
use crate::{
|
||||
git::{self, repository::open::OpenRepositoryLike, RepoDetails, UserNotification},
|
||||
git::{self, graph::log, repository::open::OpenRepositoryLike, RepoDetails, UserNotification},
|
||||
BranchName, RepoConfig,
|
||||
};
|
||||
|
||||
|
@ -61,6 +61,7 @@ pub fn validate(
|
|||
main_branch,
|
||||
dev_commit: dev,
|
||||
main_commit: main,
|
||||
log: log(repo_details),
|
||||
},
|
||||
));
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue