Loading tools/sdk-lints/Cargo.lock +24 −0 Original line number Diff line number Diff line Loading @@ -137,6 +137,12 @@ dependencies = [ "hashbrown", ] [[package]] name = "itoa" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" [[package]] name = "lazy_static" version = "1.4.0" Loading Loading @@ -255,6 +261,12 @@ dependencies = [ "proc-macro2", ] [[package]] name = "ryu" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" [[package]] name = "sdk-lints" version = "0.1.0" Loading @@ -266,6 +278,7 @@ dependencies = [ "ordinal", "pretty_assertions", "serde", "serde_json", "time", "toml", ] Loading @@ -290,6 +303,17 @@ dependencies = [ "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]] name = "strsim" version = "0.10.0" Loading tools/sdk-lints/Cargo.toml +1 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ cargo_toml = "0.10.1" clap = { version = "3.1.7", features = ["derive"]} toml = "0.5.8" serde = { version = "1", features = ["derive"]} serde_json = "1" lazy_static = "1.4.0" time = { version = "0.3.9", features = ["local-offset"]} ordinal = "0.3.2" Loading tools/sdk-lints/src/changelog.rs +57 −17 Original line number Diff line number Diff line Loading @@ -6,7 +6,7 @@ use crate::lint::LintError; use crate::{repo_root, Check, Lint}; use anyhow::{bail, Context, Result}; use serde::{de, Deserialize, Deserializer}; use serde::{de, Deserialize, Deserializer, Serialize}; use std::fmt::Write; use std::path::{Path, PathBuf}; use std::process::Command; Loading Loading @@ -282,12 +282,28 @@ fn no_uncommited_changes(path: &Path) -> Result<()> { 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( changelog_next: impl AsRef<Path>, smithy_rs_path: impl AsRef<Path>, aws_sdk_rust_path: impl AsRef<Path>, smithy_rs_release_header: &str, aws_sdk_rust_release_header: &str, smithy_rs_metadata: &ReleaseMetadata, aws_sdk_rust_metadata: &ReleaseMetadata, release_manifest_output_path: Option<&Path>, ) -> Result<()> { no_uncommited_changes(changelog_next.as_ref()).context( "CHANGELOG.next.toml had unstaged changes. Refusing to perform changelog update.", Loading @@ -302,19 +318,35 @@ pub(crate) fn update_changelogs( smithy_rs, aws_sdk_rust, } = changelog.into_entries(); for (entries, path, release_header) in [ (smithy_rs, smithy_rs_path.as_ref(), smithy_rs_release_header), for (entries, path, release_metadata) in [ (smithy_rs, smithy_rs_path.as_ref(), smithy_rs_metadata), ( aws_sdk_rust, aws_sdk_rust_path.as_ref(), aws_sdk_rust_release_header, aws_sdk_rust_metadata, ), ] { no_uncommited_changes(path) .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(); 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, ""); update.push_str(¤t); std::fs::write(path, update)?; Loading Loading @@ -370,16 +402,18 @@ fn render_sdk_model_entries<'a>( } } /// Convert a list of changelog entries into markdown fn render(entries: &[ChangelogEntry], release_header: &str) -> String { let mut out = String::new(); out.push_str(release_header); out.push('\n'); /// Convert a list of changelog entries into markdown. /// Returns (header, body) fn render(entries: &[ChangelogEntry], release_header: &str) -> (String, String) { let mut header = String::new(); header.push_str(release_header); header.push('\n'); for _ in 0..release_header.len() { out.push('='); header.push('='); } out.push('\n'); header.push('\n'); let mut out = String::new(); render_handauthored( entries.iter().filter_map(ChangelogEntry::hand_authored), &mut out, Loading Loading @@ -429,7 +463,7 @@ fn render(entries: &[ChangelogEntry], release_header: &str) -> String { } } out (header, out) } pub(crate) struct ChangelogNext; Loading Loading @@ -475,8 +509,14 @@ fn check_changelog_next(path: impl AsRef<Path>) -> std::result::Result<Changelog #[cfg(test)] mod test { use super::ChangelogEntry; 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] fn end_to_end_changelog() { let changelog_toml = r#" Loading Loading @@ -544,7 +584,7 @@ message = "Some API change" smithy_rs, } = 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#" v0.3.0 (January 4th, 2022) ========================== Loading @@ -567,7 +607,7 @@ Thank you for your contributions! ❤ .trim_start(); 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#" v0.1.0 (January 4th, 2022) ========================== Loading tools/sdk-lints/src/main.rs +85 −17 Original line number Diff line number Diff line Loading @@ -10,6 +10,7 @@ use crate::lint_cargo_toml::{CrateAuthor, CrateLicense, DocsRs}; use crate::readmes::{ReadmesExist, ReadmesHaveFooters}; use crate::todos::TodosHaveContext; use anyhow::{bail, Context, Result}; use changelog::ReleaseMetadata; use clap::Parser; use lazy_static::lazy_static; use ordinal::Ordinal; Loading Loading @@ -68,6 +69,9 @@ enum Args { /// Whether or not independent crate versions are being used (defaults to false) #[clap(long)] independent_versioning: bool, /// Optional path to output a release manifest file to #[clap(long)] release_manifest_output_path: Option<PathBuf>, }, } Loading Loading @@ -166,27 +170,44 @@ fn main() -> Result<()> { } Args::UpdateChangelog { independent_versioning, release_manifest_output_path, } => { let now = OffsetDateTime::now_local()?; let changelog_next_path = repo_root().join("CHANGELOG.next.toml"); let changelog_path = repo_root().join("CHANGELOG.md"); let aws_changelog_path = repo_root().join("aws/SDK_CHANGELOG.md"); 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_next_path, changelog_path, aws_changelog_path, &header, &header, &smithy_rs_metadata, &sdk_metadata, release_manifest_output_path.as_deref(), )? } else { 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_next_path, changelog_path, aws_changelog_path, &release_header_sync_versioned(&auto.smithy_version)?, &release_header_sync_versioned(&auto.sdk_version)?, &smithy_rs_metadata, &sdk_metadata, release_manifest_output_path.as_deref(), )? } } Loading @@ -199,22 +220,45 @@ struct ChangelogMeta { sdk_version: String, } fn date_header() -> Result<String> { let now = OffsetDateTime::now_local()?; Ok(format!( "{month} {day}, {year}", month = now.date().month(), day = Ordinal(now.date().day()), year = now.date().year() )) fn date_based_release_metadata( now: OffsetDateTime, 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 release_header_sync_versioned(version: &str) -> Result<String> { Ok(format!( 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_header()? )) 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 = now.date().month(), day = Ordinal(now.date().day()), year = now.date().year() ) } /// Discover the new version for the changelog from gradle.properties and the date. Loading Loading @@ -266,3 +310,27 @@ fn all_runtime_crates() -> Result<impl Iterator<Item = PathBuf>> { fn all_cargo_tomls() -> Result<impl Iterator<Item = PathBuf>> { 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
tools/sdk-lints/Cargo.lock +24 −0 Original line number Diff line number Diff line Loading @@ -137,6 +137,12 @@ dependencies = [ "hashbrown", ] [[package]] name = "itoa" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" [[package]] name = "lazy_static" version = "1.4.0" Loading Loading @@ -255,6 +261,12 @@ dependencies = [ "proc-macro2", ] [[package]] name = "ryu" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" [[package]] name = "sdk-lints" version = "0.1.0" Loading @@ -266,6 +278,7 @@ dependencies = [ "ordinal", "pretty_assertions", "serde", "serde_json", "time", "toml", ] Loading @@ -290,6 +303,17 @@ dependencies = [ "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]] name = "strsim" version = "0.10.0" Loading
tools/sdk-lints/Cargo.toml +1 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ cargo_toml = "0.10.1" clap = { version = "3.1.7", features = ["derive"]} toml = "0.5.8" serde = { version = "1", features = ["derive"]} serde_json = "1" lazy_static = "1.4.0" time = { version = "0.3.9", features = ["local-offset"]} ordinal = "0.3.2" Loading
tools/sdk-lints/src/changelog.rs +57 −17 Original line number Diff line number Diff line Loading @@ -6,7 +6,7 @@ use crate::lint::LintError; use crate::{repo_root, Check, Lint}; use anyhow::{bail, Context, Result}; use serde::{de, Deserialize, Deserializer}; use serde::{de, Deserialize, Deserializer, Serialize}; use std::fmt::Write; use std::path::{Path, PathBuf}; use std::process::Command; Loading Loading @@ -282,12 +282,28 @@ fn no_uncommited_changes(path: &Path) -> Result<()> { 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( changelog_next: impl AsRef<Path>, smithy_rs_path: impl AsRef<Path>, aws_sdk_rust_path: impl AsRef<Path>, smithy_rs_release_header: &str, aws_sdk_rust_release_header: &str, smithy_rs_metadata: &ReleaseMetadata, aws_sdk_rust_metadata: &ReleaseMetadata, release_manifest_output_path: Option<&Path>, ) -> Result<()> { no_uncommited_changes(changelog_next.as_ref()).context( "CHANGELOG.next.toml had unstaged changes. Refusing to perform changelog update.", Loading @@ -302,19 +318,35 @@ pub(crate) fn update_changelogs( smithy_rs, aws_sdk_rust, } = changelog.into_entries(); for (entries, path, release_header) in [ (smithy_rs, smithy_rs_path.as_ref(), smithy_rs_release_header), for (entries, path, release_metadata) in [ (smithy_rs, smithy_rs_path.as_ref(), smithy_rs_metadata), ( aws_sdk_rust, aws_sdk_rust_path.as_ref(), aws_sdk_rust_release_header, aws_sdk_rust_metadata, ), ] { no_uncommited_changes(path) .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(); 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, ""); update.push_str(¤t); std::fs::write(path, update)?; Loading Loading @@ -370,16 +402,18 @@ fn render_sdk_model_entries<'a>( } } /// Convert a list of changelog entries into markdown fn render(entries: &[ChangelogEntry], release_header: &str) -> String { let mut out = String::new(); out.push_str(release_header); out.push('\n'); /// Convert a list of changelog entries into markdown. /// Returns (header, body) fn render(entries: &[ChangelogEntry], release_header: &str) -> (String, String) { let mut header = String::new(); header.push_str(release_header); header.push('\n'); for _ in 0..release_header.len() { out.push('='); header.push('='); } out.push('\n'); header.push('\n'); let mut out = String::new(); render_handauthored( entries.iter().filter_map(ChangelogEntry::hand_authored), &mut out, Loading Loading @@ -429,7 +463,7 @@ fn render(entries: &[ChangelogEntry], release_header: &str) -> String { } } out (header, out) } pub(crate) struct ChangelogNext; Loading Loading @@ -475,8 +509,14 @@ fn check_changelog_next(path: impl AsRef<Path>) -> std::result::Result<Changelog #[cfg(test)] mod test { use super::ChangelogEntry; 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] fn end_to_end_changelog() { let changelog_toml = r#" Loading Loading @@ -544,7 +584,7 @@ message = "Some API change" smithy_rs, } = 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#" v0.3.0 (January 4th, 2022) ========================== Loading @@ -567,7 +607,7 @@ Thank you for your contributions! ❤ .trim_start(); 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#" v0.1.0 (January 4th, 2022) ========================== Loading
tools/sdk-lints/src/main.rs +85 −17 Original line number Diff line number Diff line Loading @@ -10,6 +10,7 @@ use crate::lint_cargo_toml::{CrateAuthor, CrateLicense, DocsRs}; use crate::readmes::{ReadmesExist, ReadmesHaveFooters}; use crate::todos::TodosHaveContext; use anyhow::{bail, Context, Result}; use changelog::ReleaseMetadata; use clap::Parser; use lazy_static::lazy_static; use ordinal::Ordinal; Loading Loading @@ -68,6 +69,9 @@ enum Args { /// Whether or not independent crate versions are being used (defaults to false) #[clap(long)] independent_versioning: bool, /// Optional path to output a release manifest file to #[clap(long)] release_manifest_output_path: Option<PathBuf>, }, } Loading Loading @@ -166,27 +170,44 @@ fn main() -> Result<()> { } Args::UpdateChangelog { independent_versioning, release_manifest_output_path, } => { let now = OffsetDateTime::now_local()?; let changelog_next_path = repo_root().join("CHANGELOG.next.toml"); let changelog_path = repo_root().join("CHANGELOG.md"); let aws_changelog_path = repo_root().join("aws/SDK_CHANGELOG.md"); 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_next_path, changelog_path, aws_changelog_path, &header, &header, &smithy_rs_metadata, &sdk_metadata, release_manifest_output_path.as_deref(), )? } else { 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_next_path, changelog_path, aws_changelog_path, &release_header_sync_versioned(&auto.smithy_version)?, &release_header_sync_versioned(&auto.sdk_version)?, &smithy_rs_metadata, &sdk_metadata, release_manifest_output_path.as_deref(), )? } } Loading @@ -199,22 +220,45 @@ struct ChangelogMeta { sdk_version: String, } fn date_header() -> Result<String> { let now = OffsetDateTime::now_local()?; Ok(format!( "{month} {day}, {year}", month = now.date().month(), day = Ordinal(now.date().day()), year = now.date().year() )) fn date_based_release_metadata( now: OffsetDateTime, 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 release_header_sync_versioned(version: &str) -> Result<String> { Ok(format!( 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_header()? )) 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 = now.date().month(), day = Ordinal(now.date().day()), year = now.date().year() ) } /// Discover the new version for the changelog from gradle.properties and the date. Loading Loading @@ -266,3 +310,27 @@ fn all_runtime_crates() -> Result<impl Iterator<Item = PathBuf>> { fn all_cargo_tomls() -> Result<impl Iterator<Item = PathBuf>> { 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); } }