Skip to content

Commit 81ffa81

Browse files
authored
Merge pull request #2109 from Urgau/ping-project-goals-improv
Execute `ping-goals` command in the background to avoid timeout and infinite retries
2 parents 1cdce75 + 6bc5bfb commit 81ffa81

File tree

3 files changed

+56
-21
lines changed

3 files changed

+56
-21
lines changed

src/main.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ async fn serve_req(
151151
payload.extend_from_slice(&chunk);
152152
}
153153

154+
log::info!("/zulip-hook request body: {:?}", str::from_utf8(&payload));
154155
let req = match serde_json::from_slice(&payload) {
155156
Ok(r) => r,
156157
Err(e) => {
@@ -167,7 +168,7 @@ async fn serve_req(
167168
return Ok(Response::builder()
168169
.status(StatusCode::OK)
169170
.header("Content-Type", "application/json")
170-
.body(Body::from(triagebot::zulip::respond(&ctx, req).await))
171+
.body(Body::from(triagebot::zulip::respond(ctx, req).await))
171172
.unwrap());
172173
}
173174
if req.uri.path() != "/github-hook" {

src/zulip.rs

Lines changed: 53 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use anyhow::{Context as _, format_err};
2222
use rust_team_data::v1::{TeamKind, TeamMember};
2323
use std::cmp::Reverse;
2424
use std::fmt::Write as _;
25+
use std::sync::Arc;
2526
use subtle::ConstantTimeEq;
2627
use tracing as log;
2728

@@ -88,7 +89,7 @@ struct Response {
8889
/// Top-level handler for Zulip webhooks.
8990
///
9091
/// Returns a JSON response.
91-
pub async fn respond(ctx: &Context, req: Request) -> String {
92+
pub async fn respond(ctx: Arc<Context>, req: Request) -> String {
9293
let content = match process_zulip_request(ctx, req).await {
9394
Ok(None) => {
9495
return serde_json::to_string(&ResponseNotRequired {
@@ -123,7 +124,7 @@ pub fn get_token_from_env() -> Result<String, anyhow::Error> {
123124
/// Processes a Zulip webhook.
124125
///
125126
/// Returns a string of the response, or None if no response is needed.
126-
async fn process_zulip_request(ctx: &Context, req: Request) -> anyhow::Result<Option<String>> {
127+
async fn process_zulip_request(ctx: Arc<Context>, req: Request) -> anyhow::Result<Option<String>> {
127128
let expected_token = get_token_from_env()?;
128129
if !bool::from(req.token.as_bytes().ct_eq(expected_token.as_bytes())) {
129130
anyhow::bail!("Invalid authorization.");
@@ -148,7 +149,7 @@ async fn process_zulip_request(ctx: &Context, req: Request) -> anyhow::Result<Op
148149
}
149150

150151
async fn handle_command<'a>(
151-
ctx: &'a Context,
152+
ctx: Arc<Context>,
152153
mut gh_id: u64,
153154
command: &'a str,
154155
message_data: &'a Message,
@@ -188,6 +189,8 @@ async fn handle_command<'a>(
188189
}
189190

190191
let cmd = parse_cli::<ChatCommand, _>(words.into_iter())?;
192+
tracing::info!("command parsed to {cmd:?} (impersonated: {impersonated})");
193+
191194
let output = match &cmd {
192195
ChatCommand::Acknowledge { identifier } => {
193196
acknowledge(&ctx, gh_id, identifier.into()).await
@@ -201,10 +204,12 @@ async fn handle_command<'a>(
201204
}
202205
ChatCommand::Whoami => whoami_cmd(&ctx, gh_id).await,
203206
ChatCommand::Lookup(cmd) => lookup_cmd(&ctx, cmd).await,
204-
ChatCommand::Work(cmd) => workqueue_commands(ctx, gh_id, cmd).await,
205-
ChatCommand::PingGoals(args) => ping_goals_cmd(ctx, gh_id, &args).await,
207+
ChatCommand::Work(cmd) => workqueue_commands(&ctx, gh_id, cmd).await,
208+
ChatCommand::PingGoals(args) => {
209+
ping_goals_cmd(ctx.clone(), gh_id, message_data, &args).await
210+
}
206211
ChatCommand::DocsUpdate => trigger_docs_update(message_data, &ctx.zulip),
207-
ChatCommand::TeamStats { name } => team_status_cmd(ctx, &name).await,
212+
ChatCommand::TeamStats { name } => team_status_cmd(&ctx, &name).await,
208213
};
209214

210215
let output = output?;
@@ -249,7 +254,10 @@ async fn handle_command<'a>(
249254
if cmd_index >= words.len() {
250255
return Ok(Some("Unknown command".to_string()));
251256
}
257+
252258
let cmd = parse_cli::<StreamCommand, _>(words[cmd_index..].into_iter().copied())?;
259+
tracing::info!("command parsed to {cmd:?}");
260+
253261
match cmd {
254262
StreamCommand::EndTopic => post_waiter(&ctx, message_data, WaitingMessage::end_topic())
255263
.await
@@ -262,29 +270,55 @@ async fn handle_command<'a>(
262270
StreamCommand::Read => post_waiter(&ctx, message_data, WaitingMessage::start_reading())
263271
.await
264272
.map_err(|e| format_err!("Failed to await at this time: {e:?}")),
265-
StreamCommand::PingGoals(args) => ping_goals_cmd(ctx, gh_id, &args).await,
273+
StreamCommand::PingGoals(args) => ping_goals_cmd(ctx, gh_id, message_data, &args).await,
266274
StreamCommand::DocsUpdate => trigger_docs_update(message_data, &ctx.zulip),
267275
}
268276
}
269277
}
270278

271279
async fn ping_goals_cmd(
272-
ctx: &Context,
280+
ctx: Arc<Context>,
273281
gh_id: u64,
282+
message: &Message,
274283
args: &PingGoalsArgs,
275284
) -> anyhow::Result<Option<String>> {
276285
if project_goals::check_project_goal_acl(&ctx.team, gh_id).await? {
277-
ping_project_goals_owners(
278-
&ctx.github,
279-
&ctx.zulip,
280-
&ctx.team,
281-
false,
282-
args.threshold as i64,
283-
&format!("on {}", args.next_update),
284-
)
285-
.await
286-
.map_err(|e| format_err!("Failed to await at this time: {e:?}"))?;
287-
Ok(None)
286+
let args = args.clone();
287+
let message = message.clone();
288+
tokio::spawn(async move {
289+
let res = ping_project_goals_owners(
290+
&ctx.github,
291+
&ctx.zulip,
292+
&ctx.team,
293+
false,
294+
args.threshold as i64,
295+
&format!("on {}", args.next_update),
296+
)
297+
.await;
298+
299+
let status = match res {
300+
Ok(_res) => "OK".to_string(),
301+
Err(err) => {
302+
tracing::error!("ping_project_goals_owners: {err:?}");
303+
format!("ERROR\n\n```\n{err:#?}\n```\n")
304+
}
305+
};
306+
307+
let res = MessageApiRequest {
308+
recipient: message.sender_to_recipient(),
309+
content: &format!("End pinging project groups owners: {status}"),
310+
}
311+
.send(&ctx.zulip)
312+
.await;
313+
314+
if let Err(err) = res {
315+
tracing::error!(
316+
"error sending project goals ping reply: {err:?} for status: {status}"
317+
);
318+
}
319+
});
320+
321+
Ok(Some("Started pinging project groups owners...".to_string()))
288322
} else {
289323
Err(format_err!(
290324
"That command is only permitted for those running the project-goal program.",

src/zulip/commands.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ pub enum StreamCommand {
165165
DocsUpdate,
166166
}
167167

168-
#[derive(clap::Parser, Debug, PartialEq)]
168+
#[derive(clap::Parser, Debug, PartialEq, Clone)]
169169
pub struct PingGoalsArgs {
170170
/// Number of days before an update is considered stale
171171
pub threshold: u64,

0 commit comments

Comments
 (0)