Skip to content

Commit 34b52b3

Browse files
committed
feat: added min duaration checking in RepositorConfig
Changes to be committed: modified: src/bors/event.rs modified: src/bors/handlers/workflow.rs modified: src/config.rs modified: src/github/webhook.rs
1 parent 2deb846 commit 34b52b3

File tree

4 files changed

+76
-14
lines changed

4 files changed

+76
-14
lines changed

src/bors/event.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::database::{WorkflowStatus, WorkflowType};
22
use crate::github::{CommitSha, GithubRepoName, GithubUser, PullRequest, PullRequestNumber};
3+
use chrono::Duration;
34
use octocrab::models::RunId;
45

56
#[derive(Debug)]
@@ -88,6 +89,7 @@ pub struct WorkflowCompleted {
8889
pub commit_sha: CommitSha,
8990
pub run_id: RunId,
9091
pub status: WorkflowStatus,
92+
pub running_time: Option<Duration>,
9193
}
9294

9395
#[derive(Debug)]

src/bors/handlers/workflow.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::sync::Arc;
2+
use std::time::Duration;
23

34
use crate::bors::comment::try_build_succeeded_comment;
45
use crate::bors::event::{CheckSuiteCompleted, WorkflowCompleted, WorkflowStarted};
@@ -61,12 +62,29 @@ pub(super) async fn handle_workflow_started(
6162
pub(super) async fn handle_workflow_completed(
6263
repo: Arc<RepositoryState>,
6364
db: Arc<PgDbClient>,
64-
payload: WorkflowCompleted,
65+
mut payload: WorkflowCompleted,
6566
) -> anyhow::Result<()> {
6667
if !is_bors_observed_branch(&payload.branch) {
6768
return Ok(());
6869
}
6970

71+
if let Some(running_time) = payload.running_time {
72+
let running_time_as_duration =
73+
chrono::Duration::to_std(&running_time).unwrap_or(Duration::from_secs(0));
74+
if let Some(min_ci_time) = repo.config.load().min_ci_time {
75+
if running_time_as_duration < min_ci_time {
76+
payload.status = WorkflowStatus::Failure;
77+
tracing::warn!(
78+
"Workflow running time is less than the minimum CI duration: {:?} < {:?}",
79+
running_time_as_duration,
80+
min_ci_time
81+
);
82+
}
83+
}
84+
} else {
85+
tracing::warn!("Running time is not available.");
86+
}
87+
7088
tracing::info!("Updating status of workflow to {:?}", payload.status);
7189
db.update_workflow_status(*payload.run_id, payload.status)
7290
.await?;

src/config.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,23 @@ pub struct RepositoryConfig {
1919
pub timeout: Duration,
2020
#[serde(default, deserialize_with = "deserialize_labels")]
2121
pub labels: HashMap<LabelTrigger, Vec<LabelModification>>,
22+
#[serde(default, deserialize_with = "deserialize_duration_from_secs_opt")]
23+
pub min_ci_time: Option<Duration>,
2224
}
2325

2426
fn default_timeout() -> Duration {
2527
Duration::from_secs(3600)
2628
}
2729

30+
fn deserialize_duration_from_secs_opt<'de, D>(deserializer: D) -> Result<Option<Duration>, D::Error>
31+
where
32+
D: Deserializer<'de>,
33+
{
34+
// Allow null values for the option
35+
let maybe_seconds = Option::<u64>::deserialize(deserializer)?;
36+
Ok(maybe_seconds.map(Duration::from_secs))
37+
}
38+
2839
fn deserialize_duration_from_secs<'de, D>(deserializer: D) -> Result<Duration, D::Error>
2940
where
3041
D: Deserializer<'de>,
@@ -124,7 +135,7 @@ where
124135

125136
#[cfg(test)]
126137
mod tests {
127-
use std::collections::BTreeMap;
138+
use std::{collections::BTreeMap, time::Duration};
128139

129140
use crate::config::{default_timeout, RepositoryConfig};
130141

@@ -142,6 +153,20 @@ mod tests {
142153
assert_eq!(config.timeout.as_secs(), 3600);
143154
}
144155

156+
#[test]
157+
fn deserialize_min_ci_time_empty() {
158+
let content = "";
159+
let config = load_config(content);
160+
assert_eq!(config.min_ci_time, None);
161+
}
162+
163+
#[test]
164+
fn deserialize_min_ci_time() {
165+
let content = "min_ci_time = 3600";
166+
let config = load_config(content);
167+
assert_eq!(config.min_ci_time, Some(Duration::from_secs(3600)));
168+
}
169+
145170
#[test]
146171
fn deserialize_labels() {
147172
let content = r#"[labels]

src/github/webhook.rs

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -273,18 +273,29 @@ fn parse_workflow_run_events(body: &[u8]) -> anyhow::Result<Option<BorsEvent>> {
273273
url: payload.workflow_run.html_url.into(),
274274
},
275275
))),
276-
"completed" => Some(BorsEvent::Repository(
277-
BorsRepositoryEvent::WorkflowCompleted(WorkflowCompleted {
278-
repository: repository_name,
279-
branch: payload.workflow_run.head_branch,
280-
commit_sha: CommitSha(payload.workflow_run.head_sha),
281-
run_id: RunId(payload.workflow_run.id.0),
282-
status: match payload.workflow_run.conclusion.unwrap_or_default().as_str() {
283-
"success" => WorkflowStatus::Success,
284-
_ => WorkflowStatus::Failure,
285-
},
286-
}),
287-
)),
276+
"completed" => {
277+
let running_time = if let (Some(started_at), Some(completed_at)) = (
278+
Some(payload.workflow_run.created_at),
279+
Some(payload.workflow_run.updated_at),
280+
) {
281+
Some(completed_at - started_at)
282+
} else {
283+
None
284+
};
285+
Some(BorsEvent::Repository(
286+
BorsRepositoryEvent::WorkflowCompleted(WorkflowCompleted {
287+
repository: repository_name,
288+
branch: payload.workflow_run.head_branch,
289+
commit_sha: CommitSha(payload.workflow_run.head_sha),
290+
run_id: RunId(payload.workflow_run.id.0),
291+
running_time,
292+
status: match payload.workflow_run.conclusion.unwrap_or_default().as_str() {
293+
"success" => WorkflowStatus::Success,
294+
_ => WorkflowStatus::Failure,
295+
},
296+
}),
297+
))
298+
}
288299
_ => None,
289300
};
290301
Ok(result)
@@ -774,6 +785,12 @@ mod tests {
774785
4900979072,
775786
),
776787
status: Failure,
788+
running_time: Some(
789+
TimeDelta {
790+
secs: 13,
791+
nanos: 0,
792+
},
793+
),
777794
},
778795
),
779796
),

0 commit comments

Comments
 (0)