Skip to content

Commit

Permalink
Auto merge of #13771 - noritada:feature/release-notes-on-github-relea…
Browse files Browse the repository at this point in the history
…ses, r=lnicola

Add xtask for publishing release notes in Markdown on GitHub Releases from a changelog in AsciiDoc

This PR provides `xtask publish-release-notes` to convert a changelog written in AsciiDoc to Markdown and update descriptions (release notes) of a corresponding entry on GitHub Releases.

This AsciiDoc parser is not capable of processing every AsciiDoc document, but I have surveyed a set of existing changelog entries and have confirmed that the following notations used can be converted properly. In the future, I would like to improve the parser to accept any AsciiDoc.  Alternatively, the parser could be moved out of the project.

Your feedback would be appreciated!

Closes #13191

### Supported AsciiDoc syntax

many occurrences
- [x] documentation header
- [x] section header
- [x] `*`-prefixed basic unordered single level list item
- [x] list continuation using `+`
- [x] block image macro `image::...[]` with empty alt
- [x] block image macro `image::...[]` with non-empty alt
- [x] block video marco `video::...[]` with `options=loop`
- [x] inline hard line break `+`
- [x] inline custom macro `commit:...[]`
- [x] inline custom macro `release:...[]`
- [x] inline custom macro `pr:...[]`
- [x] inline unconstrained bold text `**...**`
- [x] inline constrained monospace ``` `...`  ```

