Skip to content

Commit 7d9db90

Browse files
authored
Merge pull request #136 from nikomatsakis/status-updates
code to generate status updates
2 parents 4b08957 + cff802d commit 7d9db90

File tree

5 files changed

+140
-0
lines changed

5 files changed

+140
-0
lines changed

Cargo.lock

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mdbook-goals/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@ walkdir = "2.5.0"
1717
rust_team_data = { git = "https://github.com/rust-lang/team" }
1818
lazy_static = "1.5.0"
1919
progress_bar = "1.0.5"
20+
chrono = "0.4.38"

mdbook-goals/src/gh/issues.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::{
33
process::Command,
44
};
55

6+
use chrono::NaiveDate;
67
use serde::{Deserialize, Serialize};
78

89
use crate::util::comma;
@@ -67,6 +68,15 @@ pub enum ExistingIssueState {
6768
Closed,
6869
}
6970

71+
impl std::fmt::Display for ExistingIssueState {
72+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73+
match self {
74+
ExistingIssueState::Open => write!(f, "open"),
75+
ExistingIssueState::Closed => write!(f, "closed"),
76+
}
77+
}
78+
}
79+
7080
pub fn list_issue_titles_in_milestone(
7181
repository: &str,
7282
timeframe: &str,
@@ -239,4 +249,9 @@ impl ExistingGithubComment {
239249
pub fn is_automated_comment(&self) -> bool {
240250
self.body.trim() == LOCK_TEXT
241251
}
252+
253+
pub fn created_at_date(&self) -> NaiveDate {
254+
NaiveDate::parse_from_str(&self.created_at, "%Y-%m-%dT%H:%M:%SZ")
255+
.expect("failed to parse date")
256+
}
242257
}

mdbook-goals/src/main.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ mod re;
1616
mod rfc;
1717
mod team;
1818
mod team_repo;
19+
mod updates;
1920
mod util;
2021

2122
#[derive(StructOpt, Debug)]
@@ -80,6 +81,21 @@ enum Command {
8081
#[structopt(long)]
8182
json_path: Option<PathBuf>,
8283
},
84+
85+
/// Generate markdown with the list of updates for each tracking issue.
86+
/// Collects updates
87+
Updates {
88+
/// Milestone for which we generate tracking issue data (e.g., `2024h2`).
89+
milestone: String,
90+
91+
/// Start date for comments.
92+
/// If not given, defaults to 1 week before the start of this month.
93+
start_date: Option<chrono::NaiveDate>,
94+
95+
/// End date for comments.
96+
/// If not given, no end date.
97+
end_date: Option<chrono::NaiveDate>,
98+
},
8399
}
84100

85101
fn main() -> anyhow::Result<()> {
@@ -128,6 +144,13 @@ fn main() -> anyhow::Result<()> {
128144
} => {
129145
json::generate_json(&opt.repository, &milestone, json_path)?;
130146
}
147+
Command::Updates {
148+
milestone,
149+
start_date,
150+
end_date,
151+
} => {
152+
updates::updates(&opt.repository, milestone, start_date, end_date)?;
153+
}
131154
}
132155

133156
Ok(())

mdbook-goals/src/updates.rs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
use chrono::{Datelike, NaiveDate};
2+
3+
use crate::{
4+
gh::issues::{list_issue_titles_in_milestone, ExistingGithubComment},
5+
util::comma,
6+
};
7+
8+
pub fn updates(
9+
repo: &str,
10+
milestone: &str,
11+
start_date: &Option<NaiveDate>,
12+
end_date: &Option<NaiveDate>,
13+
) -> anyhow::Result<()> {
14+
let issues = list_issue_titles_in_milestone(repo, milestone)?;
15+
16+
let filter = Filter {
17+
start_date: match start_date {
18+
Some(date) => date.clone(),
19+
None => default_start_date(),
20+
},
21+
end_date,
22+
};
23+
24+
for (title, issue) in issues {
25+
let total_updates = issue
26+
.comments
27+
.iter()
28+
.filter(|c| !c.is_automated_comment())
29+
.count();
30+
31+
println!(
32+
"# {title} (#{number})",
33+
title = title,
34+
number = issue.number
35+
);
36+
println!("");
37+
println!("| Metadata | |");
38+
println!("| --- | --- |");
39+
println!(
40+
"| Assigned to | {assignees} |",
41+
assignees = comma(&issue.assignees),
42+
);
43+
println!("| State | {state} |", state = issue.state);
44+
println!("| Total updates | {total_updates} |");
45+
println!(
46+
"| Date of most recent update | {date} |",
47+
date = issue
48+
.comments
49+
.last()
50+
.map(|c| c.created_at_date().to_string())
51+
.unwrap_or("none".to_string())
52+
);
53+
54+
let mut comments = issue.comments;
55+
comments.sort_by_key(|c| c.created_at.clone());
56+
comments.retain(|c| filter.matches(c));
57+
58+
println!();
59+
if comments.len() == 0 {
60+
println!("No updates since {date}.", date = filter.start_date);
61+
} else {
62+
for comment in comments {
63+
println!(
64+
"## Update by {author} from {created} ([link]({url}))",
65+
author = comment.author,
66+
created = comment.created_at_date(),
67+
url = comment.url,
68+
);
69+
println!("\n{body}\n", body = comment.body);
70+
}
71+
}
72+
}
73+
74+
Ok(())
75+
}
76+
77+
struct Filter<'f> {
78+
start_date: NaiveDate,
79+
end_date: &'f Option<NaiveDate>,
80+
}
81+
82+
impl Filter<'_> {
83+
fn matches(&self, comment: &ExistingGithubComment) -> bool {
84+
let date = comment.created_at_date();
85+
86+
date >= self.start_date
87+
&& match self.end_date {
88+
Some(end_date) => date <= *end_date,
89+
None => true,
90+
}
91+
}
92+
}
93+
94+
fn default_start_date() -> NaiveDate {
95+
let date = chrono::Utc::now().date_naive();
96+
let start_of_month = NaiveDate::from_ymd_opt(date.year(), date.month(), 1).unwrap();
97+
start_of_month - chrono::Duration::days(7)
98+
}

0 commit comments

Comments
 (0)