mirror of
https://github.com/yewstack/yew.git
synced 2025-12-08 21:26:25 +00:00
Helper to build changelog (#1845)
This commit is contained in:
parent
1d63395c3b
commit
e99e83ad37
35
CHANGELOG.md
35
CHANGELOG.md
@ -1,5 +1,38 @@
|
||||
# Changelog
|
||||
|
||||
## ✨ **0.18.0** *(2021-05-15)*
|
||||
|
||||
#### Changelog
|
||||
|
||||
- #### 🛠 Fixes
|
||||
|
||||
- Fix missing redirects (#1640). [[@siku2] [#1640]](https://github.com/yewstack/yew/pull/1640)
|
||||
|
||||
- #### ⚡️ Features
|
||||
|
||||
- Implicit optional attributes (#1637). [[@siku2] [#1637]](https://github.com/yewstack/yew/pull/1637)
|
||||
- Added callback_future_once in yewtil (#1696) (#1712). [[@fraillt] [#1696]](https://github.com/yewstack/yew/pull/1696)
|
||||
- Added relevant examples section to the docs (#1695). [[@oOBoomberOo] [#1695]](https://github.com/yewstack/yew/pull/1695)
|
||||
- Enable std feature for indexmap (#1709). [[@jstarry] [#1709]](https://github.com/yewstack/yew/pull/1709)
|
||||
- Added missing KeyboardService re-export (#1694). [[@SOF3] [#1694]](https://github.com/yewstack/yew/pull/1694)
|
||||
- Clean up component lifecycle state (#1700). [[@jstarry] [#1700]](https://github.com/yewstack/yew/pull/1700)
|
||||
- Move top-level crates to packages/<crate> (#1680). [[@philip-peterson] [#1680]](https://github.com/yewstack/yew/pull/1680)
|
||||
- Refactor component lifecycle event handling (#1692). [[@jstarry] [#1692]](https://github.com/yewstack/yew/pull/1692)
|
||||
- Prune stdweb examples to reduce maintenance burden (#1690). [[@jstarry] [#1690]](https://github.com/yewstack/yew/pull/1690)
|
||||
- Refactor html module into new component submodule (#1689). [[@jstarry] [#1689]](https://github.com/yewstack/yew/pull/1689)
|
||||
- Rename internal Agent structs to match Component (#1688). [[@jstarry] [#1688]](https://github.com/yewstack/yew/pull/1688)
|
||||
- Revert "Update rand requirement from 0.7 to 0.8 (#1682)" (#1684). [[@siku2] [#1682]](https://github.com/yewstack/yew/pull/1682)
|
||||
- Add discussion link to issue selector (#1674). [[@jstarry] [#1674]](https://github.com/yewstack/yew/pull/1674)
|
||||
- Update link to Material Design Components (#1662). [[@TapioT] [#1662]](https://github.com/yewstack/yew/pull/1662)
|
||||
- Extract Classes to a separate macro (#1601). [[@cecton] [#1601]](https://github.com/yewstack/yew/pull/1601)
|
||||
- Improve the "keyed_list" example (#1650). [[@titaneric] [#1650]](https://github.com/yewstack/yew/pull/1650)
|
||||
- Apply new Clippy lints, examples deployment, and stdweb integration tests (#1651). [[@siku2] [#1651]](https://github.com/yewstack/yew/pull/1651)
|
||||
- Add documentation for component children (#1616). [[@K4rakara] [#1616]](https://github.com/yewstack/yew/pull/1616)
|
||||
- More ergonomic use state 1505 (#1630). [[@mattferrin] [#1630]](https://github.com/yewstack/yew/pull/1630)
|
||||
- Remove Drop bound from Task trait (#1627). [[@siku2] [#1627]](https://github.com/yewstack/yew/pull/1627)
|
||||
- Document dynamic tag names (#1628). [[@siku2] [#1628]](https://github.com/yewstack/yew/pull/1628)
|
||||
- Add a macro for building properties outside of html! (#1599). [[@siku2] [#1599]](https://github.com/yewstack/yew/pull/1599)
|
||||
|
||||
## ✨ **0.17.4** *(2020-10-18)*
|
||||
|
||||
#### Changelog
|
||||
@ -301,7 +334,7 @@ Lastly, take note that API docs on https://docs.rs/yew will be using the `"web_s
|
||||
- Implemented `PartialEq` for `ChildrenRenderer` to allow `children` comparison. [[@jstarry], [#916](https://github.com/yewstack/yew/pull/916)]
|
||||
- Reduced restrictions on `ComponentLink` methods to improve `Future` support. [[@jplatte], [#931](https://github.com/yewstack/yew/pull/931)]
|
||||
- Added `referrer`, `referrer_policy` and `integrity` to `FetchOptions`. [[@leo-lb], [#931](https://github.com/yewstack/yew/pull/931)]
|
||||
|
||||
|
||||
- #### 🛠 Fixes
|
||||
|
||||
- Fixed touch event listeners. [[@AlephAlpha], [#872](https://github.com/yewstack/yew/pull/872)]
|
||||
|
||||
@ -44,4 +44,7 @@ members = [
|
||||
"examples/todomvc",
|
||||
"examples/two_apps",
|
||||
"examples/webgl",
|
||||
|
||||
# Release tools
|
||||
"packages/changelog",
|
||||
]
|
||||
|
||||
16
packages/changelog/Cargo.toml
Normal file
16
packages/changelog/Cargo.toml
Normal file
@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "changelog"
|
||||
version = "0.1.0"
|
||||
authors = ["Cecile Tonglet <cecile.tonglet@cecton.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1"
|
||||
chrono = "0.4"
|
||||
git2 = "0.13"
|
||||
regex = "1"
|
||||
reqwest = { version = "0.11", features = ["blocking", "json"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
structopt = "0.3"
|
||||
208
packages/changelog/src/main.rs
Normal file
208
packages/changelog/src/main.rs
Normal file
@ -0,0 +1,208 @@
|
||||
use anyhow::{bail, Context, Result};
|
||||
use serde::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::io::Write;
|
||||
use structopt::StructOpt;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
Cli::from_args().run()
|
||||
}
|
||||
|
||||
#[derive(StructOpt)]
|
||||
pub struct Cli {
|
||||
/// From commit.
|
||||
from: String,
|
||||
|
||||
/// To commit.
|
||||
#[structopt(default_value = "HEAD")]
|
||||
to: String,
|
||||
|
||||
#[structopt(skip = Self::open_repository())]
|
||||
repo: git2::Repository,
|
||||
|
||||
#[structopt(skip)]
|
||||
github_users: GitHubUsers,
|
||||
|
||||
#[structopt(skip = regex::Regex::new(r"\(#(\d+)\)").unwrap())]
|
||||
re_issue: regex::Regex,
|
||||
}
|
||||
|
||||
impl Cli {
|
||||
fn open_repository() -> git2::Repository {
|
||||
match git2::Repository::open(".") {
|
||||
Err(err) => {
|
||||
eprintln!("Error: could not open repository: {}", err);
|
||||
std::process::exit(1);
|
||||
}
|
||||
Ok(repo) => repo,
|
||||
}
|
||||
}
|
||||
|
||||
fn run(&mut self) -> Result<()> {
|
||||
let mut old_changelog =
|
||||
fs::File::open("CHANGELOG.md").context("could not open CHANGELOG.md for reading")?;
|
||||
let mut f = fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.open("CHANGELOG.md.new")
|
||||
.context("could not open CHANGELOG.md.new for writing")?;
|
||||
|
||||
let mut revwalk = self.repo.revwalk()?;
|
||||
revwalk.set_sorting(git2::Sort::TOPOLOGICAL)?;
|
||||
|
||||
let from_object = self
|
||||
.repo
|
||||
.revparse_single(&self.from)
|
||||
.context("Could not find `from` revision")?;
|
||||
let to_object = self
|
||||
.repo
|
||||
.revparse_single(&self.to)
|
||||
.context("Could not find `to` revision")?;
|
||||
revwalk.hide(from_object.id())?;
|
||||
revwalk.push(to_object.id())?;
|
||||
|
||||
let mut logs = Vec::new();
|
||||
for oid in revwalk {
|
||||
let oid = oid?;
|
||||
let commit = self.repo.find_commit(oid)?;
|
||||
let first_line = commit
|
||||
.message()
|
||||
.context("Invalid UTF-8 in commit message")?
|
||||
.lines()
|
||||
.next()
|
||||
.context("Missing commit message")?;
|
||||
let author = commit.author();
|
||||
let email = author.email().context("Missing author's email")?;
|
||||
|
||||
if email.contains("dependabot") {
|
||||
continue;
|
||||
}
|
||||
|
||||
let issue =
|
||||
if let Some(issue) = self.re_issue.captures(first_line).map(|x| x[1].to_string()) {
|
||||
issue
|
||||
} else {
|
||||
eprintln!("Missing issue for commit: {}", oid);
|
||||
continue;
|
||||
};
|
||||
|
||||
let user = self
|
||||
.github_users
|
||||
.find_user_by_commit_author(email, oid.to_string())
|
||||
.with_context(|| format!("Could not find GitHub user for commit: {}", oid))?;
|
||||
|
||||
logs.push((first_line.to_owned(), user.to_owned(), issue.to_owned()));
|
||||
}
|
||||
|
||||
let (features, fixes): (Vec<_>, Vec<_>) = logs
|
||||
.into_iter()
|
||||
.partition(|(msg, _, _)| msg.to_lowercase().contains("fix"));
|
||||
|
||||
writeln!(
|
||||
f,
|
||||
"## ✨ **x.y.z** *({})*",
|
||||
chrono::Utc::now().format("%Y-%m-%d")
|
||||
)?;
|
||||
writeln!(f)?;
|
||||
writeln!(f, "#### Changelog")?;
|
||||
writeln!(f)?;
|
||||
|
||||
writeln!(f, "- #### 🛠 Fixes")?;
|
||||
writeln!(f)?;
|
||||
for (msg, user, issue) in fixes {
|
||||
writeln!(
|
||||
f,
|
||||
" - {msg}. [[@{user}] [#{issue}]](https://github.com/yewstack/yew/pull/{issue})",
|
||||
msg = msg,
|
||||
user = user,
|
||||
issue = issue
|
||||
)?;
|
||||
}
|
||||
|
||||
writeln!(f, "- #### ⚡️ Features")?;
|
||||
writeln!(f)?;
|
||||
for (msg, user, issue) in features {
|
||||
writeln!(
|
||||
f,
|
||||
" - {msg}. [[@{user}] [#{issue}]](https://github.com/yewstack/yew/pull/{issue})",
|
||||
msg = msg,
|
||||
user = user,
|
||||
issue = issue
|
||||
)?;
|
||||
}
|
||||
|
||||
writeln!(f)?;
|
||||
io::copy(&mut old_changelog, &mut f)?;
|
||||
|
||||
drop(old_changelog);
|
||||
drop(f);
|
||||
|
||||
fs::remove_file("CHANGELOG.md").context("Could not delete CHANGELOG.md")?;
|
||||
fs::rename("CHANGELOG.md.new", "CHANGELOG.md")
|
||||
.context("Could not replace CHANGELOG.md with CHANGELOG.md.new")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct GitHubUsers {
|
||||
cache: HashMap<String, Option<String>>,
|
||||
}
|
||||
|
||||
impl GitHubUsers {
|
||||
pub fn find_user_by_commit_author(
|
||||
&mut self,
|
||||
key: impl Into<String>,
|
||||
commit: impl AsRef<str>,
|
||||
) -> Option<&str> {
|
||||
self.cache
|
||||
.entry(key.into())
|
||||
.or_insert_with(|| match Self::query_commit(commit) {
|
||||
Ok(value) => value,
|
||||
Err(err) => {
|
||||
eprintln!("Error: {}", err);
|
||||
None
|
||||
}
|
||||
})
|
||||
.as_deref()
|
||||
}
|
||||
|
||||
fn query_commit(q: impl AsRef<str>) -> Result<Option<String>> {
|
||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
let client = reqwest::blocking::Client::new();
|
||||
let resp = client
|
||||
.get(format!(
|
||||
"https://api.github.com/repos/yewstack/yew/commits/{}",
|
||||
q.as_ref(),
|
||||
))
|
||||
.header("user-agent", "reqwest")
|
||||
.header("accept", "application/vnd.github.v3+json")
|
||||
.send()?;
|
||||
let status = resp.status();
|
||||
if !status.is_success() {
|
||||
if let Some(remaining) = resp.headers().get("x-ratelimit-remaining") {
|
||||
if remaining == "0" {
|
||||
bail!("GitHub API limit reached.");
|
||||
}
|
||||
}
|
||||
bail!("GitHub API request error: {}", status);
|
||||
}
|
||||
let body = resp.json::<GitHubCommitApi>()?;
|
||||
|
||||
Ok(Some(body.author.login))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct GitHubCommitApi {
|
||||
author: GitHubCommitAuthorApi,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct GitHubCommitAuthorApi {
|
||||
login: String,
|
||||
}
|
||||
@ -144,9 +144,9 @@ impl ComponentProps {
|
||||
});
|
||||
|
||||
let set_children = children_renderer.map(|children| {
|
||||
Some(quote_spanned! {props_ty.span()=>
|
||||
quote_spanned! {props_ty.span()=>
|
||||
.children(#children)
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
quote_spanned! {props_ty.span()=>
|
||||
@ -159,9 +159,9 @@ impl ComponentProps {
|
||||
Self::With(with_props) => {
|
||||
let ident = Ident::new("__yew_props", props_ty.span());
|
||||
let set_children = children_renderer.map(|children| {
|
||||
Some(quote_spanned! {props_ty.span()=>
|
||||
quote_spanned! {props_ty.span()=>
|
||||
#ident.children = #children;
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
let expr = &with_props.expr;
|
||||
|
||||
@ -234,6 +234,7 @@ mod tests {
|
||||
#[derive(Clone, Properties, Default)]
|
||||
struct Props {
|
||||
lifecycle: Rc<RefCell<Vec<String>>>,
|
||||
#[allow(dead_code)]
|
||||
#[cfg(feature = "wasm_test")]
|
||||
create_message: Option<bool>,
|
||||
update_message: RefCell<Option<bool>>,
|
||||
|
||||
@ -213,10 +213,7 @@ impl VTag {
|
||||
&mut self,
|
||||
listeners: impl IntoIterator<Item = Option<Rc<dyn Listener>>>,
|
||||
) {
|
||||
self.listeners = listeners
|
||||
.into_iter()
|
||||
.filter_map(std::convert::identity)
|
||||
.collect();
|
||||
self.listeners = listeners.into_iter().flatten().collect();
|
||||
}
|
||||
|
||||
/// Every render it removes all listeners and attach it back later
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user