mirror of https://github.com/smithy-lang/smithy-rs
Add option to create a release manifest to changelog tool (#1384)
This commit is contained in:
parent
fb5e235446
commit
83af60855f
|
@ -137,6 +137,12 @@ dependencies = [
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
|
@ -255,6 +261,12 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sdk-lints"
|
name = "sdk-lints"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -266,6 +278,7 @@ dependencies = [
|
||||||
"ordinal",
|
"ordinal",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"time",
|
"time",
|
||||||
"toml",
|
"toml",
|
||||||
]
|
]
|
||||||
|
@ -290,6 +303,17 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.81"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
|
|
|
@ -16,6 +16,7 @@ cargo_toml = "0.10.1"
|
||||||
clap = { version = "3.1.7", features = ["derive"]}
|
clap = { version = "3.1.7", features = ["derive"]}
|
||||||
toml = "0.5.8"
|
toml = "0.5.8"
|
||||||
serde = { version = "1", features = ["derive"]}
|
serde = { version = "1", features = ["derive"]}
|
||||||
|
serde_json = "1"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
time = { version = "0.3.9", features = ["local-offset"]}
|
time = { version = "0.3.9", features = ["local-offset"]}
|
||||||
ordinal = "0.3.2"
|
ordinal = "0.3.2"
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
use crate::lint::LintError;
|
use crate::lint::LintError;
|
||||||
use crate::{repo_root, Check, Lint};
|
use crate::{repo_root, Check, Lint};
|
||||||
use anyhow::{bail, Context, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
use serde::{de, Deserialize, Deserializer};
|
use serde::{de, Deserialize, Deserializer, Serialize};
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
@ -282,12 +282,28 @@ fn no_uncommited_changes(path: &Path) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ReleaseMetadata {
|
||||||
|
pub title: String,
|
||||||
|
pub tag: String,
|
||||||
|
pub manifest_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct ReleaseManifest {
|
||||||
|
#[serde(rename = "tagName")]
|
||||||
|
tag_name: String,
|
||||||
|
name: String,
|
||||||
|
body: String,
|
||||||
|
prerelease: bool,
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn update_changelogs(
|
pub(crate) fn update_changelogs(
|
||||||
changelog_next: impl AsRef<Path>,
|
changelog_next: impl AsRef<Path>,
|
||||||
smithy_rs_path: impl AsRef<Path>,
|
smithy_rs_path: impl AsRef<Path>,
|
||||||
aws_sdk_rust_path: impl AsRef<Path>,
|
aws_sdk_rust_path: impl AsRef<Path>,
|
||||||
smithy_rs_release_header: &str,
|
smithy_rs_metadata: &ReleaseMetadata,
|
||||||
aws_sdk_rust_release_header: &str,
|
aws_sdk_rust_metadata: &ReleaseMetadata,
|
||||||
|
release_manifest_output_path: Option<&Path>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
no_uncommited_changes(changelog_next.as_ref()).context(
|
no_uncommited_changes(changelog_next.as_ref()).context(
|
||||||
"CHANGELOG.next.toml had unstaged changes. Refusing to perform changelog update.",
|
"CHANGELOG.next.toml had unstaged changes. Refusing to perform changelog update.",
|
||||||
|
@ -302,19 +318,35 @@ pub(crate) fn update_changelogs(
|
||||||
smithy_rs,
|
smithy_rs,
|
||||||
aws_sdk_rust,
|
aws_sdk_rust,
|
||||||
} = changelog.into_entries();
|
} = changelog.into_entries();
|
||||||
for (entries, path, release_header) in [
|
for (entries, path, release_metadata) in [
|
||||||
(smithy_rs, smithy_rs_path.as_ref(), smithy_rs_release_header),
|
(smithy_rs, smithy_rs_path.as_ref(), smithy_rs_metadata),
|
||||||
(
|
(
|
||||||
aws_sdk_rust,
|
aws_sdk_rust,
|
||||||
aws_sdk_rust_path.as_ref(),
|
aws_sdk_rust_path.as_ref(),
|
||||||
aws_sdk_rust_release_header,
|
aws_sdk_rust_metadata,
|
||||||
),
|
),
|
||||||
] {
|
] {
|
||||||
no_uncommited_changes(path)
|
no_uncommited_changes(path)
|
||||||
.with_context(|| format!("{} had unstaged changes", path.display()))?;
|
.with_context(|| format!("{} had unstaged changes", path.display()))?;
|
||||||
|
let (release_header, release_notes) = render(&entries, &release_metadata.title);
|
||||||
|
if let Some(output_path) = release_manifest_output_path {
|
||||||
|
let release_manifest = ReleaseManifest {
|
||||||
|
tag_name: release_metadata.tag.clone(),
|
||||||
|
name: release_metadata.title.clone(),
|
||||||
|
body: release_notes.clone(),
|
||||||
|
// All releases are pre-releases for now
|
||||||
|
prerelease: true,
|
||||||
|
};
|
||||||
|
std::fs::write(
|
||||||
|
output_path.join(&release_metadata.manifest_name),
|
||||||
|
serde_json::to_string_pretty(&release_manifest)?,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
let mut update = USE_UPDATE_CHANGELOGS.to_string();
|
let mut update = USE_UPDATE_CHANGELOGS.to_string();
|
||||||
update.push('\n');
|
update.push('\n');
|
||||||
update.push_str(&render(&entries, release_header));
|
update.push_str(&release_header);
|
||||||
|
update.push_str(&release_notes);
|
||||||
let current = std::fs::read_to_string(path)?.replace(USE_UPDATE_CHANGELOGS, "");
|
let current = std::fs::read_to_string(path)?.replace(USE_UPDATE_CHANGELOGS, "");
|
||||||
update.push_str(¤t);
|
update.push_str(¤t);
|
||||||
std::fs::write(path, update)?;
|
std::fs::write(path, update)?;
|
||||||
|
@ -370,16 +402,18 @@ fn render_sdk_model_entries<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a list of changelog entries into markdown
|
/// Convert a list of changelog entries into markdown.
|
||||||
fn render(entries: &[ChangelogEntry], release_header: &str) -> String {
|
/// Returns (header, body)
|
||||||
let mut out = String::new();
|
fn render(entries: &[ChangelogEntry], release_header: &str) -> (String, String) {
|
||||||
out.push_str(release_header);
|
let mut header = String::new();
|
||||||
out.push('\n');
|
header.push_str(release_header);
|
||||||
|
header.push('\n');
|
||||||
for _ in 0..release_header.len() {
|
for _ in 0..release_header.len() {
|
||||||
out.push('=');
|
header.push('=');
|
||||||
}
|
}
|
||||||
out.push('\n');
|
header.push('\n');
|
||||||
|
|
||||||
|
let mut out = String::new();
|
||||||
render_handauthored(
|
render_handauthored(
|
||||||
entries.iter().filter_map(ChangelogEntry::hand_authored),
|
entries.iter().filter_map(ChangelogEntry::hand_authored),
|
||||||
&mut out,
|
&mut out,
|
||||||
|
@ -429,7 +463,7 @@ fn render(entries: &[ChangelogEntry], release_header: &str) -> String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
out
|
(header, out)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct ChangelogNext;
|
pub(crate) struct ChangelogNext;
|
||||||
|
@ -475,8 +509,14 @@ fn check_changelog_next(path: impl AsRef<Path>) -> std::result::Result<Changelog
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use super::ChangelogEntry;
|
||||||
use crate::changelog::{render, Changelog, ChangelogEntries};
|
use crate::changelog::{render, Changelog, ChangelogEntries};
|
||||||
|
|
||||||
|
fn render_full(entries: &[ChangelogEntry], release_header: &str) -> String {
|
||||||
|
let (header, body) = render(entries, release_header);
|
||||||
|
return format!("{}{}", header, body);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn end_to_end_changelog() {
|
fn end_to_end_changelog() {
|
||||||
let changelog_toml = r#"
|
let changelog_toml = r#"
|
||||||
|
@ -544,7 +584,7 @@ message = "Some API change"
|
||||||
smithy_rs,
|
smithy_rs,
|
||||||
} = changelog.into_entries();
|
} = changelog.into_entries();
|
||||||
|
|
||||||
let smithy_rs_rendered = render(&smithy_rs, "v0.3.0 (January 4th, 2022)");
|
let smithy_rs_rendered = render_full(&smithy_rs, "v0.3.0 (January 4th, 2022)");
|
||||||
let smithy_rs_expected = r#"
|
let smithy_rs_expected = r#"
|
||||||
v0.3.0 (January 4th, 2022)
|
v0.3.0 (January 4th, 2022)
|
||||||
==========================
|
==========================
|
||||||
|
@ -567,7 +607,7 @@ Thank you for your contributions! ❤
|
||||||
.trim_start();
|
.trim_start();
|
||||||
pretty_assertions::assert_str_eq!(smithy_rs_expected, smithy_rs_rendered);
|
pretty_assertions::assert_str_eq!(smithy_rs_expected, smithy_rs_rendered);
|
||||||
|
|
||||||
let aws_sdk_rust_rendered = render(&aws_sdk_rust, "v0.1.0 (January 4th, 2022)");
|
let aws_sdk_rust_rendered = render_full(&aws_sdk_rust, "v0.1.0 (January 4th, 2022)");
|
||||||
let aws_sdk_expected = r#"
|
let aws_sdk_expected = r#"
|
||||||
v0.1.0 (January 4th, 2022)
|
v0.1.0 (January 4th, 2022)
|
||||||
==========================
|
==========================
|
||||||
|
|
|
@ -10,6 +10,7 @@ use crate::lint_cargo_toml::{CrateAuthor, CrateLicense, DocsRs};
|
||||||
use crate::readmes::{ReadmesExist, ReadmesHaveFooters};
|
use crate::readmes::{ReadmesExist, ReadmesHaveFooters};
|
||||||
use crate::todos::TodosHaveContext;
|
use crate::todos::TodosHaveContext;
|
||||||
use anyhow::{bail, Context, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
|
use changelog::ReleaseMetadata;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use ordinal::Ordinal;
|
use ordinal::Ordinal;
|
||||||
|
@ -68,6 +69,9 @@ enum Args {
|
||||||
/// Whether or not independent crate versions are being used (defaults to false)
|
/// Whether or not independent crate versions are being used (defaults to false)
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
independent_versioning: bool,
|
independent_versioning: bool,
|
||||||
|
/// Optional path to output a release manifest file to
|
||||||
|
#[clap(long)]
|
||||||
|
release_manifest_output_path: Option<PathBuf>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,27 +170,44 @@ fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
Args::UpdateChangelog {
|
Args::UpdateChangelog {
|
||||||
independent_versioning,
|
independent_versioning,
|
||||||
|
release_manifest_output_path,
|
||||||
} => {
|
} => {
|
||||||
|
let now = OffsetDateTime::now_local()?;
|
||||||
let changelog_next_path = repo_root().join("CHANGELOG.next.toml");
|
let changelog_next_path = repo_root().join("CHANGELOG.next.toml");
|
||||||
let changelog_path = repo_root().join("CHANGELOG.md");
|
let changelog_path = repo_root().join("CHANGELOG.md");
|
||||||
let aws_changelog_path = repo_root().join("aws/SDK_CHANGELOG.md");
|
let aws_changelog_path = repo_root().join("aws/SDK_CHANGELOG.md");
|
||||||
if independent_versioning {
|
if independent_versioning {
|
||||||
let header = date_header()?;
|
let smithy_rs_metadata =
|
||||||
|
date_based_release_metadata(now, "smithy-rs-release-manifest.json");
|
||||||
|
let sdk_metadata =
|
||||||
|
date_based_release_metadata(now, "aws-sdk-rust-release-manifest.json");
|
||||||
changelog::update_changelogs(
|
changelog::update_changelogs(
|
||||||
changelog_next_path,
|
changelog_next_path,
|
||||||
changelog_path,
|
changelog_path,
|
||||||
aws_changelog_path,
|
aws_changelog_path,
|
||||||
&header,
|
&smithy_rs_metadata,
|
||||||
&header,
|
&sdk_metadata,
|
||||||
|
release_manifest_output_path.as_deref(),
|
||||||
)?
|
)?
|
||||||
} else {
|
} else {
|
||||||
let auto = auto_changelog_meta()?;
|
let auto = auto_changelog_meta()?;
|
||||||
|
let smithy_rs_metadata = version_based_release_metadata(
|
||||||
|
now,
|
||||||
|
&auto.smithy_version,
|
||||||
|
"smithy-rs-release-manifest.json",
|
||||||
|
);
|
||||||
|
let sdk_metadata = version_based_release_metadata(
|
||||||
|
now,
|
||||||
|
&auto.sdk_version,
|
||||||
|
"aws-sdk-rust-release-manifest.json",
|
||||||
|
);
|
||||||
changelog::update_changelogs(
|
changelog::update_changelogs(
|
||||||
changelog_next_path,
|
changelog_next_path,
|
||||||
changelog_path,
|
changelog_path,
|
||||||
aws_changelog_path,
|
aws_changelog_path,
|
||||||
&release_header_sync_versioned(&auto.smithy_version)?,
|
&smithy_rs_metadata,
|
||||||
&release_header_sync_versioned(&auto.sdk_version)?,
|
&sdk_metadata,
|
||||||
|
release_manifest_output_path.as_deref(),
|
||||||
)?
|
)?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,22 +220,45 @@ struct ChangelogMeta {
|
||||||
sdk_version: String,
|
sdk_version: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn date_header() -> Result<String> {
|
fn date_based_release_metadata(
|
||||||
let now = OffsetDateTime::now_local()?;
|
now: OffsetDateTime,
|
||||||
Ok(format!(
|
manifest_name: impl Into<String>,
|
||||||
|
) -> ReleaseMetadata {
|
||||||
|
ReleaseMetadata {
|
||||||
|
title: date_title(&now),
|
||||||
|
tag: format!(
|
||||||
|
"release-{year}-{month:02}-{day:02}",
|
||||||
|
year = now.date().year(),
|
||||||
|
month = u8::from(now.date().month()),
|
||||||
|
day = now.date().day()
|
||||||
|
),
|
||||||
|
manifest_name: manifest_name.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn version_based_release_metadata(
|
||||||
|
now: OffsetDateTime,
|
||||||
|
version: &str,
|
||||||
|
manifest_name: impl Into<String>,
|
||||||
|
) -> ReleaseMetadata {
|
||||||
|
ReleaseMetadata {
|
||||||
|
title: format!(
|
||||||
|
"v{version} ({date})",
|
||||||
|
version = version,
|
||||||
|
date = date_title(&now)
|
||||||
|
),
|
||||||
|
tag: format!("v{version}", version = version),
|
||||||
|
manifest_name: manifest_name.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn date_title(now: &OffsetDateTime) -> String {
|
||||||
|
format!(
|
||||||
"{month} {day}, {year}",
|
"{month} {day}, {year}",
|
||||||
month = now.date().month(),
|
month = now.date().month(),
|
||||||
day = Ordinal(now.date().day()),
|
day = Ordinal(now.date().day()),
|
||||||
year = now.date().year()
|
year = now.date().year()
|
||||||
))
|
)
|
||||||
}
|
|
||||||
|
|
||||||
fn release_header_sync_versioned(version: &str) -> Result<String> {
|
|
||||||
Ok(format!(
|
|
||||||
"v{version} ({date})",
|
|
||||||
version = version,
|
|
||||||
date = date_header()?
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Discover the new version for the changelog from gradle.properties and the date.
|
/// Discover the new version for the changelog from gradle.properties and the date.
|
||||||
|
@ -266,3 +310,27 @@ fn all_runtime_crates() -> Result<impl Iterator<Item = PathBuf>> {
|
||||||
fn all_cargo_tomls() -> Result<impl Iterator<Item = PathBuf>> {
|
fn all_cargo_tomls() -> Result<impl Iterator<Item = PathBuf>> {
|
||||||
Ok(all_runtime_crates()?.map(|pkg| pkg.join("Cargo.toml")))
|
Ok(all_runtime_crates()?.map(|pkg| pkg.join("Cargo.toml")))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::{date_based_release_metadata, version_based_release_metadata};
|
||||||
|
use time::OffsetDateTime;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_date_based_release_metadata() {
|
||||||
|
let now = OffsetDateTime::from_unix_timestamp(100_000_000).unwrap();
|
||||||
|
let result = date_based_release_metadata(now, "some-manifest.json");
|
||||||
|
assert_eq!("March 3rd, 1973", result.title);
|
||||||
|
assert_eq!("release-1973-03-03", result.tag);
|
||||||
|
assert_eq!("some-manifest.json", result.manifest_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_version_based_release_metadata() {
|
||||||
|
let now = OffsetDateTime::from_unix_timestamp(100_000_000).unwrap();
|
||||||
|
let result = version_based_release_metadata(now, "0.11.0", "some-other-manifest.json");
|
||||||
|
assert_eq!("v0.11.0 (March 3rd, 1973)", result.title);
|
||||||
|
assert_eq!("v0.11.0", result.tag);
|
||||||
|
assert_eq!("some-other-manifest.json", result.manifest_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue