Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't panic when there is no zulip token #1767

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading