Skip to content

Commit c335263

Browse files
authored
Merge pull request #2005 from Urgau/fix-behind-upstream
Fix `[behind-upstream]` when dealing with subtrees
2 parents 14fefb6 + 788e056 commit c335263

File tree

3 files changed

+58
-86
lines changed

3 files changed

+58
-86
lines changed

src/github.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,18 @@ pub struct FileDiff {
320320
pub diff: String,
321321
}
322322

323+
/// The return from GitHub compare API
324+
#[derive(Debug, serde::Deserialize)]
325+
pub struct GithubCompare {
326+
/// The base commit of the PR
327+
pub base_commit: GithubCommit,
328+
/// The merge base commit
329+
///
330+
/// See <https://git-scm.com/docs/git-merge-base> for more details
331+
pub merge_base_commit: GithubCommit,
332+
// FIXME: Also retrieve and use the files list (see our diff function)
333+
}
334+
323335
impl PullRequestDetails {
324336
pub fn new() -> PullRequestDetails {
325337
PullRequestDetails {
@@ -994,6 +1006,23 @@ impl Issue {
9941006
Ok(Some(diff))
9951007
}
9961008

1009+
/// Returns the comparison of this event.
1010+
///
1011+
/// Returns `None` if the issue is not a PR.
1012+
pub async fn compare(&self, client: &GithubClient) -> anyhow::Result<Option<GithubCompare>> {
1013+
let (before, after) = if let (Some(base), Some(head)) = (&self.base, &self.head) {
1014+
(&base.sha, &head.sha)
1015+
} else {
1016+
return Ok(None);
1017+
};
1018+
1019+
let req = client.get(&format!(
1020+
"{}/compare/{before}...{after}",
1021+
self.repository().url(client)
1022+
));
1023+
Ok(Some(client.json(req).await?))
1024+
}
1025+
9971026
/// Returns the commits from this pull request (no commits are returned if this `Issue` is not
9981027
/// a pull request).
9991028
pub async fn commits(&self, client: &GithubClient) -> anyhow::Result<Vec<GithubCommit>> {

src/handlers/check_commits.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ pub(super) async fn handle(ctx: &Context, event: &Event, config: &Config) -> any
5858
event.issue.number
5959
)
6060
};
61+
let Some(compare) = event.issue.compare(&ctx.github).await? else {
62+
bail!(
63+
"expected issue {} to be a PR, but the compare could not be determined",
64+
event.issue.number
65+
)
66+
};
6167
let commits = event.issue.commits(&ctx.github).await?;
6268

6369
let mut warnings = Vec::new();
@@ -101,7 +107,7 @@ pub(super) async fn handle(ctx: &Context, event: &Event, config: &Config) -> any
101107
.unwrap_or(behind_upstream::DEFAULT_DAYS_THRESHOLD);
102108

103109
if let Some(warning) =
104-
behind_upstream::behind_upstream(age_threshold, event, &ctx.github, &commits).await
110+
behind_upstream::behind_upstream(age_threshold, event, &compare).await
105111
{
106112
warnings.push(warning);
107113
}
Lines changed: 22 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::github::{GithubClient, GithubCommit, IssuesEvent, Repository};
1+
use crate::github::{GithubCompare, IssuesEvent};
22
use tracing as log;
33

44
/// Default threshold for parent commit age in days to trigger a warning
@@ -8,95 +8,32 @@ pub(super) const DEFAULT_DAYS_THRESHOLD: usize = 7;
88
pub(super) async fn behind_upstream(
99
age_threshold: usize,
1010
event: &IssuesEvent,
11-
client: &GithubClient,
12-
commits: &Vec<GithubCommit>,
11+
compare: &GithubCompare,
1312
) -> Option<String> {
1413
log::debug!("Checking if PR #{} is behind upstream", event.issue.number);
1514

16-
let Some(head_commit) = commits.first() else {
17-
return None;
18-
};
15+
// Compute the number of days old the merge base commit is
16+
let commit_date = compare.merge_base_commit.commit.author.date;
17+
let now = chrono::Utc::now().with_timezone(&commit_date.timezone());
18+
let days_old = (now - commit_date).num_days() as usize;
1919

2020
// First try the parent commit age check as it's more accurate
21-
match is_parent_commit_too_old(head_commit, &event.repository, client, age_threshold).await {
22-
Ok(Some(days_old)) => {
23-
log::info!(
24-
"PR #{} has a parent commit that is {} days old",
25-
event.issue.number,
26-
days_old
27-
);
28-
29-
return Some(format!(
30-
r"This PR is based on an upstream commit that is {} days old.
31-
32-
*It's recommended to update your branch according to the [rustc_dev_guide](https://rustc-dev-guide.rust-lang.org/contributing.html#keeping-your-branch-up-to-date).*",
33-
days_old
34-
));
35-
}
36-
Ok(None) => {
37-
// Parent commit is not too old, log and do nothing
38-
log::debug!("PR #{} parent commit is not too old", event.issue.number);
39-
}
40-
Err(e) => {
41-
// Error checking parent commit age, log and do nothing
42-
log::error!(
43-
"Error checking parent commit age for PR #{}: {}",
44-
event.issue.number,
45-
e
46-
);
47-
}
48-
}
49-
50-
None
51-
}
52-
53-
/// Checks if the PR's parent commit is too old.
54-
///
55-
/// This determines if a PR needs updating by examining the first parent of the PR's head commit,
56-
/// which typically represents the base branch commit that the PR is based on.
57-
///
58-
/// If this parent commit is older than the specified threshold, it suggests the PR
59-
/// should be updated/rebased to a more recent version of the base branch.
60-
///
61-
/// Returns:
62-
/// - Ok(Some(days_old)) - If parent commit is older than the threshold
63-
/// - Ok(None)
64-
/// - If there is no parent commit
65-
/// - If parent is within threshold
66-
/// - Err(...) - If an error occurred during processing
67-
pub(super) async fn is_parent_commit_too_old(
68-
commit: &GithubCommit,
69-
repo: &Repository,
70-
client: &GithubClient,
71-
max_days_old: usize,
72-
) -> anyhow::Result<Option<usize>> {
73-
// Get the first parent (it should be from the base branch)
74-
let Some(parent_sha) = commit.parents.get(0).map(|c| c.sha.clone()) else {
75-
return Ok(None);
76-
};
77-
78-
let days_old = commit_days_old(&parent_sha, repo, client).await?;
79-
80-
if days_old > max_days_old {
81-
Ok(Some(days_old))
21+
if days_old > age_threshold {
22+
log::info!(
23+
"PR #{} has a parent commit that is {} days old",
24+
event.issue.number,
25+
days_old
26+
);
27+
28+
Some(format!(
29+
r"This PR is based on an upstream commit that is {} days old.
30+
31+
*It's recommended to update your branch according to the [rustc-dev-guide](https://rustc-dev-guide.rust-lang.org/contributing.html#keeping-your-branch-up-to-date).*",
32+
days_old
33+
))
8234
} else {
83-
Ok(None)
35+
// Parent commit is not too old, log and do nothing
36+
log::debug!("PR #{} parent commit is not too old", event.issue.number);
37+
None
8438
}
8539
}
86-
87-
/// Returns the number of days old the commit is
88-
pub(super) async fn commit_days_old(
89-
sha: &str,
90-
repo: &Repository,
91-
client: &GithubClient,
92-
) -> anyhow::Result<usize> {
93-
// Get the commit details to check its date
94-
let commit: GithubCommit = repo.github_commit(client, &sha).await?;
95-
96-
// compute the number of days old the commit is
97-
let commit_date = commit.commit.author.date;
98-
let now = chrono::Utc::now().with_timezone(&commit_date.timezone());
99-
let days_old = (now - commit_date).num_days() as usize;
100-
101-
Ok(days_old)
102-
}

0 commit comments

Comments
 (0)