From f06a29f6e1fed49c0e817e669b77f3110ef84d6c Mon Sep 17 00:00:00 2001 From: Noritada Kobayashi Date: Sun, 11 Dec 2022 23:49:28 +0900 Subject: [PATCH] Implement `xtask publish-release-notes` to publish release notes on GitHub Releases --- xtask/src/flags.rs | 15 +++++++++ xtask/src/main.rs | 2 ++ xtask/src/publish.rs | 80 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+) create mode 100644 xtask/src/publish.rs diff --git a/xtask/src/flags.rs b/xtask/src/flags.rs index 0fce488983..2100479701 100644 --- a/xtask/src/flags.rs +++ b/xtask/src/flags.rs @@ -34,6 +34,13 @@ xflags::xflags! { cmd dist { optional --client-patch-version version: String } + /// Read a changelog AsciiDoc file and update the GitHub Releases entry in Markdown. + cmd publish-release-notes { + /// Only run conversion and show the result. + optional --dry-run + /// Target changelog file. + required changelog: String + } cmd metrics { optional --dry-run } @@ -59,6 +66,7 @@ pub enum XtaskCmd { Release(Release), Promote(Promote), Dist(Dist), + PublishReleaseNotes(PublishReleaseNotes), Metrics(Metrics), Bb(Bb), } @@ -90,6 +98,13 @@ pub struct Dist { pub client_patch_version: Option, } +#[derive(Debug)] +pub struct PublishReleaseNotes { + pub changelog: String, + + pub dry_run: bool, +} + #[derive(Debug)] pub struct Metrics { pub dry_run: bool, diff --git a/xtask/src/main.rs b/xtask/src/main.rs index a37f469adc..6a45033ada 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -15,6 +15,7 @@ mod flags; mod install; mod release; mod dist; +mod publish; mod metrics; use anyhow::bail; @@ -36,6 +37,7 @@ fn main() -> anyhow::Result<()> { flags::XtaskCmd::Release(cmd) => cmd.run(sh), flags::XtaskCmd::Promote(cmd) => cmd.run(sh), flags::XtaskCmd::Dist(cmd) => cmd.run(sh), + flags::XtaskCmd::PublishReleaseNotes(cmd) => cmd.run(sh), flags::XtaskCmd::Metrics(cmd) => cmd.run(sh), flags::XtaskCmd::Bb(cmd) => { { diff --git a/xtask/src/publish.rs b/xtask/src/publish.rs new file mode 100644 index 0000000000..8045334207 --- /dev/null +++ b/xtask/src/publish.rs @@ -0,0 +1,80 @@ +mod notes; + +use crate::flags; +use anyhow::{anyhow, bail, Result}; +use std::env; +use xshell::{cmd, Shell}; + +impl flags::PublishReleaseNotes { + pub(crate) fn run(self, sh: &Shell) -> Result<()> { + let asciidoc = sh.read_file(&self.changelog)?; + let markdown = notes::convert_asciidoc_to_markdown(std::io::Cursor::new(&asciidoc))?; + let tag_name = extract_tag_name(&self.changelog)?; + if self.dry_run { + println!("{}", markdown); + } else { + update_release(sh, &tag_name, &markdown)?; + } + Ok(()) + } +} + +fn extract_tag_name>(path: P) -> Result { + let file_name = path + .as_ref() + .file_name() + .ok_or_else(|| anyhow!("file name is not specified as `changelog`"))? + .to_string_lossy(); + + let mut chars = file_name.chars(); + if file_name.len() >= 10 + && chars.next().unwrap().is_ascii_digit() + && chars.next().unwrap().is_ascii_digit() + && chars.next().unwrap().is_ascii_digit() + && chars.next().unwrap().is_ascii_digit() + && chars.next().unwrap() == '-' + && chars.next().unwrap().is_ascii_digit() + && chars.next().unwrap().is_ascii_digit() + && chars.next().unwrap() == '-' + && chars.next().unwrap().is_ascii_digit() + && chars.next().unwrap().is_ascii_digit() + { + Ok(file_name[0..10].to_owned()) + } else { + bail!("extraction of date from the file name failed") + } +} + +fn update_release(sh: &Shell, tag_name: &str, release_notes: &str) -> Result<()> { + let token = match env::var("GITHUB_TOKEN") { + Ok(token) => token, + Err(_) => bail!("Please obtain a personal access token from https://github.com/settings/tokens and set the `GITHUB_TOKEN` environment variable."), + }; + let accept = "Accept: application/vnd.github+json"; + let authorization = format!("Authorization: Bearer {}", token); + let api_version = "X-GitHub-Api-Version: 2022-11-28"; + let release_url = "https://api.github.com/repos/rust-lang/rust-analyzer/releases"; + + let release_json = cmd!( + sh, + "curl -s -H {accept} -H {authorization} -H {api_version} {release_url}/tags/{tag_name}" + ) + .read()?; + let release_id = cmd!(sh, "jq .id").stdin(release_json).read()?; + + let mut patch = String::new(); + write_json::object(&mut patch) + .string("tag_name", &tag_name) + .string("target_commitish", "master") + .string("name", &tag_name) + .string("body", &release_notes) + .bool("draft", false) + .bool("prerelease", false); + let _ = cmd!( + sh, + "curl -s -X PATCH -H {accept} -H {authorization} -H {api_version} {release_url}/{release_id} -d {patch}" + ) + .read()?; + + Ok(()) +}