From 7fb0ae9def26e6d757c8a53fd49b35144125f3ea Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Tue, 23 Aug 2022 10:32:03 +0200 Subject: [PATCH] Import release automation tool from website repo Having it in the website repository where it is used was convenient, but it's outgrowing it a bit, I find. Here we have much better infrastructure for maintaining Rust code. In addition, there might be use cases for the tool in this repository, in the future. --- Cargo.lock | 360 ++++++++++++++++++++++++++- Cargo.toml | 1 + tools/automator/Cargo.toml | 19 ++ tools/automator/README.md | 19 ++ tools/automator/src/announcement.rs | 122 +++++++++ tools/automator/src/args.rs | 24 ++ tools/automator/src/main.rs | 9 + tools/automator/src/pull_requests.rs | 69 +++++ tools/automator/src/run.rs | 15 ++ 9 files changed, 634 insertions(+), 4 deletions(-) create mode 100644 tools/automator/Cargo.toml create mode 100644 tools/automator/README.md create mode 100644 tools/automator/src/announcement.rs create mode 100644 tools/automator/src/args.rs create mode 100644 tools/automator/src/main.rs create mode 100644 tools/automator/src/pull_requests.rs create mode 100644 tools/automator/src/run.rs diff --git a/Cargo.lock b/Cargo.lock index fbab9a70d..a10682b85 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,6 +18,15 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a13739d7177fbd22bb0ed28badfff9f372f8bef46c863db4e1c6248f6b223b6e" +[[package]] +name = "addr2line" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +dependencies = [ + "gimli", +] + [[package]] name = "adler" version = "1.0.2" @@ -127,6 +136,12 @@ dependencies = [ "x11rb", ] +[[package]] +name = "arc-swap" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "983cd8b9d4b02a6dc6ffa557262eb5858a27a0038ffffe21a0f133eaa819a164" + [[package]] name = "arrayref" version = "0.3.6" @@ -154,6 +169,17 @@ dependencies = [ "libloading", ] +[[package]] +name = "async-trait" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "atomic" version = "0.5.1" @@ -186,6 +212,33 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "automator" +version = "0.1.0" +dependencies = [ + "anyhow", + "chrono", + "clap", + "octocrab", + "tokio", + "url", +] + +[[package]] +name = "backtrace" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base64" version = "0.13.0" @@ -259,9 +312,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.2.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" [[package]] name = "calloop" @@ -331,6 +384,22 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +[[package]] +name = "chrono" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-integer", + "num-traits", + "serde", + "time 0.1.44", + "wasm-bindgen", + "winapi", +] + [[package]] name = "clap" version = "3.2.17" @@ -713,6 +782,12 @@ dependencies = [ "libloading", ] +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + [[package]] name = "downcast-rs" version = "1.2.0" @@ -1258,9 +1333,15 @@ checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", ] +[[package]] +name = "gimli" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" + [[package]] name = "glow" version = "0.11.2" @@ -1477,6 +1558,35 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "hyperx" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5617e92fc2f2501c3e2bc6ce547cad841adba2bae5b921c7e52510beca6d084c" +dependencies = [ + "base64", + "bytes", + "http", + "httpdate", + "language-tags", + "mime", + "percent-encoding", + "unicase", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad2bfd338099682614d3ee3fe0cd72e0b6a41ca6a87f6a74a3bd593c91650501" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "js-sys", + "wasm-bindgen", + "winapi", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -1589,6 +1699,20 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonwebtoken" +version = "8.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa4b4af834c6cfd35d8763d359661b90f2e45d8f750a0849156c7f4671af09c" +dependencies = [ + "base64", + "pem", + "ring", + "serde", + "serde_json", + "simple_asn1", +] + [[package]] name = "khronos-egl" version = "4.1.0" @@ -1620,6 +1744,12 @@ dependencies = [ "libc", ] +[[package]] +name = "language-tags" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" + [[package]] name = "lazy_static" version = "1.4.0" @@ -1779,7 +1909,7 @@ checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" dependencies = [ "libc", "log", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys", ] @@ -2002,6 +2132,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-complex" version = "0.4.2" @@ -2084,6 +2225,15 @@ dependencies = [ "syn", ] +[[package]] +name = "num_threads" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", +] + [[package]] name = "objc" version = "0.2.7" @@ -2123,6 +2273,39 @@ dependencies = [ "objc", ] +[[package]] +name = "object" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +dependencies = [ + "memchr", +] + +[[package]] +name = "octocrab" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db14aefad92da160884fae912983ba22a05afd0437c69d793ac86f639ec7a0fe" +dependencies = [ + "arc-swap", + "async-trait", + "base64", + "bytes", + "cfg-if", + "chrono", + "hyperx", + "jsonwebtoken", + "once_cell", + "reqwest", + "secrecy", + "serde", + "serde_json", + "serde_path_to_error", + "snafu", + "url", +] + [[package]] name = "once_cell" version = "1.13.1" @@ -2317,6 +2500,15 @@ dependencies = [ "syn", ] +[[package]] +name = "pem" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c64931a1a212348ec4f3b4362585eca7159d0d09cbdf4a7f74f02173596fd4" +dependencies = [ + "base64", +] + [[package]] name = "percent-encoding" version = "2.1.0" @@ -2639,6 +2831,21 @@ dependencies = [ "winreg", ] +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + [[package]] name = "robust" version = "0.2.3" @@ -2654,6 +2861,12 @@ dependencies = [ "cc", ] +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + [[package]] name = "rustc-hash" version = "1.1.0" @@ -2727,6 +2940,15 @@ dependencies = [ "tiny-skia", ] +[[package]] +name = "secrecy" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" +dependencies = [ + "zeroize", +] + [[package]] name = "secstr" version = "0.5.0" @@ -2799,6 +3021,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "184c643044780f7ceb59104cef98a5a6f12cb2288a7bc701ab93a362b49fd47d" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -2841,6 +3072,15 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + [[package]] name = "simba" version = "0.7.2" @@ -2854,6 +3094,18 @@ dependencies = [ "wide", ] +[[package]] +name = "simple_asn1" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" +dependencies = [ + "num-bigint", + "num-traits", + "thiserror", + "time 0.3.13", +] + [[package]] name = "slab" version = "0.4.7" @@ -2907,6 +3159,29 @@ dependencies = [ "wayland-client", ] +[[package]] +name = "snafu" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5177903bf45656592d9eb5c0e22f408fc023aae51dbe2088889b71633ba451f2" +dependencies = [ + "backtrace", + "doc-comment", + "snafu-derive", +] + +[[package]] +name = "snafu-derive" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "410b26ed97440d90ced3e2488c868d56a86e2064f5d7d6f417909b286afe25e5" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "socket2" version = "0.4.4" @@ -2936,6 +3211,12 @@ dependencies = [ "smallvec", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "spirv" version = "0.2.0+1.5.4" @@ -3066,6 +3347,35 @@ dependencies = [ "zip", ] +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "time" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db76ff9fa4b1458b3c7f077f3ff9887394058460d21e634355b273aaf11eea45" +dependencies = [ + "itoa", + "libc", + "num_threads", + "time-macros", +] + +[[package]] +name = "time-macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792" + [[package]] name = "tiny-skia" version = "0.7.0" @@ -3119,11 +3429,25 @@ dependencies = [ "mio", "num_cpus", "once_cell", + "parking_lot", "pin-project-lite", + "signal-hook-registry", "socket2", + "tokio-macros", "winapi", ] +[[package]] +name = "tokio-macros" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tokio-native-tls" version = "0.3.0" @@ -3272,6 +3596,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-bidi" version = "0.3.8" @@ -3305,6 +3638,12 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "url" version = "2.2.2" @@ -3315,6 +3654,7 @@ dependencies = [ "idna", "matches", "percent-encoding", + "serde", ] [[package]] @@ -3362,6 +3702,12 @@ dependencies = [ "try-lock", ] +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -3837,6 +4183,12 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +[[package]] +name = "zeroize" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" + [[package]] name = "zip" version = "0.6.2" diff --git a/Cargo.toml b/Cargo.toml index e542aa91f..7eeb8fabc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ members = [ "models/star", "models/test", + "tools/automator", "tools/export-validator", "tools/release-operator", ] diff --git a/tools/automator/Cargo.toml b/tools/automator/Cargo.toml new file mode 100644 index 000000000..5ab1d8ed4 --- /dev/null +++ b/tools/automator/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "automator" +version = "0.1.0" +edition = "2021" +publish = false + +[dependencies] +anyhow = "1.0.62" +chrono = "0.4.22" +octocrab = "0.17.0" +url = "2.2.2" + +[dependencies.clap] +version = "3.2.17" +features = ["derive"] + +[dependencies.tokio] +version = "1.20.1" +features = ["full"] diff --git a/tools/automator/README.md b/tools/automator/README.md new file mode 100644 index 000000000..d4bb0b1a5 --- /dev/null +++ b/tools/automator/README.md @@ -0,0 +1,19 @@ +# `automator` + +CLI tool that automates Fornjot development tasks (mostly release automation). + +## Usage + +Install `automator`, so you can use it in other repositories (most relevantly, the [website repository](https://github.com/hannobraun/www.fornjot.app)): + +``` sh +cargo install --path tools/automator/ +``` + +To learn how to use it, run the following command: + +``` sh +automator --help +``` + +Please also refer to the [release procedure](../../RELEASES.md), which is the main use case for `automator`, as of this writing. diff --git a/tools/automator/src/announcement.rs b/tools/automator/src/announcement.rs new file mode 100644 index 000000000..90031d5bb --- /dev/null +++ b/tools/automator/src/announcement.rs @@ -0,0 +1,122 @@ +use std::{fmt::Write, path::PathBuf}; + +use anyhow::Context; +use chrono::{Date, Datelike, Utc}; +use tokio::{ + fs::{self, File}, + io::AsyncWriteExt, +}; + +use crate::pull_requests::PullRequest; + +pub async fn create_release_announcement( + last_release_date: Date, + version: String, +) -> anyhow::Result<()> { + let now = Utc::now(); + + let year = now.year(); + let week = now.iso_week().week(); + + let pull_requests = + PullRequest::fetch_since_last_release(last_release_date) + .await? + .into_values(); + + let mut file = create_file(year, week).await?; + generate_announcement(week, version, pull_requests, &mut file).await?; + + Ok(()) +} + +async fn create_file(year: i32, week: u32) -> anyhow::Result { + let dir = + PathBuf::from(format!("content/blog/weekly-release/{year}-w{week}")); + let file = dir.join("index.md"); + + fs::create_dir_all(&dir).await.with_context(|| { + format!("Failed to create directory `{}`", dir.display()) + })?; + let file = File::create(&file).await.with_context(|| { + format!("Failed to create file `{}`", file.display()) + })?; + + Ok(file) +} + +async fn generate_announcement( + week: u32, + version: String, + pull_requests: impl IntoIterator, + file: &mut File, +) -> anyhow::Result<()> { + let mut pull_request_links = String::new(); + + for PullRequest { number, html_url } in pull_requests { + let link = format!("[#{number}]: {html_url}\n"); + + pull_request_links.push_str(&link); + } + + let mut buf = String::new(); + write!( + buf, + "\ ++++ +title = \"Weekly Release - 2022-W{week}\" + +[extra] +version = \"{version}\" ++++ + +**TASK: Write introduction.** + + +### Sponsors + +Fornjot is supported by [@webtrax-oz](https://github.com/webtrax-oz), [@lthiery](https://github.com/lthiery), [@Yatekii](https://github.com/Yatekii), [@martindederer](https://github.com/martindederer), [@hobofan](https://github.com/hobofan), [@ahdinosaur](https://github.com/ahdinosaur), [@thawkins](https://github.com/thawkins), [@bollian](https://github.com/bollian), [@rozgo](https://github.com/rozgo), and [my other awesome sponsors](https://github.com/sponsors/hannobraun). Thank you! + +If you want Fornjot to be stable and sustainable long-term, please consider [supporting me](https://github.com/sponsors/hannobraun) too. + + +### End-user improvements + +Improvements to Fornjot and its documentation that are visible to end-users. + +**TASK: Add end-user improvements.** + + +### Ecosystem improvements + +Improvements to the Fornjot ecosystem that are relevant to developers who are building on top of Fornjot components. + +#### `fj-kernel` + +**TASK: Add ecosystem improvements.** + + +### Internal Improvements + +Improvements that are relevant to developers working on Fornjot itself. + +**TASK: Add internal improvements.** + + +### Issue of the Week + +**TASK: Write.** + + +### Outlook + +**TASK: Write.** + + +{pull_request_links}\ + " + )?; + + file.write_all(buf.as_bytes()).await?; + + Ok(()) +} diff --git a/tools/automator/src/args.rs b/tools/automator/src/args.rs new file mode 100644 index 000000000..571b90003 --- /dev/null +++ b/tools/automator/src/args.rs @@ -0,0 +1,24 @@ +use chrono::{Date, NaiveDate, Utc}; + +#[derive(clap::Parser)] +pub enum Args { + CreateReleaseAnnouncement(CreateReleaseAnnouncement), +} + +impl Args { + pub fn parse() -> Self { + ::parse() + } +} + +#[derive(clap::Parser)] +pub struct CreateReleaseAnnouncement { + pub last_release_date: NaiveDate, + pub version: String, +} + +impl CreateReleaseAnnouncement { + pub fn last_release_date(&self) -> Date { + Date::from_utc(self.last_release_date, Utc) + } +} diff --git a/tools/automator/src/main.rs b/tools/automator/src/main.rs new file mode 100644 index 000000000..de47bdcba --- /dev/null +++ b/tools/automator/src/main.rs @@ -0,0 +1,9 @@ +mod announcement; +mod args; +mod pull_requests; +mod run; + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + run::run().await +} diff --git a/tools/automator/src/pull_requests.rs b/tools/automator/src/pull_requests.rs new file mode 100644 index 000000000..4fa04e787 --- /dev/null +++ b/tools/automator/src/pull_requests.rs @@ -0,0 +1,69 @@ +use std::collections::BTreeMap; + +use anyhow::anyhow; +use chrono::{Date, Utc}; +use octocrab::params::{pulls::Sort, Direction, State}; +use url::Url; + +pub struct PullRequest { + pub number: u64, + pub html_url: Url, +} + +impl PullRequest { + pub async fn fetch_since_last_release( + last_release_date: Date, + ) -> anyhow::Result> { + let mut pull_requests = BTreeMap::new(); + let mut page = 1u32; + + 'outer: loop { + println!("Fetching page {}...", page); + let pull_request_page = octocrab::instance() + .pulls("hannobraun", "Fornjot") + .list() + .state(State::Closed) + .sort(Sort::Updated) + .direction(Direction::Descending) + .per_page(100) // this is the maximum number of results per page + .page(page) + .send() + .await?; + + for pull_request in pull_request_page.items { + if let Some(updated_at) = pull_request.updated_at { + if updated_at.date() < last_release_date { + // This pull request has been updated before the last + // release. Since we sort pull requests by + // updated-descending, that means all following pull + // requests have been updated before the last release, + // and thus couldn't have been merged after. + break 'outer; + } + } + + if let Some(merged_at) = pull_request.merged_at { + if merged_at.date() >= last_release_date { + let number = pull_request.number; + let html_url = + pull_request.html_url.ok_or_else(|| { + anyhow!("Pull request is missing URL") + })?; + + let pull_request = Self { number, html_url }; + + pull_requests.insert(pull_request.number, pull_request); + } + } + } + + if pull_request_page.next.is_some() { + page += 1; + } else { + break; + } + } + + Ok(pull_requests) + } +} diff --git a/tools/automator/src/run.rs b/tools/automator/src/run.rs new file mode 100644 index 000000000..7cd975938 --- /dev/null +++ b/tools/automator/src/run.rs @@ -0,0 +1,15 @@ +use anyhow::Context; + +use crate::{announcement::create_release_announcement, args::Args}; + +pub async fn run() -> anyhow::Result<()> { + match Args::parse() { + Args::CreateReleaseAnnouncement(args) => { + create_release_announcement(args.last_release_date(), args.version) + .await + .context("Failed to create release announcement")?; + } + } + + Ok(()) +}