Skip to content

Commit

Permalink
Don't panic when there is no zulip token
Browse files Browse the repository at this point in the history
  • Loading branch information
WaffleLapkin committed Jan 30, 2024
1 parent df574c1 commit 87f703b
Show file tree
Hide file tree
Showing 7 changed files with 242 additions and 144 deletions.
1 change: 1 addition & 0 deletions src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ lazy_static::lazy_static! {
};
}

#[derive(Clone)]
pub struct ClientPool {
connections: Arc<Mutex<Vec<tokio_postgres::Client>>>,
permits: Arc<Semaphore>,
Expand Down
3 changes: 3 additions & 0 deletions src/handlers.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::config::{self, Config, ConfigurationError};
use crate::github::{Event, GithubClient, IssueCommentAction, IssuesAction, IssuesEvent};
use crate::zulip::ZulipTokens;
use octocrab::Octocrab;
use parser::command::{assign::AssignCommand, Command, Input};
use std::fmt;
Expand Down Expand Up @@ -291,9 +292,11 @@ command_handlers! {
note: Note,
}

#[derive(Clone)]
pub struct Context {
pub github: GithubClient,
pub db: crate::db::ClientPool,
pub username: String,
pub octocrab: Octocrab,
pub zulip: Option<ZulipTokens>,
}
141 changes: 75 additions & 66 deletions src/handlers/major_change.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,59 +112,63 @@ pub(super) async fn handle_input(
event.issue.number, event.issue.html_url,
),
Invocation::Rename { prev_issue } => {
let issue = &event.issue;
if let Some(ctx) = crate::zulip::ZulipContext::from_ctx(ctx) {
let issue = &event.issue;

let prev_topic = zulip_topic_from_issue(&prev_issue);
let partial_issue = issue.to_zulip_github_reference();
let new_topic = zulip_topic_from_issue(&partial_issue);
let prev_topic = zulip_topic_from_issue(&prev_issue);
let partial_issue = issue.to_zulip_github_reference();
let new_topic = zulip_topic_from_issue(&partial_issue);

let zulip_send_req = crate::zulip::MessageApiRequest {
recipient: crate::zulip::Recipient::Stream {
id: config.zulip_stream,
topic: &prev_topic,
},
content: "The associated GitHub issue has been renamed. Renaming this Zulip topic.",
};
let zulip_send_res = zulip_send_req
.send(&ctx.github.raw())
.await
.context("zulip post failed")?;
let zulip_send_req = crate::zulip::MessageApiRequest {
recipient: crate::zulip::Recipient::Stream {
id: config.zulip_stream,
topic: &prev_topic,
},
content:
"The associated GitHub issue has been renamed. Renaming this Zulip topic.",
};
let zulip_send_res = zulip_send_req
.send(&ctx, &ctx.github.raw())
.await
.context("zulip post failed")?;

let zulip_send_res: crate::zulip::MessageApiResponse = zulip_send_res.json().await?;
let zulip_send_res: crate::zulip::MessageApiResponse =
zulip_send_res.json().await?;

let zulip_update_req = crate::zulip::UpdateMessageApiRequest {
message_id: zulip_send_res.message_id,
topic: Some(&new_topic),
propagate_mode: Some("change_all"),
content: None,
};
zulip_update_req
.send(&ctx.github.raw())
.await
.context("zulip message update failed")?;
let zulip_update_req = crate::zulip::UpdateMessageApiRequest {
message_id: zulip_send_res.message_id,
topic: Some(&new_topic),
propagate_mode: Some("change_all"),
content: None,
};
zulip_update_req
.send(&ctx, &ctx.github.raw())
.await
.context("zulip message update failed")?;

// after renaming the zulip topic, post an additional comment under the old topic with a url to the new, renamed topic
// this is necessary due to the lack of topic permalinks, see https://github.com/zulip/zulip/issues/15290
let new_topic_url = crate::zulip::Recipient::Stream {
id: config.zulip_stream,
topic: &new_topic,
}
.url();
let breadcrumb_comment = format!(
// after renaming the zulip topic, post an additional comment under the old topic with a url to the new, renamed topic
// this is necessary due to the lack of topic permalinks, see https://github.com/zulip/zulip/issues/15290
let new_topic_url = crate::zulip::Recipient::Stream {
id: config.zulip_stream,
topic: &new_topic,
}
.url();
let breadcrumb_comment = format!(
"The associated GitHub issue has been renamed. Please see the [renamed Zulip topic]({}).",
new_topic_url
);
let zulip_send_breadcrumb_req = crate::zulip::MessageApiRequest {
recipient: crate::zulip::Recipient::Stream {
id: config.zulip_stream,
topic: &prev_topic,
},
content: &breadcrumb_comment,
};
zulip_send_breadcrumb_req
.send(&ctx.github.raw())
.await
.context("zulip post failed")?;
let zulip_send_breadcrumb_req = crate::zulip::MessageApiRequest {
recipient: crate::zulip::Recipient::Stream {
id: config.zulip_stream,
topic: &prev_topic,
},
content: &breadcrumb_comment,
};
zulip_send_breadcrumb_req
.send(&ctx, &ctx.github.raw())
.await
.context("zulip post failed")?;
}

return Ok(());
}
Expand Down Expand Up @@ -245,20 +249,21 @@ async fn handle(
) -> anyhow::Result<()> {
let github_req = issue.add_labels(&ctx.github, vec![Label { name: label_to_add }]);

let partial_issue = issue.to_zulip_github_reference();
let zulip_topic = zulip_topic_from_issue(&partial_issue);
if let Some(ctx) = crate::zulip::ZulipContext::from_ctx(ctx) {
let partial_issue = issue.to_zulip_github_reference();
let zulip_topic = zulip_topic_from_issue(&partial_issue);

let zulip_req = crate::zulip::MessageApiRequest {
recipient: crate::zulip::Recipient::Stream {
id: config.zulip_stream,
topic: &zulip_topic,
},
content: &zulip_msg,
};
let zulip_req = crate::zulip::MessageApiRequest {
recipient: crate::zulip::Recipient::Stream {
id: config.zulip_stream,
topic: &zulip_topic,
},
content: &zulip_msg,
};

if new_proposal {
let topic_url = zulip_req.url();
let comment = format!(
if new_proposal {
let topic_url = zulip_req.url();
let comment = format!(
"This issue is not meant to be used for technical discussion. \
There is a Zulip [stream] for that. Use this issue to leave \
procedural comments, such as volunteering to review, indicating that you \
Expand Down Expand Up @@ -290,17 +295,21 @@ async fn handle(
config.open_extra_text.as_deref().unwrap_or_default(),
topic_url
);
issue
.post_comment(&ctx.github, &comment)
.await
.context("post major change comment")?;
}
issue
.post_comment(&ctx.github, &comment)
.await
.context("post major change comment")?;
}

let zulip_req = zulip_req.send(&ctx, &ctx.github.raw());

let zulip_req = zulip_req.send(&ctx.github.raw());
let (gh_res, zulip_res) = futures::join!(github_req, zulip_req);
zulip_res.context("zulip post failed")?;
gh_res.context("label setting failed")?;
} else {
github_req.await.context("label setting failed")?;
}

let (gh_res, zulip_res) = futures::join!(github_req, zulip_req);
zulip_res.context("zulip post failed")?;
gh_res.context("label setting failed")?;
Ok(())
}

Expand Down
62 changes: 32 additions & 30 deletions src/handlers/notify_zulip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,37 +137,39 @@ pub(super) async fn handle_input<'a>(
event: &IssuesEvent,
inputs: Vec<NotifyZulipInput>,
) -> anyhow::Result<()> {
for input in inputs {
let config = &config.labels[&input.label.name];

let mut topic = config.topic.clone();
topic = topic.replace("{number}", &event.issue.number.to_string());
topic = topic.replace("{title}", &event.issue.title);
// Truncate to 60 chars (a Zulip limitation)
let mut chars = topic.char_indices().skip(59);
if let (Some((len, _)), Some(_)) = (chars.next(), chars.next()) {
topic.truncate(len);
topic.push('…');
}

let mut msg = match input.notification_type {
NotificationType::Labeled => config.message_on_add.as_ref().unwrap().clone(),
NotificationType::Unlabeled => config.message_on_remove.as_ref().unwrap().clone(),
NotificationType::Closed => config.message_on_close.as_ref().unwrap().clone(),
NotificationType::Reopened => config.message_on_reopen.as_ref().unwrap().clone(),
};

msg = msg.replace("{number}", &event.issue.number.to_string());
msg = msg.replace("{title}", &event.issue.title);
if let Some(ctx) = crate::zulip::ZulipContext::from_ctx(ctx) {
for input in inputs {
let config = &config.labels[&input.label.name];

let mut topic = config.topic.clone();
topic = topic.replace("{number}", &event.issue.number.to_string());
topic = topic.replace("{title}", &event.issue.title);
// Truncate to 60 chars (a Zulip limitation)
let mut chars = topic.char_indices().skip(59);
if let (Some((len, _)), Some(_)) = (chars.next(), chars.next()) {
topic.truncate(len);
topic.push('…');
}

let zulip_req = crate::zulip::MessageApiRequest {
recipient: crate::zulip::Recipient::Stream {
id: config.zulip_stream,
topic: &topic,
},
content: &msg,
};
zulip_req.send(&ctx.github.raw()).await?;
let mut msg = match input.notification_type {
NotificationType::Labeled => config.message_on_add.as_ref().unwrap().clone(),
NotificationType::Unlabeled => config.message_on_remove.as_ref().unwrap().clone(),
NotificationType::Closed => config.message_on_close.as_ref().unwrap().clone(),
NotificationType::Reopened => config.message_on_reopen.as_ref().unwrap().clone(),
};

msg = msg.replace("{number}", &event.issue.number.to_string());
msg = msg.replace("{title}", &event.issue.title);

let zulip_req = crate::zulip::MessageApiRequest {
recipient: crate::zulip::Recipient::Stream {
id: config.zulip_stream,
topic: &topic,
},
content: &msg,
};
zulip_req.send(&ctx, &ctx.github.raw()).await?;
}
}

Ok(())
Expand Down
12 changes: 10 additions & 2 deletions src/handlers/types_planning_updates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ impl Job for TypesPlanningMeetingThreadOpenJob {
}

async fn run(&self, ctx: &super::Context, _metadata: &serde_json::Value) -> anyhow::Result<()> {
let Some(ctx) = crate::zulip::ZulipContext::from_ctx(ctx) else {
return Ok(());
};

// On the last week of the month, we open a thread on zulip for the next Monday
let today = chrono::Utc::now().date().naive_utc();
let first_monday = today + chrono::Duration::days(7);
Expand All @@ -38,7 +42,7 @@ impl Job for TypesPlanningMeetingThreadOpenJob {
},
content: &message,
};
zulip_req.send(&ctx.github.raw()).await?;
zulip_req.send(&ctx, &ctx.github.raw()).await?;

// Then, we want to schedule the next Thursday after this
let mut thursday = today;
Expand Down Expand Up @@ -88,6 +92,10 @@ pub async fn request_updates(
ctx: &super::Context,
metadata: PlanningMeetingUpdatesPingMetadata,
) -> anyhow::Result<()> {
let Some(ctx) = crate::zulip::ZulipContext::from_ctx(ctx) else {
return Ok(());
};

let gh = &ctx.github;
let types_repo = gh.repository(TYPES_REPO).await?;

Expand Down Expand Up @@ -164,7 +172,7 @@ pub async fn request_updates(
},
content: &message,
};
zulip_req.send(&ctx.github.raw()).await?;
zulip_req.send(&ctx, &ctx.github.raw()).await?;

Ok(())
}
59 changes: 47 additions & 12 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,13 @@ async fn serve_req(
.unwrap());
}
if req.uri.path() == "/zulip-hook" {
let Some(ctx) = triagebot::zulip::ZulipContext::from_ctx(&ctx) else {
return Ok(Response::builder()
.status(StatusCode::SERVICE_UNAVAILABLE)
.body(Body::from("zulip integration is not configured"))
.unwrap());
};

let mut c = body_stream;
let mut payload = Vec::new();
while let Some(chunk) = c.next().await {
Expand Down Expand Up @@ -241,24 +248,52 @@ async fn serve_req(
}

async fn run_server(addr: SocketAddr) -> anyhow::Result<()> {
let pool = db::ClientPool::new();
db::run_migrations(&*pool.get().await)
let db = db::ClientPool::new();
db::run_migrations(&*db.get().await)
.await
.context("database migrations")?;

let gh = github::GithubClient::new_from_env();
let oc = octocrab::OctocrabBuilder::new()
let username = std::env::var("TRIAGEBOT_USERNAME").or_else(|err| match err {
std::env::VarError::NotPresent => Ok("rustbot".to_owned()),
err => Err(err),
})?;

let github = github::GithubClient::new_from_env();

let octocrab = octocrab::OctocrabBuilder::new()
.personal_token(github::default_token_from_env())
.build()
.expect("Failed to build octograb.");
.expect("Failed to build octocrab.");

let zulip = match (
std::env::var("ZULIP_API_TOKEN"),
std::env::var("ZULIP_TOKEN"),
) {
(Ok(api_token), Ok(auth_token)) => Some(triagebot::zulip::ZulipTokens {
api_token,
auth_token,
}),
(Err(std::env::VarError::NotPresent), Err(std::env::VarError::NotPresent) | Ok(_)) => {
tracing::warn!(
"missing `ZULIP_API_TOKEN` env variable, zulip integration won't be enabled"
);
None
}
(Ok(_), Err(std::env::VarError::NotPresent)) => {
tracing::warn!(
"missing `ZULIP_TOKEN` env variable, zulip integration won't be enabled"
);
None
}
(Err(e), _) | (_, Err(e)) => Err(e)?,
};

let ctx = Arc::new(Context {
username: std::env::var("TRIAGEBOT_USERNAME").or_else(|err| match err {
std::env::VarError::NotPresent => Ok("rustbot".to_owned()),
err => Err(err),
})?,
db: pool,
github: gh,
octocrab: oc,
username,
db,
github,
octocrab,
zulip,
});

if !is_scheduled_jobs_disabled() {
Expand Down
Loading

0 comments on commit 87f703b

Please sign in to comment.