[thisweek/_posts/2019-07-24-changelog-0.adoc](https://github.com/rust-analyzer/rust-analyzer.github.io/tree/src/thisweek/_posts#:~:text=2019%2D07%2D24%2Dchangelog%2D0.adoc)
- [x] paragraphs
- [x] mixture of `*` and `-` for unordered list item prefix
- [x] inline external link `https://...[]`

[thisweek/_posts/2020-01-13-changelog-7.adoc](https://github.com/rust-analyzer/rust-analyzer.github.io/tree/src/thisweek/_posts#:~:text=2020%2D01%2D13%2Dchangelog%2D7.adoc)
- [x] list item with multiline principal text with indent
- [x] inline image macro `image:...[]`

[thisweek/_posts/2020-03-02-changelog-14.adoc](https://github.com/rust-analyzer/rust-analyzer.github.io/blob/src/thisweek/_posts/2020-03-02-changelog-14.adoc)
- [x] empty lines between list items
- [x] nested unordered list item with `**`
- [x] inline macro `kbd:[...]`

[thisweek/_posts/2020-03-16-changelog-16.adoc](https://github.com/rust-analyzer/rust-analyzer.github.io/blob/src/thisweek/_posts/2020-03-16-changelog-16.adoc)
- [x] `[source]`-prefixed listing

[thisweek/_posts/2020-04-06-changelog-19.adoc](https://github.com/rust-analyzer/rust-analyzer.github.io/blob/src/thisweek/_posts/2020-04-06-changelog-19.adoc)
- [x] list item with multiline principal text without indent
- [x] `[source,lang]`-prefixed listing
- [x] `.`-prefiexed ordered list item
- [x] list item immediately after list continuation paragraph without an empty line in between

[thisweek/_posts/2020-04-20-changelog-21.adoc](https://github.com/rust-analyzer/rust-analyzer.github.io/blob/src/thisweek/_posts/2020-04-20-changelog-21.adoc)
- [x] title line for block image

[thisweek/_posts/2020-12-21-changelog-56.adoc](https://github.com/rust-analyzer/rust-analyzer.github.io/blob/src/thisweek/_posts/2020-12-21-changelog-56.adoc)
- [x] block video `video::...[]` with `options="autoplay,loop"`
  • Loading branch information
bors committed Dec 21, 2022
2 parents 761b127 + fccc094 commit 3c00b19
Show file tree
Hide file tree
Showing 7 changed files with 932 additions and 3 deletions.
7 changes: 4 additions & 3 deletions docs/dev/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ Look for `fn benchmark_xxx` tests for a quick way to reproduce performance probl

## Release Process

Release process is handled by `release`, `dist` and `promote` xtasks, `release` being the main one.
Release process is handled by `release`, `dist`, `publish-release-notes` and `promote` xtasks, `release` being the main one.

`release` assumes that you have checkouts of `rust-analyzer`, `rust-analyzer.github.io`, and `rust-lang/rust` in the same directory:

Expand Down Expand Up @@ -231,8 +231,9 @@ Release steps:
* create a new changelog in `rust-analyzer.github.io`
3. While the release is in progress, fill in the changelog
4. Commit & push the changelog
5. Tweet
6. Inside `rust-analyzer`, run `cargo xtask promote` -- this will create a PR to rust-lang/rust updating rust-analyzer's subtree.
5. Run `cargo xtask publish-release-notes <CHANGELOG>` -- this will convert the changelog entry in AsciiDoc to Markdown and update the body of GitHub Releases entry.
6. Tweet
7. Inside `rust-analyzer`, run `cargo xtask promote` -- this will create a PR to rust-lang/rust updating rust-analyzer's subtree.
Self-approve the PR.

If the GitHub Actions release fails because of a transient problem like a timeout, you can re-run the job from the Actions console.
Expand Down
15 changes: 15 additions & 0 deletions xtask/src/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -59,6 +66,7 @@ pub enum XtaskCmd {
Release(Release),
Promote(Promote),
Dist(Dist),
PublishReleaseNotes(PublishReleaseNotes),
Metrics(Metrics),
Bb(Bb),
}
Expand Down Expand Up @@ -90,6 +98,13 @@ pub struct Dist {
pub client_patch_version: Option<String>,
}

#[derive(Debug)]
pub struct PublishReleaseNotes {
pub changelog: String,

pub dry_run: bool,
}

#[derive(Debug)]
pub struct Metrics {
pub dry_run: bool,
Expand Down
2 changes: 2 additions & 0 deletions xtask/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ mod flags;
mod install;
mod release;
mod dist;
mod publish;
mod metrics;

use anyhow::bail;
Expand All @@ -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) => {
{
Expand Down
109 changes: 109 additions & 0 deletions xtask/src/publish.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
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 mut markdown = notes::convert_asciidoc_to_markdown(std::io::Cursor::new(&asciidoc))?;
let file_name = check_file_name(self.changelog)?;
let tag_name = &file_name[0..10];
let original_changelog_url = create_original_changelog_url(&file_name);
let additional_paragraph =
format!("\nSee also [original changelog]({original_changelog_url}).");
markdown.push_str(&additional_paragraph);
if self.dry_run {
println!("{markdown}");
} else {
update_release(sh, tag_name, &markdown)?;
}
Ok(())
}
}

fn check_file_name<P: AsRef<std::path::Path>>(path: P) -> Result<String> {
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.to_string())
} else {
bail!("unexpected file name format; no date information prefixed")
}
}

fn create_original_changelog_url(file_name: &str) -> String {
let year = &file_name[0..4];
let month = &file_name[5..7];
let day = &file_name[8..10];
let mut stem = &file_name[11..];
if let Some(stripped) = stem.strip_suffix(".adoc") {
stem = stripped;
}
format!("https://rust-analyzer.github.io/thisweek/{year}/{month}/{day}/{stem}.html")
}

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 -sf -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 -sf -X PATCH -H {accept} -H {authorization} -H {api_version} {release_url}/{release_id} -d {patch}"
)
.read()?;

Ok(())
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn original_changelog_url_creation() {
let input = "2019-07-24-changelog-0.adoc";
let actual = create_original_changelog_url(input);
let expected = "https://rust-analyzer.github.io/thisweek/2019/07/24/changelog-0.html";
assert_eq!(actual, expected);
}
}
Loading

0 comments on commit 3c00b19

Please sign in to comment.