Make messages look better and publish multiple for push events

For push events, publish one message per commit pushed, up to a limit of
15.
This commit is contained in:
Brian Tarricone 2023-09-16 02:35:56 -07:00
parent 33d3313927
commit 3daab2f3f8
2 changed files with 101 additions and 61 deletions

View File

@ -2,8 +2,7 @@ pub trait GitlabEventExt {
fn project(&self) -> &Project;
fn r#ref(&self) -> &str;
fn user(&self) -> &str;
fn url(&self) -> String;
fn title(&self) -> String;
fn titles(&self) -> Vec<String>;
}
#[derive(Debug, Deserialize)]
@ -70,6 +69,7 @@ impl MergeRequestAction {
#[derive(Debug, Deserialize)]
pub struct MergeRequestObjectAttributes {
pub iid: u64,
pub target_branch: String,
pub source_branch: String,
pub title: String,
@ -158,7 +158,7 @@ pub enum GitlabEvent {
#[serde(rename = "pipeline")]
Pipeline {
object_attributes: PipelineObjectAttributes,
merge_request: PipelineMergeRequest,
merge_request: Option<PipelineMergeRequest>,
user: User,
project: Project,
},
@ -192,56 +192,101 @@ impl GitlabEventExt for GitlabEvent {
}
}
fn url(&self) -> String {
let url = match self {
GitlabEvent::Push { after, project, .. } => format!("{}/-/commits/{}", project.web_url, after),
GitlabEvent::TagPush {
r#ref,
checkout_sha,
project,
..
} => {
let refname = r#ref.split('/').into_iter().last().unwrap_or(checkout_sha);
format!("{}/-/tags/{}", project.web_url, refname)
}
GitlabEvent::MergeRequest { object_attributes, .. } => object_attributes.url.clone(),
GitlabEvent::Pipeline { object_attributes, .. } => object_attributes.url.clone(),
};
url.replace("http://", "https://").to_string()
}
fn title(&self) -> String {
fn titles(&self) -> Vec<String> {
fn find_commit<'a>(commits: &'a Vec<Commit>, sha: &str) -> Option<&'a Commit> {
commits.iter().find(|commit| commit.id == sha)
}
match self {
GitlabEvent::Push { after, commits, .. } => find_commit(commits, &after)
.map(|commit| commit.title.clone())
.unwrap_or_else(|| "New commit(s) pushed".to_string()),
GitlabEvent::Push {
after,
project,
commits,
..
} => {
const MAX_COMMITS: usize = 15; // TODO: make configurable
commits.iter().fold(Vec::new(), |mut titles, commit| {
if titles.len() < MAX_COMMITS {
titles.push(format!("[**pushed** {}]({})", commit.title, commit.url));
if titles.len() == MAX_COMMITS {
titles.push(format!(
"[**pushed** {} more commits]({}/-/compare/{}...{})",
commits.len() - MAX_COMMITS,
project.web_url,
commit.id,
after
));
}
}
titles
})
}
GitlabEvent::TagPush {
checkout_sha, commits, ..
} => find_commit(commits, &checkout_sha)
.iter()
.fold("New tag pushed".to_string(), |accum, commit| {
format!("{} ({})", accum, commit.title)
}),
r#ref,
checkout_sha,
project,
commits,
..
} => {
let title = format!(
"**tagged** {}",
find_commit(commits, &checkout_sha)
.map(|commit| &commit.title)
.unwrap_or(checkout_sha)
);
let url = format!("{}/-/tags/{}", project.web_url, parse_ref(r#ref));
vec![markdown_link(&title, &url)]
}
GitlabEvent::MergeRequest { object_attributes, .. } => {
format!("MR {}: {}", object_attributes.action.as_str(), object_attributes.title)
let title = format!(
"MR !{} **{}**: {}",
object_attributes.iid,
object_attributes.action.as_str(),
object_attributes.title
);
vec![markdown_link(&title, &object_attributes.url)]
}
GitlabEvent::Pipeline {
object_attributes,
merge_request,
..
} => {
let title = object_attributes.name.as_ref().unwrap_or(&merge_request.title);
format!("Pipeline {}: {}", object_attributes.status.as_str(), title)
let title = object_attributes
.name
.as_ref()
.map(|n| n.clone())
.or(merge_request.as_ref().map(|mr| mr.title.clone()))
.iter()
.fold(
format!("Pipeline **{}**", object_attributes.status.as_str()),
|accum, title| format!("{}: {}", accum, title),
);
vec![markdown_link(&title, &object_attributes.url)]
}
}
}
}
#[inline]
fn markdown_link(title: &String, url: &String) -> String {
format!("[{}]({})", title, url)
}
pub fn parse_ref(r#ref: &str) -> String {
if r#ref.starts_with("refs/") {
let parts = r#ref.split('/').collect::<Vec<_>>();
if parts.len() > 2 {
parts.into_iter().skip(2).collect::<Vec<_>>().join("/").to_string()
} else {
r#ref.to_string()
}
} else {
r#ref.to_string()
}
}
#[cfg(test)]
mod test {
use super::*;
@ -357,7 +402,7 @@ mod test {
assert_eq!(object_attributes.name, Some("Pipeline for branch: master".to_string()));
assert_eq!(object_attributes.r#ref, "master");
assert_eq!(object_attributes.status, PipelineStatus::Failed);
assert_eq!(merge_request.title, "Test");
assert_eq!(merge_request.unwrap().title, "Test");
assert_eq!(user.name, "Administrator");
}
_ => panic!("not a pipeline event"),

View File

@ -105,28 +105,22 @@ async fn ensure_matrix_room_joined(matrix_client: &Client, room_id: &OwnedRoomOr
room.ok_or_else(|| anyhow!("Unable to join room {}", room_id))
}
fn build_gitlab_message(event: &GitlabEvent) -> String {
fn build_gitlab_messages(event: &GitlabEvent) -> Vec<String> {
let project = event.project();
let r = event.r#ref();
let refname = if r.starts_with("refs/") {
let parts = r.split('/').collect::<Vec<_>>();
if parts.len() > 2 {
parts.into_iter().skip(2).collect::<Vec<_>>().join("/").to_string()
} else {
drop(parts);
r.to_string()
}
} else {
r.to_string()
};
format!(
"*{}* {} **{}** [{}]({})",
project.path_with_namespace,
refname,
event.user(),
event.title(),
event.url()
)
let refname = event::parse_ref(event.r#ref());
event
.titles()
.iter()
.map(|title| {
format!(
"\\[{}\\] `{}` *{}* {}",
project.path_with_namespace,
refname,
event.user(),
title,
)
})
.collect()
}
async fn handle_gitlab_event(
@ -145,10 +139,11 @@ async fn handle_gitlab_event(
}
let room = ensure_matrix_room_joined(matrix_client, room_id).await?;
let msg = build_gitlab_message(&event);
debug!("Sending message to {}: {}", room_id, msg);
let msg_content = RoomMessageEventContent::text_markdown(&msg);
room.send(msg_content, None).await?;
for msg in build_gitlab_messages(&event) {
debug!("Sending message to {}: {}", room_id, msg);
let msg_content = RoomMessageEventContent::text_markdown(&msg);
room.send(msg_content, None).await?;
}
Ok(())
}