Skip to content

Commit 89968f4

Browse files
authored
Merge pull request #299 from nikomatsakis/why-this-goal-help-wanted
include 'help wanted" and "tl;ldr" comments
2 parents c869852 + 63b866e commit 89968f4

File tree

8 files changed

+225
-44
lines changed

8 files changed

+225
-44
lines changed

crates/rust-project-goals-cli-llm/src/templates.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,13 @@ pub struct UpdatesGoal {
7373
/// True if the issue is closed.
7474
pub is_closed: bool,
7575

76+
/// True if there are "help wanted" comments OR the TL;DR includes a help wanted request.
77+
pub has_help_wanted: bool,
78+
79+
/// If there are comments that include ["help wanted"](`rust_project_goals::re::HELP_WANTED`)
80+
/// comments, those comments are included here.
81+
pub help_wanted: Vec<HelpWanted>,
82+
7683
/// Markdown with update text (bullet list)
7784
pub comments: Vec<ExistingGithubComment>,
7885

@@ -81,4 +88,15 @@ pub struct UpdatesGoal {
8188

8289
/// Progress towards the goal
8390
pub progress: Progress,
91+
92+
/// TL;DR comment (if any, empty string if none)
93+
pub tldr: Option<String>,
94+
95+
/// Contents of a "Why this goal?" section in the tracking issue (empty string if not present)
96+
pub why_this_goal: String,
97+
}
98+
99+
#[derive(Serialize, Debug)]
100+
pub struct HelpWanted {
101+
pub text: String,
84102
}

crates/rust-project-goals-cli-llm/src/updates.rs

Lines changed: 86 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
use anyhow::Context;
22
use chrono::{Datelike, NaiveDate};
3+
use rust_project_goals::markwaydown;
4+
use rust_project_goals::re::{HELP_WANTED, TLDR};
35
use rust_project_goals::util::comma;
46
use rust_project_goals_json::GithubIssueState;
57
use std::io::Write;
68
use std::path::Path;
79
use std::process::{Command, Stdio};
810

9-
use crate::templates::{self, Updates, UpdatesGoal};
11+
use crate::templates::{self, HelpWanted, Updates, UpdatesGoal};
1012
use rust_project_goals::gh::issues::ExistingGithubIssue;
1113
use rust_project_goals::gh::{
1214
issue_id::{IssueId, Repository},
@@ -92,6 +94,11 @@ async fn prepare_goals(
9294
continue;
9395
}
9496

97+
let issue_id = IssueId {
98+
repository: repository.clone(),
99+
number: issue.number,
100+
};
101+
95102
let title = &issue.title;
96103

97104
progress_bar::print_progress_bar_info(
@@ -107,26 +114,99 @@ async fn prepare_goals(
107114
comments.sort_by_key(|c| c.created_at.clone());
108115
comments.retain(|c| !c.is_automated_comment() && filter.matches(c));
109116

117+
let tldr = tldr(&issue_id, &mut comments)?;
118+
119+
let (has_help_wanted, help_wanted) = help_wanted(&issue_id, &tldr, &comments)?;
120+
121+
let why_this_goal = why_this_goal(&issue_id, issue)?;
122+
110123
result.push(UpdatesGoal {
111124
title: title.clone(),
112125
issue_number: issue.number,
113126
issue_assignees: comma(&issue.assignees),
114-
issue_url: IssueId {
115-
repository: repository.clone(),
116-
number: issue.number,
117-
}
118-
.url(),
127+
issue_url: issue_id.url(),
119128
progress,
129+
has_help_wanted,
130+
help_wanted,
120131
is_closed: issue.state == GithubIssueState::Closed,
121132
num_comments: comments.len(),
122133
comments,
134+
tldr,
135+
why_this_goal,
123136
});
124137

125138
progress_bar::inc_progress_bar();
126139
}
127140
Ok(result)
128141
}
129142

143+
/// Search for a TL;DR comment. If one is found, remove it and return the text.
144+
fn tldr(
145+
_issue_id: &IssueId,
146+
comments: &mut Vec<ExistingGithubComment>,
147+
) -> anyhow::Result<Option<String>> {
148+
let Some(index) = comments.iter().position(|c| c.body.starts_with(TLDR)) else {
149+
return Ok(None);
150+
};
151+
152+
let comment = comments.remove(index);
153+
Ok(Some(comment.body[TLDR.len()..].trim().to_string()))
154+
}
155+
156+
/// Search for comments that talk about help being wanted and extract that
157+
fn help_wanted(
158+
_issue_id: &IssueId,
159+
tldr: &Option<String>,
160+
comments: &[ExistingGithubComment],
161+
) -> anyhow::Result<(bool, Vec<HelpWanted>)> {
162+
use std::fmt::Write;
163+
164+
let mut help_wanted = vec![];
165+
166+
let tldr_has_help_wanted = tldr.as_deref().unwrap_or("").lines().any(|line| HELP_WANTED.is_match(line));
167+
168+
for comment in comments {
169+
let mut lines = comment.body.split('\n').peekable();
170+
171+
// Look for a line that says "Help wanted" at the front.
172+
// Then extract the rest of that line along with subsequent lines until we find a blank line.
173+
while lines.peek().is_some() {
174+
while let Some(line) = lines.next() {
175+
if let Some(c) = HELP_WANTED.captures(line) {
176+
help_wanted.push(HelpWanted {
177+
text: c["text"].to_string()
178+
});
179+
break;
180+
}
181+
}
182+
183+
while let Some(line) = lines.next() {
184+
if line.trim().is_empty() {
185+
break;
186+
} else {
187+
let last = help_wanted.len() - 1;
188+
writeln!(&mut help_wanted[last].text, "{line}")?;
189+
}
190+
}
191+
}
192+
}
193+
194+
Ok((tldr_has_help_wanted || !help_wanted.is_empty(), help_wanted))
195+
}
196+
197+
fn why_this_goal(
198+
issue_id: &IssueId,
199+
issue: &ExistingGithubIssue,
200+
) -> anyhow::Result<String> {
201+
let sections = markwaydown::parse_text(issue_id.url(), &issue.body)?;
202+
for section in sections {
203+
if section.title == "Why this goal?" {
204+
return Ok(section.text.trim().to_string());
205+
}
206+
}
207+
return Ok("".to_string());
208+
}
209+
130210
struct Filter<'f> {
131211
start_date: NaiveDate,
132212
end_date: &'f Option<NaiveDate>,

crates/rust-project-goals/src/markwaydown.rs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,14 @@ pub struct Table {
3131
pub rows: Vec<Vec<String>>,
3232
}
3333

34-
pub fn parse(path: &Path) -> anyhow::Result<Vec<Section>> {
34+
pub fn parse(path: impl AsRef<Path>) -> anyhow::Result<Vec<Section>> {
35+
let path = path.as_ref();
3536
let text = std::fs::read_to_string(path)?;
37+
parse_text(path, &text)
38+
}
39+
40+
pub fn parse_text(path: impl AsRef<Path>, text: &str) -> anyhow::Result<Vec<Section>> {
41+
let path: &Path = path.as_ref();
3642
let mut result = vec![];
3743
let mut open_section = None;
3844
let mut open_table = None;
@@ -54,11 +60,14 @@ pub fn parse(path: &Path) -> anyhow::Result<Vec<Section>> {
5460
}
5561
CategorizeLine::TableRow(mut row) => {
5662
if open_section.is_none() {
57-
anyhow::bail!(
58-
"{}:{}: markdowwn table outside of any section",
59-
path.display(),
60-
line_num
61-
);
63+
// create an "anonymous" section to house the table
64+
open_section = Some(Section {
65+
line_num,
66+
level: 0,
67+
title: String::new(),
68+
text: String::new(),
69+
tables: vec![],
70+
});
6271
}
6372

6473
if let Some(table) = &mut open_table {

crates/rust-project-goals/src/re.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,13 @@ pub fn is_just(re: &Regex, s: &str) -> bool {
100100
let output = re.replace(s, "X");
101101
output == "X"
102102
}
103+
104+
lazy_static! {
105+
/// If a line within a comment begins with this text, it will be considered a request for help
106+
pub static ref HELP_WANTED: Regex =
107+
Regex::new(r"^(?i:help wanted:|\*\*help wanted:\*\*) (?P<text>.*)")
108+
.unwrap();
109+
}
110+
111+
/// If a comment begins with this text, it will be considered a summary.
112+
pub const TLDR: &str = "TL;DR:";

src/admin/author_updates.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
## Soliciting updates
44

5-
Triagebot can ping project-goal owners for updates. To use it, go to Zulip and execute a command like this.
5+
Triagebot can ping project-goal owners for updates. To use it, go to Zulip and execute a command like this (you need to use an `@` in front of triagebot).
66

77
```
8-
@triagebot ping-goals 14 Oct-21
8+
triagebot ping-goals 14 Oct-21
99
```
1010

1111
The first number (14) is a threshold, it is typically set to the current day of the month (e.g., the above command assumes it is Oct 14). It means "if they have posted a comment in the last 14 days, don't bug them".
@@ -14,14 +14,15 @@ The second string ("Oct-21") is the deadline for updates to be included.
1414

1515
We need to improve this UI.
1616

17-
## Drafting the post
17+
## Filling out the template
1818

19-
The [blog post template][updates template] includes a longer description of the flagship goals as well as a bullet-point or two for each of the other goals -- more than that and the blog post becomes overwhelming, the goal is mostly to let people find the goal they're interested in and click to read the full details. You can fill out the template by hand but you can also use the `cargo rpg updates` ([documented here](./updates.md)) to prepare a rough draft; the tool uses an LLM currently hosted on AWS Bedrock and hence requires setting up an AWS account.
19+
Run the `cargo rpg updates` command to create the blog post template. If running from within vscode, the `--vscode` command will open the result in a fresh tab, which is convenient.
20+
21+
The template will be filled in with the list of flagship goals. Each flagship goal will have their [Why this goal?](./merge_rfc.md#author-the-why-this-goal-sections-for-the-flagship-goals) section auto-inserted from the corresponding tracking issue.
22+
23+
The template will also include the detailed list of updates in a `<details>` section as well as any TL;DR comments left by users.
24+
25+
The update template itself is maintained with handlebars, you will find it [here](https://github.com/rust-lang/rust-project-goals/blob/main/templates/updates.hbs).
2026

21-
```bash
22-
> cargo rpg updates
23-
```
2427

25-
Once the draft is prepared, create a hackmd in the rust-project-goals hackmd team, post it to `#project-goals` and apply edits (particularly for the flagship goals). Then open a PR against [the blog.rust-lang.org repository](https://github.com/rust-lang/blog.rust-lang.org).
2628

27-
[updates template]: https://github.com/rust-lang/rust-project-goals/blob/main/templates/updates.hbs

src/admin/merge_rfc.md

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,31 @@
11
# Merging the RFC
22

3-
Once the RFC is accepted
4-
5-
* Merge the RFC itself
6-
* Run `cargo rpg teams` to prepare adjustment to the teams repository
7-
* In particular to populate the project-goal-owners team
8-
* This will be needed to allow people to author updates on their goals
9-
* Run `cargo rpg issues` to create and update tracking issues
10-
* Continuing goals will be moved to the new milestone
11-
* You can run this tool over and over to keep things synchronized
12-
* Close out of date issues
13-
* For all issues in previous milestone, close them
3+
Once the RFC is accepted, you need to take the following steps.
4+
5+
## Merge the RFC itself
6+
7+
Update the RFC and merge as normal
8+
9+
## Update the teams
10+
11+
Run `cargo rpg teams` to prepare adjustment to the teams repository. You will need to have a checkout of the teams repository somewhere; you pass the command the path to that repo and it will make changes. You can then commit the changes and prepare a PR.
12+
13+
This will create a `project-goal-owners` team containing all the project goal owners. It will also add people to the rust-lang repository. This may trigger them to get invites if they are not already members of the org. You should encourage them to accept those invites. If they don't take these steps you won't be able to assign them to issues and they won't be able to author updates, etc.
14+
15+
## Create the milestone
16+
17+
Next you need to (manually) create a milestone on the rust-project-goals repository with the appropriate name (e.g., `2025h1`). We usually create a paired meta milestone like `2025h1-meta` to track other tasks related to running the program.
18+
19+
## Create tracking issues
20+
21+
Finally, you can create the tracking issues. To do this, you run `cargo rpg issues`. Before doing so, make sure that the metadata for any goals that are continuing from the previous milestone already lists the appropriate tracking issue, otherwise the comment will create a duplicate issue.
22+
23+
You can run the command more than once, it tries to pick up from where it left off. It will adjust the state of all issues to match what is expected.
24+
25+
## Author the "why this goal" sections for the flagship goals
26+
27+
For each flagship goal, you should add a section entitled `## Why this goal?` into the tracking issue. Put in there about a paragraph of text that explains the background for this goal. This text will be included verbatim when you prepare monthly updates, so make it readily understood. Often this is the same text that appeared in the RFC itself.
28+
29+
## Close old tracking issues
30+
31+
Finally, you can go to the previous milestone, find all remaining issues, and close them. These should correspond to goals from the previous session that were not continued into the current one.

src/how_to/report_status.md

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,23 @@ We will recursively open up the "see also" issue and extract checkboxes (or sear
5858

5959
If we don't find any of the above, we will consider your issue either 0% done if it is not yet closed or 100% done if it is.
6060

61-
## Status update comments
61+
## Status update comments and summaries
6262

63-
Status updates are posted as comments on the Github tracking issue. You will receive regular pings on Zulip to author status updates periodically. It's a good idea to take the opportunity to update your [progress checkboxes](#checkboxes) as well.
64-
65-
There is no strict format for these updates but we recommend including the following information:
63+
We encourage you to post regular detailed updates about your progress as comments on the Github tracking issue. There is no strict format for these updates but we recommend including the following information:
6664

6765
* What happened since the last update? Were any key decisions made or milestones achieved?
6866
* What is the next step to get done?
6967
* Are you blocked on anyone or anything?
70-
* Is there any opportunity to others to pitch in and help out?
68+
69+
Status update comments will be included verbatim in a "details" section of the monthly blog post.
70+
71+
### Help wanted comments
72+
73+
If your updates include the text `Help wanted:` or `**Help wanted:**` at the start of a line, then the remainder of that line (and any non-block lines afterwards) will be extracted as a "help wanted" request. This will be highlighted in the monthly blog post.
74+
75+
### Summary comments
76+
77+
Once per month, you will start receiving pings to author a monthly status update. This is a special update that will be included verbatim in the blog post. Unless you have a flagship goal, the monthly status update should be short, no more than 2 or 3 bullet points. For a flagship goal it can be longer. To write your monthly status update, leave a comment that begins with the text "TL;DR:". It's a good idea to take the opportunity to update your [progress checkboxes](#checkboxes) as well, as the status bar in the blog post will be based on those.
7178

7279
## Closing the issue
7380

templates/updates.hbs

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,58 @@
1-
The Rust project is currently working towards a [slate of 26 project
2-
goals](https://rust-lang.github.io/rust-project-goals/2024h2/goals.html), with 3 of them designed as [Flagship
3-
Goals](https://rust-lang.github.io/rust-project-goals/2024h2/goals.html#flagship-goals). This post provides selected
4-
updates on our progress towards these goals (or, in some cases, lack thereof). The full details for any particular goal
5-
are available in its associated [tracking issue on the rust-project-goals
6-
repository](https://github.com/rust-lang/rust-project-goals/milestone/2).
1+
The Rust project is currently working towards a [slate of 26 project goals](https://rust-lang.github.io/rust-project-goals/{{milestone}}/goals.html), with 3 of them designed as [Flagship Goals](https://rust-lang.github.io/rust-project-goals/{{milestone}}/goals.html#flagship-goals). This post provides selected updates on our progress towards these goals (or, in some cases, lack thereof). The full details for any particular goal are available in its associated [tracking issue on the rust-project-goals repository](https://github.com/rust-lang/rust-project-goals/issues?q=is%3Aissue%20state%3Aopen%20label%3AC-tracking-issue).
72

83
## Flagship goals
94

105
{{#each flagship_goals}}
116
{{>introduce_goal}}
7+
8+
<!-- markdown separator -->
9+
10+
**Why this goal?** {{why_this_goal}}
11+
12+
**What has happened?** {{{tldr}}}
13+
14+
<!-- markdown separator -->
15+
16+
{{>goal_comments}}
17+
{{/each}}
18+
19+
## Goals looking for help
20+
21+
{{#each other_goals}}
22+
{{#if has_help_wanted}}
23+
{{>introduce_goal}}
24+
25+
<!-- markdown separator -->
26+
{{{tldr}}}
27+
<!-- markdown separator -->
28+
29+
{{#each help_wanted}}
30+
31+
<!-- markdown separator -->
32+
![Help wanted](https://img.shields.io/badge/Help%20wanted-yellow) {{{text}}}
33+
<!-- markdown separator -->
34+
35+
{{/each}}
36+
37+
<!-- markdown separator -->
38+
1239
{{>goal_comments}}
40+
{{/if}}
1341
{{/each}}
1442

15-
## Other goals
43+
## Other goal updates
1644

1745
{{#each other_goals}}
46+
{{#if has_help_wanted}}
47+
{{else}}
1848
{{>introduce_goal}}
49+
50+
<!-- markdown separator -->
51+
52+
{{{tldr}}}
53+
54+
<!-- markdown separator -->
55+
1956
{{>goal_comments}}
57+
{{/if}}
2058
{{/each}}

0 commit comments

Comments
 (0)