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 fcdb10a7ff
2 changed files with 100 additions and 60 deletions

View File

@ -2,8 +2,7 @@ pub trait GitlabEventExt {
fn project(&self) -> &Project; fn project(&self) -> &Project;
fn r#ref(&self) -> &str; fn r#ref(&self) -> &str;
fn user(&self) -> &str; fn user(&self) -> &str;
fn url(&self) -> String; fn titles(&self) -> Vec<String>;
fn title(&self) -> String;
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
@ -70,6 +69,7 @@ impl MergeRequestAction {
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
pub struct MergeRequestObjectAttributes { pub struct MergeRequestObjectAttributes {
pub iid: u64,
pub target_branch: String, pub target_branch: String,
pub source_branch: String, pub source_branch: String,
pub title: String, pub title: String,
@ -158,7 +158,7 @@ pub enum GitlabEvent {
#[serde(rename = "pipeline")] #[serde(rename = "pipeline")]
Pipeline { Pipeline {
object_attributes: PipelineObjectAttributes, object_attributes: PipelineObjectAttributes,
merge_request: PipelineMergeRequest, merge_request: Option<PipelineMergeRequest>,
user: User, user: User,
project: Project, project: Project,
}, },
@ -192,56 +192,101 @@ impl GitlabEventExt for GitlabEvent {
} }
} }
fn url(&self) -> String { fn titles(&self) -> Vec<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 find_commit<'a>(commits: &'a Vec<Commit>, sha: &str) -> Option<&'a Commit> { fn find_commit<'a>(commits: &'a Vec<Commit>, sha: &str) -> Option<&'a Commit> {
commits.iter().find(|commit| commit.id == sha) commits.iter().find(|commit| commit.id == sha)
} }
match self { match self {
GitlabEvent::Push { after, commits, .. } => find_commit(commits, &after) GitlabEvent::Push {
.map(|commit| commit.title.clone()) after,
.unwrap_or_else(|| "New commit(s) pushed".to_string()), 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 { GitlabEvent::TagPush {
checkout_sha, commits, .. r#ref,
} => find_commit(commits, &checkout_sha) checkout_sha,
.iter() project,
.fold("New tag pushed".to_string(), |accum, commit| { commits,
format!("{} ({})", accum, commit.title) ..
}), } => {
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, .. } => { 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 { GitlabEvent::Pipeline {
object_attributes, object_attributes,
merge_request, merge_request,
.. ..
} => { } => {
let title = object_attributes.name.as_ref().unwrap_or(&merge_request.title); let title = object_attributes
format!("Pipeline {}: {}", object_attributes.status.as_str(), title) .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)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;

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)) 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 project = event.project();
let r = event.r#ref(); let refname = event::parse_ref(event.r#ref());
let refname = if r.starts_with("refs/") { event
let parts = r.split('/').collect::<Vec<_>>(); .titles()
if parts.len() > 2 { .iter()
parts.into_iter().skip(2).collect::<Vec<_>>().join("/").to_string() .map(|title| {
} else { format!(
drop(parts); "\\[{}\\] `{}` *{}* {}",
r.to_string() project.path_with_namespace,
} refname,
} else { event.user(),
r.to_string() title,
}; )
format!( })
"*{}* {} **{}** [{}]({})", .collect()
project.path_with_namespace,
refname,
event.user(),
event.title(),
event.url()
)
} }
async fn handle_gitlab_event( 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 room = ensure_matrix_room_joined(matrix_client, room_id).await?;
let msg = build_gitlab_message(&event); for msg in build_gitlab_messages(&event) {
debug!("Sending message to {}: {}", room_id, msg); debug!("Sending message to {}: {}", room_id, msg);
let msg_content = RoomMessageEventContent::text_markdown(&msg); let msg_content = RoomMessageEventContent::text_markdown(&msg);
room.send(msg_content, None).await?; room.send(msg_content, None).await?;
}
Ok(()) Ok(())
} }