Skip to content

Commit 49024fc

Browse files
Merge pull request #1835 from Mark-Simulacrum/relnotes-issues
Initial implementation of relnotes handler
2 parents c24a22d + 0f059f3 commit 49024fc

File tree

3 files changed

+153
-1
lines changed

3 files changed

+153
-1
lines changed

src/github.rs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,34 @@ impl GithubClient {
166166
let (body, _req_dbg) = self.send_req(req).await?;
167167
Ok(serde_json::from_slice(&body)?)
168168
}
169+
170+
pub(crate) async fn new_issue(
171+
&self,
172+
repo: &IssueRepository,
173+
title: &str,
174+
body: &str,
175+
labels: Vec<String>,
176+
) -> anyhow::Result<NewIssueResponse> {
177+
#[derive(serde::Serialize)]
178+
struct NewIssue<'a> {
179+
title: &'a str,
180+
body: &'a str,
181+
labels: Vec<String>,
182+
}
183+
let url = format!("{}/issues", repo.url(&self));
184+
self.json(self.post(&url).json(&NewIssue {
185+
title,
186+
body,
187+
labels,
188+
}))
189+
.await
190+
.context("failed to create issue")
191+
}
192+
}
193+
194+
#[derive(Debug, serde::Deserialize)]
195+
pub struct NewIssueResponse {
196+
pub number: u64,
169197
}
170198

171199
impl User {
@@ -327,6 +355,7 @@ pub struct Issue {
327355
pub head: Option<CommitBase>,
328356
/// Whether it is open or closed.
329357
pub state: IssueState,
358+
pub milestone: Option<Milestone>,
330359
}
331360

332361
#[derive(Debug, serde::Deserialize, Eq, PartialEq)]
@@ -2509,7 +2538,7 @@ impl GithubClient {
25092538
}
25102539

25112540
/// Set the milestone of an issue or PR.
2512-
async fn set_milestone(
2541+
pub async fn set_milestone(
25132542
&self,
25142543
full_repo_name: &str,
25152544
milestone: &Milestone,

src/handlers.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ mod prioritize;
4343
pub mod project_goals;
4444
pub mod pull_requests_assignment_update;
4545
mod relabel;
46+
mod relnotes;
4647
mod review_requested;
4748
mod review_submitted;
4849
mod rfc_helper;
@@ -107,6 +108,14 @@ pub async fn handle(ctx: &Context, event: &Event) -> Vec<HandlerError> {
107108
);
108109
}
109110

111+
if let Err(e) = relnotes::handle(ctx, event).await {
112+
log::error!(
113+
"failed to process event {:?} with relnotes handler: {:?}",
114+
event,
115+
e
116+
);
117+
}
118+
110119
if let Some(config) = config
111120
.as_ref()
112121
.ok()

src/handlers/relnotes.rs

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
//! This handler implements collecting release notes from issues and PRs that are tagged with
2+
//! `relnotes`. Any such tagging will open a new issue in rust-lang/rust responsible for tracking
3+
//! the inclusion in releases notes.
4+
//!
5+
//! The new issue will be closed when T-release has added the text proposed (tracked in the issue
6+
//! description) into the final release notes PR.
7+
//!
8+
//! The issue description will be edited manually by teams through the GitHub UI -- in the future,
9+
//! we might add triagebot support for maintaining that text via commands or similar.
10+
//!
11+
//! These issues will also be automatically milestoned when their corresponding PR or issue is. In
12+
//! the absence of a milestone, T-release is responsible for ascertaining which release is
13+
//! associated with the issue.
14+
15+
use serde::{Deserialize, Serialize};
16+
17+
use crate::{
18+
db::issue_data::IssueData,
19+
github::{Event, IssuesAction},
20+
handlers::Context,
21+
};
22+
23+
const RELNOTES_KEY: &str = "relnotes";
24+
25+
#[derive(Debug, Default, Deserialize, Serialize)]
26+
struct RelnotesState {
27+
relnotes_issue: Option<u64>,
28+
}
29+
30+
pub async fn handle(ctx: &Context, event: &Event) -> anyhow::Result<()> {
31+
let Event::Issue(e) = event else {
32+
return Ok(());
33+
};
34+
35+
let repo = e.issue.repository();
36+
if !(repo.organization == "rust-lang" && repo.repository == "rust") {
37+
return Ok(());
38+
}
39+
40+
if e.issue
41+
.title
42+
.starts_with("Tracking issue for release notes")
43+
{
44+
// Ignore these issues -- they're otherwise potentially self-recursive.
45+
return Ok(());
46+
}
47+
48+
let mut client = ctx.db.get().await;
49+
let mut state: IssueData<'_, RelnotesState> =
50+
IssueData::load(&mut client, &e.issue, RELNOTES_KEY).await?;
51+
52+
if let Some(paired) = state.data.relnotes_issue {
53+
// Already has a paired release notes issue.
54+
55+
if let IssuesAction::Milestoned = &e.action {
56+
if let Some(milestone) = &e.issue.milestone {
57+
ctx.github
58+
.set_milestone(&e.issue.repository().to_string(), &milestone, paired)
59+
.await?;
60+
}
61+
}
62+
63+
return Ok(());
64+
}
65+
66+
if let IssuesAction::Labeled { label } = &e.action {
67+
if label.name == "relnotes" || label.name == "relnotes-perf" {
68+
let title = format!(
69+
"Tracking issue for release notes of #{}: {}",
70+
e.issue.number, e.issue.title
71+
);
72+
let body = format!(
73+
"
74+
This issue tracks the release notes text for #{}.
75+
76+
- [ ] Issue is nominated for the responsible team (and T-release nomination is removed).
77+
- [ ] Proposed text is drafted by team responsible for underlying change.
78+
- [ ] Issue is nominated for release team review of clarity for wider audience.
79+
- [ ] Release team includes text in release notes/blog posts.
80+
81+
Release notes text:
82+
83+
The section title will be de-duplicated by the release team with other release notes issues.
84+
Prefer to use the standard titles from [previous releases](https://doc.rust-lang.org/nightly/releases.html).
85+
More than one section can be included if needed.
86+
87+
```markdown
88+
# Compatibility Notes
89+
- [{}]({})
90+
```
91+
92+
Release blog section (if any, leave blank if no section is expected):
93+
94+
```markdown
95+
```
96+
",
97+
e.issue.number, e.issue.title, e.issue.html_url,
98+
);
99+
let resp = ctx
100+
.github
101+
.new_issue(
102+
&e.issue.repository(),
103+
&title,
104+
&body,
105+
vec!["relnotes".to_owned(), "I-release-nominated".to_owned()],
106+
)
107+
.await?;
108+
state.data.relnotes_issue = Some(resp.number);
109+
state.save().await?;
110+
}
111+
}
112+
113+
Ok(())
114+
}

0 commit comments

Comments
 (0)