Skip to content

Commit 15bb968

Browse files
cectonranile
authored andcommitted
Helper to build changelog (yewstack#1845)
1 parent 8829486 commit 15bb968

File tree

7 files changed

+267
-9
lines changed

7 files changed

+267
-9
lines changed

CHANGELOG.md

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,38 @@
11
# Changelog
22

3+
## **0.18.0** *(2021-05-15)*
4+
5+
#### Changelog
6+
7+
- #### 🛠 Fixes
8+
9+
- Fix missing redirects (#1640). [[@siku2] [#1640]](https://github.com/yewstack/yew/pull/1640)
10+
11+
- #### ⚡️ Features
12+
13+
- Implicit optional attributes (#1637). [[@siku2] [#1637]](https://github.com/yewstack/yew/pull/1637)
14+
- Added callback_future_once in yewtil (#1696) (#1712). [[@fraillt] [#1696]](https://github.com/yewstack/yew/pull/1696)
15+
- Added relevant examples section to the docs (#1695). [[@oOBoomberOo] [#1695]](https://github.com/yewstack/yew/pull/1695)
16+
- Enable std feature for indexmap (#1709). [[@jstarry] [#1709]](https://github.com/yewstack/yew/pull/1709)
17+
- Added missing KeyboardService re-export (#1694). [[@SOF3] [#1694]](https://github.com/yewstack/yew/pull/1694)
18+
- Clean up component lifecycle state (#1700). [[@jstarry] [#1700]](https://github.com/yewstack/yew/pull/1700)
19+
- Move top-level crates to packages/<crate> (#1680). [[@philip-peterson] [#1680]](https://github.com/yewstack/yew/pull/1680)
20+
- Refactor component lifecycle event handling (#1692). [[@jstarry] [#1692]](https://github.com/yewstack/yew/pull/1692)
21+
- Prune stdweb examples to reduce maintenance burden (#1690). [[@jstarry] [#1690]](https://github.com/yewstack/yew/pull/1690)
22+
- Refactor html module into new component submodule (#1689). [[@jstarry] [#1689]](https://github.com/yewstack/yew/pull/1689)
23+
- Rename internal Agent structs to match Component (#1688). [[@jstarry] [#1688]](https://github.com/yewstack/yew/pull/1688)
24+
- Revert "Update rand requirement from 0.7 to 0.8 (#1682)" (#1684). [[@siku2] [#1682]](https://github.com/yewstack/yew/pull/1682)
25+
- Add discussion link to issue selector (#1674). [[@jstarry] [#1674]](https://github.com/yewstack/yew/pull/1674)
26+
- Update link to Material Design Components (#1662). [[@TapioT] [#1662]](https://github.com/yewstack/yew/pull/1662)
27+
- Extract Classes to a separate macro (#1601). [[@cecton] [#1601]](https://github.com/yewstack/yew/pull/1601)
28+
- Improve the "keyed_list" example (#1650). [[@titaneric] [#1650]](https://github.com/yewstack/yew/pull/1650)
29+
- Apply new Clippy lints, examples deployment, and stdweb integration tests (#1651). [[@siku2] [#1651]](https://github.com/yewstack/yew/pull/1651)
30+
- Add documentation for component children (#1616). [[@K4rakara] [#1616]](https://github.com/yewstack/yew/pull/1616)
31+
- More ergonomic use state 1505 (#1630). [[@mattferrin] [#1630]](https://github.com/yewstack/yew/pull/1630)
32+
- Remove Drop bound from Task trait (#1627). [[@siku2] [#1627]](https://github.com/yewstack/yew/pull/1627)
33+
- Document dynamic tag names (#1628). [[@siku2] [#1628]](https://github.com/yewstack/yew/pull/1628)
34+
- Add a macro for building properties outside of html! (#1599). [[@siku2] [#1599]](https://github.com/yewstack/yew/pull/1599)
35+
336
## **0.17.4** *(2020-10-18)*
437

538
#### Changelog
@@ -301,7 +334,7 @@ Lastly, take note that API docs on https://docs.rs/yew will be using the `"web_s
301334
- Implemented `PartialEq` for `ChildrenRenderer` to allow `children` comparison. [[@jstarry], [#916](https://github.com/yewstack/yew/pull/916)]
302335
- Reduced restrictions on `ComponentLink` methods to improve `Future` support. [[@jplatte], [#931](https://github.com/yewstack/yew/pull/931)]
303336
- Added `referrer`, `referrer_policy` and `integrity` to `FetchOptions`. [[@leo-lb], [#931](https://github.com/yewstack/yew/pull/931)]
304-
337+
305338
- #### 🛠 Fixes
306339

307340
- Fixed touch event listeners. [[@AlephAlpha], [#872](https://github.com/yewstack/yew/pull/872)]

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,7 @@ members = [
4444
"examples/todomvc",
4545
"examples/two_apps",
4646
"examples/webgl",
47+
48+
# Release tools
49+
"packages/changelog",
4750
]

packages/changelog/Cargo.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[package]
2+
name = "changelog"
3+
version = "0.1.0"
4+
authors = ["Cecile Tonglet <[email protected]>"]
5+
edition = "2018"
6+
7+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8+
9+
[dependencies]
10+
anyhow = "1"
11+
chrono = "0.4"
12+
git2 = "0.13"
13+
regex = "1"
14+
reqwest = { version = "0.11", features = ["blocking", "json"] }
15+
serde = { version = "1", features = ["derive"] }
16+
structopt = "0.3"

packages/changelog/src/main.rs

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
use anyhow::{bail, Context, Result};
2+
use serde::Deserialize;
3+
use std::collections::HashMap;
4+
use std::fs;
5+
use std::io;
6+
use std::io::Write;
7+
use structopt::StructOpt;
8+
9+
fn main() -> Result<()> {
10+
Cli::from_args().run()
11+
}
12+
13+
#[derive(StructOpt)]
14+
pub struct Cli {
15+
/// From commit.
16+
from: String,
17+
18+
/// To commit.
19+
#[structopt(default_value = "HEAD")]
20+
to: String,
21+
22+
#[structopt(skip = Self::open_repository())]
23+
repo: git2::Repository,
24+
25+
#[structopt(skip)]
26+
github_users: GitHubUsers,
27+
28+
#[structopt(skip = regex::Regex::new(r"\(#(\d+)\)").unwrap())]
29+
re_issue: regex::Regex,
30+
}
31+
32+
impl Cli {
33+
fn open_repository() -> git2::Repository {
34+
match git2::Repository::open(".") {
35+
Err(err) => {
36+
eprintln!("Error: could not open repository: {}", err);
37+
std::process::exit(1);
38+
}
39+
Ok(repo) => repo,
40+
}
41+
}
42+
43+
fn run(&mut self) -> Result<()> {
44+
let mut old_changelog =
45+
fs::File::open("CHANGELOG.md").context("could not open CHANGELOG.md for reading")?;
46+
let mut f = fs::OpenOptions::new()
47+
.write(true)
48+
.create(true)
49+
.truncate(true)
50+
.open("CHANGELOG.md.new")
51+
.context("could not open CHANGELOG.md.new for writing")?;
52+
53+
let mut revwalk = self.repo.revwalk()?;
54+
revwalk.set_sorting(git2::Sort::TOPOLOGICAL)?;
55+
56+
let from_object = self
57+
.repo
58+
.revparse_single(&self.from)
59+
.context("Could not find `from` revision")?;
60+
let to_object = self
61+
.repo
62+
.revparse_single(&self.to)
63+
.context("Could not find `to` revision")?;
64+
revwalk.hide(from_object.id())?;
65+
revwalk.push(to_object.id())?;
66+
67+
let mut logs = Vec::new();
68+
for oid in revwalk {
69+
let oid = oid?;
70+
let commit = self.repo.find_commit(oid)?;
71+
let first_line = commit
72+
.message()
73+
.context("Invalid UTF-8 in commit message")?
74+
.lines()
75+
.next()
76+
.context("Missing commit message")?;
77+
let author = commit.author();
78+
let email = author.email().context("Missing author's email")?;
79+
80+
if email.contains("dependabot") {
81+
continue;
82+
}
83+
84+
let issue =
85+
if let Some(issue) = self.re_issue.captures(first_line).map(|x| x[1].to_string()) {
86+
issue
87+
} else {
88+
eprintln!("Missing issue for commit: {}", oid);
89+
continue;
90+
};
91+
92+
let user = self
93+
.github_users
94+
.find_user_by_commit_author(email, oid.to_string())
95+
.with_context(|| format!("Could not find GitHub user for commit: {}", oid))?;
96+
97+
logs.push((first_line.to_owned(), user.to_owned(), issue.to_owned()));
98+
}
99+
100+
let (features, fixes): (Vec<_>, Vec<_>) = logs
101+
.into_iter()
102+
.partition(|(msg, _, _)| msg.to_lowercase().contains("fix"));
103+
104+
writeln!(
105+
f,
106+
"## ✨ **x.y.z** *({})*",
107+
chrono::Utc::now().format("%Y-%m-%d")
108+
)?;
109+
writeln!(f)?;
110+
writeln!(f, "#### Changelog")?;
111+
writeln!(f)?;
112+
113+
writeln!(f, "- #### 🛠 Fixes")?;
114+
writeln!(f)?;
115+
for (msg, user, issue) in fixes {
116+
writeln!(
117+
f,
118+
" - {msg}. [[@{user}] [#{issue}]](https://github.com/yewstack/yew/pull/{issue})",
119+
msg = msg,
120+
user = user,
121+
issue = issue
122+
)?;
123+
}
124+
125+
writeln!(f, "- #### ⚡️ Features")?;
126+
writeln!(f)?;
127+
for (msg, user, issue) in features {
128+
writeln!(
129+
f,
130+
" - {msg}. [[@{user}] [#{issue}]](https://github.com/yewstack/yew/pull/{issue})",
131+
msg = msg,
132+
user = user,
133+
issue = issue
134+
)?;
135+
}
136+
137+
writeln!(f)?;
138+
io::copy(&mut old_changelog, &mut f)?;
139+
140+
drop(old_changelog);
141+
drop(f);
142+
143+
fs::remove_file("CHANGELOG.md").context("Could not delete CHANGELOG.md")?;
144+
fs::rename("CHANGELOG.md.new", "CHANGELOG.md")
145+
.context("Could not replace CHANGELOG.md with CHANGELOG.md.new")?;
146+
147+
Ok(())
148+
}
149+
}
150+
151+
#[derive(Debug, Default)]
152+
pub struct GitHubUsers {
153+
cache: HashMap<String, Option<String>>,
154+
}
155+
156+
impl GitHubUsers {
157+
pub fn find_user_by_commit_author(
158+
&mut self,
159+
key: impl Into<String>,
160+
commit: impl AsRef<str>,
161+
) -> Option<&str> {
162+
self.cache
163+
.entry(key.into())
164+
.or_insert_with(|| match Self::query_commit(commit) {
165+
Ok(value) => value,
166+
Err(err) => {
167+
eprintln!("Error: {}", err);
168+
None
169+
}
170+
})
171+
.as_deref()
172+
}
173+
174+
fn query_commit(q: impl AsRef<str>) -> Result<Option<String>> {
175+
std::thread::sleep(std::time::Duration::from_secs(1));
176+
let client = reqwest::blocking::Client::new();
177+
let resp = client
178+
.get(format!(
179+
"https://api.github.com/repos/yewstack/yew/commits/{}",
180+
q.as_ref(),
181+
))
182+
.header("user-agent", "reqwest")
183+
.header("accept", "application/vnd.github.v3+json")
184+
.send()?;
185+
let status = resp.status();
186+
if !status.is_success() {
187+
if let Some(remaining) = resp.headers().get("x-ratelimit-remaining") {
188+
if remaining == "0" {
189+
bail!("GitHub API limit reached.");
190+
}
191+
}
192+
bail!("GitHub API request error: {}", status);
193+
}
194+
let body = resp.json::<GitHubCommitApi>()?;
195+
196+
Ok(Some(body.author.login))
197+
}
198+
}
199+
200+
#[derive(Deserialize, Debug)]
201+
pub struct GitHubCommitApi {
202+
author: GitHubCommitAuthorApi,
203+
}
204+
205+
#[derive(Deserialize, Debug)]
206+
pub struct GitHubCommitAuthorApi {
207+
login: String,
208+
}

packages/yew-macro/src/props/component.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,9 @@ impl ComponentProps {
144144
});
145145

146146
let set_children = children_renderer.map(|children| {
147-
Some(quote_spanned! {props_ty.span()=>
147+
quote_spanned! {props_ty.span()=>
148148
.children(#children)
149-
})
149+
}
150150
});
151151

152152
quote_spanned! {props_ty.span()=>
@@ -159,9 +159,9 @@ impl ComponentProps {
159159
Self::With(with_props) => {
160160
let ident = Ident::new("__yew_props", props_ty.span());
161161
let set_children = children_renderer.map(|children| {
162-
Some(quote_spanned! {props_ty.span()=>
162+
quote_spanned! {props_ty.span()=>
163163
#ident.children = #children;
164-
})
164+
}
165165
});
166166

167167
let expr = &with_props.expr;

packages/yew/src/html/component/lifecycle.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ mod tests {
248248
#[derive(Clone, Properties, Default)]
249249
struct Props {
250250
lifecycle: Rc<RefCell<Vec<String>>>,
251+
#[allow(dead_code)]
251252
#[cfg(feature = "wasm_test")]
252253
create_message: Option<bool>,
253254
update_message: RefCell<Option<bool>>,

packages/yew/src/virtual_dom/vtag.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -213,10 +213,7 @@ impl VTag {
213213
&mut self,
214214
listeners: impl IntoIterator<Item = Option<Rc<dyn Listener>>>,
215215
) {
216-
self.listeners = listeners
217-
.into_iter()
218-
.filter_map(std::convert::identity)
219-
.collect();
216+
self.listeners = listeners.into_iter().flatten().collect();
220217
}
221218

222219
/// Every render it removes all listeners and attach it back later

0 commit comments

Comments
 (0)