Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expand release automation #1141

Merged
merged 11 commits into from
Sep 27, 2022
18 changes: 16 additions & 2 deletions tools/automator/src/announcement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ use tokio::{
io::AsyncWriteExt,
};

use crate::pull_requests::{Author, PullRequest, PullRequestsSinceLastRelease};
use crate::{
pull_requests::{Author, PullRequest, PullRequestsSinceLastRelease},
sponsors::Sponsors,
};

pub async fn create_release_announcement(
octocrab: &Octocrab,
Expand All @@ -33,11 +36,18 @@ pub async fn create_release_announcement(
let mut version = pull_requests_since_last_release.version_of_last_release;
version.minor += 1;

let min_dollars = 32;
let for_readme = false;
let sponsors = Sponsors::query(octocrab)
.await?
.as_markdown(min_dollars, for_readme)?;

let mut file = create_file(year, week).await?;
generate_announcement(
week,
date,
version.to_string(),
sponsors,
pull_requests,
&mut file,
)
Expand Down Expand Up @@ -65,6 +75,7 @@ async fn generate_announcement(
week: u32,
date: String,
version: String,
sponsors: String,
pull_requests: impl IntoIterator<Item = PullRequest>,
file: &mut File,
) -> anyhow::Result<()> {
Expand Down Expand Up @@ -121,6 +132,9 @@ title = \"Weekly Release - 2022-W{week}\"
# TASK: Uncomment this date, once the announcement is ready to be published.
# date = {date}

# TASK: Uncomment to generate the HTML for the email newsletter.
# template = \"newsletter/weekly-release.html\"

[extra]
version = \"{version}\"
+++
Expand All @@ -130,7 +144,7 @@ version = \"{version}\"

### 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), [@reivilibre](https://github.com/reivilibre), and [my other awesome sponsors](https://github.com/sponsors/hannobraun). Thank you!
{sponsors}

<strong class=\"call-to-action\">
<p>
Expand Down
8 changes: 7 additions & 1 deletion tools/automator/src/args.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
#[derive(clap::Parser)]
pub enum Args {
Announcement,
Sponsors,
Sponsors(Sponsors),
}

impl Args {
pub fn parse() -> Self {
<Self as clap::Parser>::parse()
}
}

#[derive(clap::Parser)]
pub struct Sponsors {
#[clap(short, long)]
pub for_readme: bool,
}
15 changes: 7 additions & 8 deletions tools/automator/src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ use anyhow::Context;
use octocrab::Octocrab;

use crate::{
announcement::create_release_announcement, args::Args,
sponsors::query_sponsors,
announcement::create_release_announcement, args::Args, sponsors::Sponsors,
};

pub async fn run() -> anyhow::Result<()> {
Expand All @@ -19,14 +18,14 @@ pub async fn run() -> anyhow::Result<()> {
.await
.context("Failed to create release announcement")?;
}
Args::Sponsors => {
let sponsors = query_sponsors(&octocrab)
Args::Sponsors(args) => {
let sponsors = Sponsors::query(&octocrab)
.await
.context("Failed to query sponsors")?;
.context("Failed to query sponsors")?
.as_markdown(8, args.for_readme)
.context("Failed to format sponsors")?;

println!("{sponsors:#?}");

todo!("Querying sponsors is not supported yet.")
println!("{sponsors}");
}
}

Expand Down
176 changes: 106 additions & 70 deletions tools/automator/src/sponsors.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,113 @@
use std::cmp::Ordering;
use std::{cmp::Ordering, fmt::Write};

use chrono::{DateTime, Utc};
use octocrab::Octocrab;

#[derive(Debug)]
pub struct Sponsors {
pub inner: Vec<Sponsor>,
}

impl Sponsors {
pub async fn query(octocrab: &Octocrab) -> anyhow::Result<Sponsors> {
let response: QueryResult = octocrab
.graphql(
"query {
viewer {
sponsors(first: 100) {
nodes {
__typename
... on User {
login
sponsorshipForViewerAsSponsorable {
createdAt
tier {
monthlyPriceInDollars
}
}
}
... on Organization {
login
sponsorshipForViewerAsSponsorable {
createdAt
tier {
monthlyPriceInDollars
}
}
}
}
}
}
}",
)
.await?;

let mut sponsors = response
.data
.viewer
.sponsors
.nodes
.into_iter()
.map(|node| {
let login = node.login;
let since =
node.sponsorship_for_viewer_as_sponsorable.created_at;
let dollars = node
.sponsorship_for_viewer_as_sponsorable
.tier
.monthly_price_in_dollars;

Sponsor {
login,
since,
dollars,
}
})
.collect::<Vec<_>>();

if sponsors.len() >= 100 {
todo!(
"Number of sponsors has reached max page size, but query does \
not support pagination."
)
}

sponsors.sort();

Ok(Sponsors { inner: sponsors })
}

pub fn as_markdown(
&self,
min_dollars: u32,
for_readme: bool,
) -> anyhow::Result<String> {
let mut output = String::from("Fornjot is supported by ");

for sponsor in &self.inner {
if sponsor.dollars < min_dollars {
continue;
}

let login = &sponsor.login;
let name = if for_readme {
format!("**@{login}**")
} else {
format!("@{login}")
};
let url = format!("https://github.com/{login}");

write!(output, "[{name}]({url}), ")?;
}

output.push_str(
"and [my other awesome sponsors](https://github.com/sponsors/hannobraun). Thank you!"
);

Ok(output)
}
}

#[derive(Debug, Eq, PartialEq)]
pub struct Sponsor {
pub login: String,
Expand Down Expand Up @@ -34,75 +139,6 @@ impl PartialOrd for Sponsor {
}
}

pub async fn query_sponsors(
octocrab: &Octocrab,
) -> anyhow::Result<Vec<Sponsor>> {
let response: QueryResult = octocrab
.graphql(
"query {
viewer {
sponsors(first: 100) {
nodes {
__typename
... on User {
login
sponsorshipForViewerAsSponsorable {
createdAt
tier {
monthlyPriceInDollars
}
}
}
... on Organization {
login
sponsorshipForViewerAsSponsorable {
createdAt
tier {
monthlyPriceInDollars
}
}
}
}
}
}
}",
)
.await?;

let mut sponsors = response
.data
.viewer
.sponsors
.nodes
.into_iter()
.map(|node| {
let login = node.login;
let since = node.sponsorship_for_viewer_as_sponsorable.created_at;
let dollars = node
.sponsorship_for_viewer_as_sponsorable
.tier
.monthly_price_in_dollars;

Sponsor {
login,
since,
dollars,
}
})
.collect::<Vec<_>>();

if sponsors.len() >= 100 {
todo!(
"Number of sponsors has reached max page size, but query does not \
support pagination."
)
}

sponsors.sort();

Ok(sponsors)
}

#[derive(Debug, serde::Deserialize)]
pub struct QueryResult {
pub data: QueryResultData,
Expand Down