Skip to content

Commit f06a29f

Browse files
committed
Implement xtask publish-release-notes to publish release notes on GitHub Releases
1 parent 0eb537f commit f06a29f

File tree

3 files changed

+97
-0
lines changed

3 files changed

+97
-0
lines changed

xtask/src/flags.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ xflags::xflags! {
3434
cmd dist {
3535
optional --client-patch-version version: String
3636
}
37+
/// Read a changelog AsciiDoc file and update the GitHub Releases entry in Markdown.
38+
cmd publish-release-notes {
39+
/// Only run conversion and show the result.
40+
optional --dry-run
41+
/// Target changelog file.
42+
required changelog: String
43+
}
3744
cmd metrics {
3845
optional --dry-run
3946
}
@@ -59,6 +66,7 @@ pub enum XtaskCmd {
5966
Release(Release),
6067
Promote(Promote),
6168
Dist(Dist),
69+
PublishReleaseNotes(PublishReleaseNotes),
6270
Metrics(Metrics),
6371
Bb(Bb),
6472
}
@@ -90,6 +98,13 @@ pub struct Dist {
9098
pub client_patch_version: Option<String>,
9199
}
92100

101+
#[derive(Debug)]
102+
pub struct PublishReleaseNotes {
103+
pub changelog: String,
104+
105+
pub dry_run: bool,
106+
}
107+
93108
#[derive(Debug)]
94109
pub struct Metrics {
95110
pub dry_run: bool,

xtask/src/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ mod flags;
1515
mod install;
1616
mod release;
1717
mod dist;
18+
mod publish;
1819
mod metrics;
1920

2021
use anyhow::bail;
@@ -36,6 +37,7 @@ fn main() -> anyhow::Result<()> {
3637
flags::XtaskCmd::Release(cmd) => cmd.run(sh),
3738
flags::XtaskCmd::Promote(cmd) => cmd.run(sh),
3839
flags::XtaskCmd::Dist(cmd) => cmd.run(sh),
40+
flags::XtaskCmd::PublishReleaseNotes(cmd) => cmd.run(sh),
3941
flags::XtaskCmd::Metrics(cmd) => cmd.run(sh),
4042
flags::XtaskCmd::Bb(cmd) => {
4143
{

xtask/src/publish.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
mod notes;
2+
3+
use crate::flags;
4+
use anyhow::{anyhow, bail, Result};
5+
use std::env;
6+
use xshell::{cmd, Shell};
7+
8+
impl flags::PublishReleaseNotes {
9+
pub(crate) fn run(self, sh: &Shell) -> Result<()> {
10+
let asciidoc = sh.read_file(&self.changelog)?;
11+
let markdown = notes::convert_asciidoc_to_markdown(std::io::Cursor::new(&asciidoc))?;
12+
let tag_name = extract_tag_name(&self.changelog)?;
13+
if self.dry_run {
14+
println!("{}", markdown);
15+
} else {
16+
update_release(sh, &tag_name, &markdown)?;
17+
}
18+
Ok(())
19+
}
20+
}
21+
22+
fn extract_tag_name<P: AsRef<std::path::Path>>(path: P) -> Result<String> {
23+
let file_name = path
24+
.as_ref()
25+
.file_name()
26+
.ok_or_else(|| anyhow!("file name is not specified as `changelog`"))?
27+
.to_string_lossy();
28+
29+
let mut chars = file_name.chars();
30+
if file_name.len() >= 10
31+
&& chars.next().unwrap().is_ascii_digit()
32+
&& chars.next().unwrap().is_ascii_digit()
33+
&& chars.next().unwrap().is_ascii_digit()
34+
&& chars.next().unwrap().is_ascii_digit()
35+
&& chars.next().unwrap() == '-'
36+
&& chars.next().unwrap().is_ascii_digit()
37+
&& chars.next().unwrap().is_ascii_digit()
38+
&& chars.next().unwrap() == '-'
39+
&& chars.next().unwrap().is_ascii_digit()
40+
&& chars.next().unwrap().is_ascii_digit()
41+
{
42+
Ok(file_name[0..10].to_owned())
43+
} else {
44+
bail!("extraction of date from the file name failed")
45+
}
46+
}
47+
48+
fn update_release(sh: &Shell, tag_name: &str, release_notes: &str) -> Result<()> {
49+
let token = match env::var("GITHUB_TOKEN") {
50+
Ok(token) => token,
51+
Err(_) => bail!("Please obtain a personal access token from https://github.com/settings/tokens and set the `GITHUB_TOKEN` environment variable."),
52+
};
53+
let accept = "Accept: application/vnd.github+json";
54+
let authorization = format!("Authorization: Bearer {}", token);
55+
let api_version = "X-GitHub-Api-Version: 2022-11-28";
56+
let release_url = "https://api.github.com/repos/rust-lang/rust-analyzer/releases";
57+
58+
let release_json = cmd!(
59+
sh,
60+
"curl -s -H {accept} -H {authorization} -H {api_version} {release_url}/tags/{tag_name}"
61+
)
62+
.read()?;
63+
let release_id = cmd!(sh, "jq .id").stdin(release_json).read()?;
64+
65+
let mut patch = String::new();
66+
write_json::object(&mut patch)
67+
.string("tag_name", &tag_name)
68+
.string("target_commitish", "master")
69+
.string("name", &tag_name)
70+
.string("body", &release_notes)
71+
.bool("draft", false)
72+
.bool("prerelease", false);
73+
let _ = cmd!(
74+
sh,
75+
"curl -s -X PATCH -H {accept} -H {authorization} -H {api_version} {release_url}/{release_id} -d {patch}"
76+
)
77+
.read()?;
78+
79+
Ok(())
80+
}

0 commit comments

Comments
 (0)