Loading tools/sdk-lints/Cargo.lock +79 −32 Original line number Diff line number Diff line Loading @@ -2,11 +2,20 @@ # It is not intended for manual editing. version = 3 [[package]] name = "ansi_term" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" dependencies = [ "winapi", ] [[package]] name = "anyhow" version = "1.0.50" version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ecc78c299ae753905840c5d3ba036c51f61ce5a98a83f98d9c9d29dffd427f71" checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc" [[package]] name = "atty" Loading @@ -33,9 +42,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "cargo_toml" version = "0.10.1" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6d613611c914a7db07f28526941ce1e956d2f977b0c5e2014fbfa42230d420f" checksum = "363c7cfaa15f101415c4ac9e68706ca4a2277773932828b33f96e59d28c68e62" dependencies = [ "serde", "serde_derive", Loading @@ -44,16 +53,16 @@ dependencies = [ [[package]] name = "clap" version = "3.1.7" version = "3.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c67e7973e74896f4bba06ca2dcfd28d54f9cb8c035e940a32b88ed48f5f5ecf2" checksum = "85a35a599b11c089a7f49105658d089b8f2cf0882993c17daf6de15285c2c35d" dependencies = [ "atty", "bitflags", "clap_derive", "clap_lex", "indexmap", "lazy_static", "os_str_bytes", "strsim", "termcolor", "textwrap", Loading @@ -72,6 +81,31 @@ dependencies = [ "syn", ] [[package]] name = "clap_lex" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213" dependencies = [ "os_str_bytes", ] [[package]] name = "ctor" version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c" dependencies = [ "quote", "syn", ] [[package]] name = "diff" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" [[package]] name = "hashbrown" version = "0.11.2" Loading Loading @@ -111,21 +145,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f" [[package]] name = "memchr" version = "2.4.1" version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b" [[package]] name = "num-integer" version = "0.1.44" version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ "autocfg", "num-traits", Loading Loading @@ -163,8 +191,26 @@ name = "os_str_bytes" version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" [[package]] name = "output_vt100" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" dependencies = [ "winapi", ] [[package]] name = "pretty_assertions" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c89f989ac94207d048d92db058e4f6ec7342b0971fc58d1271ca148b799b3563" dependencies = [ "memchr", "ansi_term", "ctor", "diff", "output_vt100", ] [[package]] Loading Loading @@ -193,18 +239,18 @@ dependencies = [ [[package]] name = "proc-macro2" version = "1.0.32" version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" dependencies = [ "unicode-xid", ] [[package]] name = "quote" version = "1.0.10" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" dependencies = [ "proc-macro2", ] Loading @@ -218,6 +264,7 @@ dependencies = [ "clap", "lazy_static", "ordinal", "pretty_assertions", "serde", "time", "toml", Loading @@ -225,18 +272,18 @@ dependencies = [ [[package]] name = "serde" version = "1.0.130" version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.130" version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" dependencies = [ "proc-macro2", "quote", Loading @@ -251,9 +298,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" version = "1.0.82" version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59" checksum = "7ff7c592601f11445996a06f8ad0c27f094a58857c2f89e97974ab9235b92c52" dependencies = [ "proc-macro2", "quote", Loading Loading @@ -287,18 +334,18 @@ dependencies = [ [[package]] name = "toml" version = "0.5.8" version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" dependencies = [ "serde", ] [[package]] name = "unicode-xid" version = "0.2.2" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" [[package]] name = "version_check" Loading tools/sdk-lints/Cargo.toml +3 −0 Original line number Diff line number Diff line Loading @@ -19,3 +19,6 @@ serde = { version = "1", features = ["derive"]} lazy_static = "1.4.0" time = { version = "0.3.9", features = ["local-offset"]} ordinal = "0.3.2" [dev-dependencies] pretty_assertions = "1.2.1" tools/sdk-lints/src/changelog.rs +265 −98 Original line number Diff line number Diff line Loading @@ -36,8 +36,29 @@ fn maintainers() -> Vec<&'static str> { .collect() } enum ChangelogEntry { HandAuthored(HandAuthoredEntry), AwsSdkModel(SdkModelEntry), } impl ChangelogEntry { fn hand_authored(&self) -> Option<&HandAuthoredEntry> { match self { ChangelogEntry::HandAuthored(hand_authored) => Some(hand_authored), _ => None, } } fn aws_sdk_model(&self) -> Option<&SdkModelEntry> { match self { ChangelogEntry::AwsSdkModel(sdk_model) => Some(sdk_model), _ => None, } } } #[derive(Deserialize)] struct ChangelogEntry { struct HandAuthoredEntry { message: String, meta: Meta, author: String, Loading @@ -45,6 +66,92 @@ struct ChangelogEntry { references: Vec<Reference>, } impl HandAuthoredEntry { /// Validate a changelog entry to ensure it follows standards fn validate(&self) -> Result<()> { if self.author.is_empty() { bail!("Author must be set (was empty)"); } if !self.author.chars().all(|c| c.is_alphanumeric() || c == '-') { bail!("Author must be valid GitHub username: [a-zA-Z0-9\\-]") } if self.references.is_empty() { bail!("Changelog entry must refer to at least one pull request or issue"); } Ok(()) } /// Write a changelog entry to [out] /// /// Example output: /// `- Add a feature (smithy-rs#123, @contributor)` fn render(&self, mut out: &mut String) { let mut meta = String::new(); if self.meta.bug { meta.push('🐛'); } if self.meta.breaking { meta.push('⚠'); } if self.meta.tada { meta.push('🎉'); } if !meta.is_empty() { meta.push(' '); } let mut references = self .references .iter() .map(Reference::to_md_link) .collect::<Vec<_>>(); if !maintainers().contains(&self.author.to_ascii_lowercase().as_str()) { references.push(format!("@{}", self.author.to_ascii_lowercase())); }; if !references.is_empty() { write!(meta, "({}) ", references.join(", ")).unwrap(); } write!( &mut out, "- {meta}{message}", meta = meta, message = indented_message(&self.message), ) .unwrap(); } } #[derive(Deserialize)] enum SdkModelChangeKind { Documentation, Feature, } #[derive(Deserialize)] struct SdkModelEntry { /// SDK module name (e.g., "aws-sdk-s3" for S3) module: String, /// SDK module version number (e.g., "0.14.0") version: String, /// What changed kind: SdkModelChangeKind, /// More details about the change message: String, } impl SdkModelEntry { fn render(&self, out: &mut String) { write!( out, "- `{module}` ({version}): {message}", module = self.module, version = self.version, message = self.message ) .unwrap(); } } struct Reference { repo: String, number: usize, Loading Loading @@ -93,46 +200,6 @@ impl FromStr for Reference { } } impl ChangelogEntry { /// Write a changelog entry to [out] /// /// Example output: /// `- Add a feature (smithy-rs#123, @contributor)` fn render(&self, mut out: &mut String) { let mut meta = String::new(); if self.meta.bug { meta.push('🐛'); } if self.meta.breaking { meta.push('⚠'); } if self.meta.tada { meta.push('🎉'); } if !meta.is_empty() { meta.push(' '); } let mut references = self .references .iter() .map(Reference::to_md_link) .collect::<Vec<_>>(); if !maintainers().contains(&self.author.to_ascii_lowercase().as_str()) { references.push(format!("@{}", self.author.to_ascii_lowercase())); }; if !references.is_empty() { write!(meta, "({}) ", references.join(", ")).unwrap(); } write!( &mut out, "- {meta}{message}", meta = meta, message = indented_message(&self.message), ) .unwrap(); } } fn indented_message(message: &str) -> String { let mut out = String::new(); for (idx, line) in message.lines().enumerate() { Loading @@ -158,9 +225,39 @@ struct Meta { pub(crate) struct Changelog { #[serde(rename = "smithy-rs")] #[serde(default)] smithy_rs: Vec<ChangelogEntry>, smithy_rs: Vec<HandAuthoredEntry>, #[serde(rename = "aws-sdk-rust")] #[serde(default)] aws_sdk_rust: Vec<HandAuthoredEntry>, #[serde(rename = "aws-sdk-model")] #[serde(default)] sdk_models: Vec<SdkModelEntry>, } impl Changelog { fn into_entries(mut self) -> ChangelogEntries { self.aws_sdk_rust.sort_by_key(|entry| !entry.meta.tada); self.sdk_models.sort_by(|a, b| a.module.cmp(&b.module)); self.smithy_rs.sort_by_key(|entry| !entry.meta.tada); ChangelogEntries { smithy_rs: self .smithy_rs .into_iter() .map(ChangelogEntry::HandAuthored) .collect(), aws_sdk_rust: self .aws_sdk_rust .into_iter() .map(ChangelogEntry::HandAuthored) .chain(self.sdk_models.into_iter().map(ChangelogEntry::AwsSdkModel)) .collect(), } } } struct ChangelogEntries { smithy_rs: Vec<ChangelogEntry>, aws_sdk_rust: Vec<ChangelogEntry>, } Loading @@ -187,11 +284,10 @@ fn no_uncommited_changes(path: &Path) -> Result<()> { pub(crate) fn update_changelogs( changelog_next: impl AsRef<Path>, smithy_rs: impl AsRef<Path>, aws_sdk_rust: impl AsRef<Path>, smithy_rs_version: &str, aws_sdk_rust_version: &str, date: &str, smithy_rs_path: impl AsRef<Path>, aws_sdk_rust_path: impl AsRef<Path>, smithy_rs_release_header: &str, aws_sdk_rust_release_header: &str, ) -> Result<()> { no_uncommited_changes(changelog_next.as_ref()).context( "CHANGELOG.next.toml had unstaged changes. Refusing to perform changelog update.", Loading @@ -202,47 +298,39 @@ pub(crate) fn update_changelogs( errs )) })?; for (entries, path, version) in [ (changelog.smithy_rs, smithy_rs.as_ref(), smithy_rs_version), let ChangelogEntries { 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), ( changelog.aws_sdk_rust, aws_sdk_rust.as_ref(), aws_sdk_rust_version, aws_sdk_rust, aws_sdk_rust_path.as_ref(), aws_sdk_rust_release_header, ), ] { no_uncommited_changes(path) .with_context(|| format!("{} had unstaged changes", path.display()))?; let mut update = USE_UPDATE_CHANGELOGS.to_string(); update.push('\n'); update.push_str(&render(entries, version, date)); update.push_str(&render(&entries, release_header)); let current = std::fs::read_to_string(path)?.replace(USE_UPDATE_CHANGELOGS, ""); update.push_str(¤t); std::fs::write(path, update)?; } std::fs::write(changelog_next.as_ref(), EXAMPLE_ENTRY.trim())?; eprintln!("Changelogs updated:\n SDK: {aws_sdk_rust_version}\n Smithy: {smithy_rs_version}\n Date: {date}"); eprintln!("Changelogs updated!"); Ok(()) } /// Convert a list of changelog entries into markdown fn render(mut entries: Vec<ChangelogEntry>, version: &str, date: &str) -> String { entries.sort_by_key(|ent| !ent.meta.tada); let mut out = String::new(); let header = format!("{version} ({date})", version = version, date = date); out.push_str(&header); out.push('\n'); for _ in 0..header.len() { out.push('='); } out.push('\n'); let (breaking, non_breaking) = entries .iter() .partition::<Vec<_>, _>(|entry| entry.meta.breaking); fn render_handauthored<'a>(entries: impl Iterator<Item = &'a HandAuthoredEntry>, out: &mut String) { let (breaking, non_breaking) = entries.partition::<Vec<_>, _>(|entry| entry.meta.breaking); if !breaking.is_empty() { out.push_str("**Breaking Changes:**\n"); for change in breaking { change.render(&mut out); change.render(out); out.push('\n'); } out.push('\n') Loading @@ -251,15 +339,59 @@ fn render(mut entries: Vec<ChangelogEntry>, version: &str, date: &str) -> String if !non_breaking.is_empty() { out.push_str("**New this release:**\n"); for change in non_breaking { change.render(&mut out); change.render(out); out.push('\n'); } out.push('\n'); } } fn render_sdk_model_entries<'a>( entries: impl Iterator<Item = &'a SdkModelEntry>, out: &mut String, ) { let (features, docs) = entries.partition::<Vec<_>, _>(|entry| matches!(entry.kind, SdkModelChangeKind::Feature)); if !features.is_empty() { out.push_str("**Service Features:**\n"); for entry in features { entry.render(out); out.push('\n'); } out.push('\n'); } if !docs.is_empty() { out.push_str("**Service Documentation:**\n"); for entry in docs { entry.render(out); out.push('\n'); } out.push('\n'); } } /// 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'); for _ in 0..release_header.len() { out.push('='); } out.push('\n'); render_handauthored( entries.iter().filter_map(ChangelogEntry::hand_authored), &mut out, ); render_sdk_model_entries( entries.iter().filter_map(ChangelogEntry::aws_sdk_model), &mut out, ); let mut external_contribs = entries .iter() .map(|entry| entry.author.to_ascii_lowercase()) .filter_map(|entry| entry.hand_authored().map(|e| e.author.to_ascii_lowercase())) .filter(|author| !maintainers().contains(&author.as_str())) .collect::<Vec<_>>(); external_contribs.sort(); Loading @@ -272,10 +404,18 @@ fn render(mut entries: Vec<ChangelogEntry>, version: &str, date: &str) -> String .iter() .filter(|entry| { entry .author .eq_ignore_ascii_case(contributor_handle.as_str()) .hand_authored() .map(|e| e.author.eq_ignore_ascii_case(contributor_handle.as_str())) .unwrap_or(false) }) .flat_map(|entry| { entry .hand_authored() .unwrap() .references .iter() .map(|it| it.to_md_link()) }) .flat_map(|entry| entry.references.iter().map(|it| it.to_md_link())) .collect::<Vec<_>>(); contribution_references.sort(); contribution_references.dedup(); Loading @@ -292,25 +432,6 @@ fn render(mut entries: Vec<ChangelogEntry>, version: &str, date: &str) -> String out } /// Validate a changelog entry to ensure it follows standards fn validate(entry: &ChangelogEntry) -> Result<()> { if entry.author.is_empty() { bail!("Author must be set (was empty)"); } if !entry .author .chars() .all(|c| c.is_alphanumeric() || c == '-') { bail!("Author must be valid GitHub username: [a-zA-Z0-9\\-]") } if entry.references.is_empty() { bail!("Changelog entry must refer to at least one pull request or issue"); } Ok(()) } pub(crate) struct ChangelogNext; impl Lint for ChangelogNext { fn name(&self) -> &str { Loading Loading @@ -341,7 +462,7 @@ fn check_changelog_next(path: impl AsRef<Path>) -> std::result::Result<Changelog .map_err(|e| vec![LintError::via_display(e)])?; let mut errors = vec![]; for entry in parsed.aws_sdk_rust.iter().chain(parsed.smithy_rs.iter()) { if let Err(e) = validate(entry) { if let Err(e) = entry.validate() { errors.push(LintError::via_display(e)) } } Loading @@ -354,7 +475,7 @@ fn check_changelog_next(path: impl AsRef<Path>) -> std::result::Result<Changelog #[cfg(test)] mod test { use crate::changelog::{render, Changelog}; use crate::changelog::{render, Changelog, ChangelogEntries}; #[test] fn end_to_end_changelog() { Loading Loading @@ -398,11 +519,33 @@ blah blah """ meta = { breaking = false, tada = true, bug = false } references = ["smithy-rs#446"] [[aws-sdk-model]] module = "aws-sdk-s3" version = "0.14.0" kind = "Feature" message = "Some new API to do X" [[aws-sdk-model]] module = "aws-sdk-ec2" version = "0.12.0" kind = "Documentation" message = "Updated some docs" [[aws-sdk-model]] module = "aws-sdk-ec2" version = "0.12.0" kind = "Feature" message = "Some API change" "#; let changelog: Changelog = toml::from_str(changelog_toml).expect("valid changelog"); let rendered = render(changelog.smithy_rs, "v0.3.0", "January 4th, 2022"); let ChangelogEntries { aws_sdk_rust, smithy_rs, } = changelog.into_entries(); let expected = r#" let smithy_rs_rendered = render(&smithy_rs, "v0.3.0 (January 4th, 2022)"); let smithy_rs_expected = r#" v0.3.0 (January 4th, 2022) ========================== **Breaking Changes:** Loading @@ -422,6 +565,30 @@ Thank you for your contributions! ❤ - @external-contrib ([smithy-rs#446](https://github.com/awslabs/smithy-rs/issues/446)) "# .trim_start(); assert_eq!(expected, 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_expected = r#" v0.1.0 (January 4th, 2022) ========================== **Breaking Changes:** - ⚠ ([smithy-rs#445](https://github.com/awslabs/smithy-rs/issues/445)) I made a major change to update the AWS SDK **New this release:** - 🎉 ([smithy-rs#446](https://github.com/awslabs/smithy-rs/issues/446), @external-contrib) I made a change to update the code generator **Service Features:** - `aws-sdk-ec2` (0.12.0): Some API change - `aws-sdk-s3` (0.14.0): Some new API to do X **Service Documentation:** - `aws-sdk-ec2` (0.12.0): Updated some docs **Contributors** Thank you for your contributions! ❤ - @external-contrib ([smithy-rs#446](https://github.com/awslabs/smithy-rs/issues/446)) "# .trim_start(); pretty_assertions::assert_str_eq!(aws_sdk_expected, aws_sdk_rust_rendered); } } tools/sdk-lints/src/main.rs +43 −26 Original line number Diff line number Diff line Loading @@ -65,12 +65,9 @@ enum Args { dry_run: Option<bool>, }, UpdateChangelog { /// Whether or not independent crate versions are being used (defaults to false) #[clap(long)] smithy_version: Option<String>, #[clap(long)] sdk_version: Option<String>, #[clap(long)] date: Option<String>, independent_versioning: bool, }, } Loading Loading @@ -168,28 +165,56 @@ fn main() -> Result<()> { } } Args::UpdateChangelog { smithy_version, sdk_version, date, independent_versioning, } => { 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()?; changelog::update_changelogs( changelog_next_path, changelog_path, aws_changelog_path, &header, &header, )? } else { let auto = auto_changelog_meta()?; changelog::update_changelogs( repo_root().join("CHANGELOG.next.toml"), repo_root().join("CHANGELOG.md"), repo_root().join("aws/SDK_CHANGELOG.md"), &smithy_version.unwrap_or(auto.smithy_version), &sdk_version.unwrap_or(auto.sdk_version), &date.unwrap_or(auto.date), changelog_next_path, changelog_path, aws_changelog_path, &release_header_sync_versioned(&auto.smithy_version)?, &release_header_sync_versioned(&auto.sdk_version)?, )? } } } Ok(()) } struct ChangelogMeta { smithy_version: String, sdk_version: String, date: 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 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. Loading @@ -206,17 +231,9 @@ fn auto_changelog_meta() -> Result<ChangelogMeta> { }; let smithy_version = load_gradle_prop("smithy.rs.runtime.crate.version")?; let sdk_version = load_gradle_prop("aws.sdk.version")?; let now = OffsetDateTime::now_local()?; let date = format!( "{month} {day}, {year}", month = now.date().month(), day = Ordinal(now.date().day()), year = now.date().year() ); Ok(ChangelogMeta { smithy_version, sdk_version, date, }) } Loading Loading
tools/sdk-lints/Cargo.lock +79 −32 Original line number Diff line number Diff line Loading @@ -2,11 +2,20 @@ # It is not intended for manual editing. version = 3 [[package]] name = "ansi_term" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" dependencies = [ "winapi", ] [[package]] name = "anyhow" version = "1.0.50" version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ecc78c299ae753905840c5d3ba036c51f61ce5a98a83f98d9c9d29dffd427f71" checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc" [[package]] name = "atty" Loading @@ -33,9 +42,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "cargo_toml" version = "0.10.1" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6d613611c914a7db07f28526941ce1e956d2f977b0c5e2014fbfa42230d420f" checksum = "363c7cfaa15f101415c4ac9e68706ca4a2277773932828b33f96e59d28c68e62" dependencies = [ "serde", "serde_derive", Loading @@ -44,16 +53,16 @@ dependencies = [ [[package]] name = "clap" version = "3.1.7" version = "3.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c67e7973e74896f4bba06ca2dcfd28d54f9cb8c035e940a32b88ed48f5f5ecf2" checksum = "85a35a599b11c089a7f49105658d089b8f2cf0882993c17daf6de15285c2c35d" dependencies = [ "atty", "bitflags", "clap_derive", "clap_lex", "indexmap", "lazy_static", "os_str_bytes", "strsim", "termcolor", "textwrap", Loading @@ -72,6 +81,31 @@ dependencies = [ "syn", ] [[package]] name = "clap_lex" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a37c35f1112dad5e6e0b1adaff798507497a18fceeb30cceb3bae7d1427b9213" dependencies = [ "os_str_bytes", ] [[package]] name = "ctor" version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c" dependencies = [ "quote", "syn", ] [[package]] name = "diff" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" [[package]] name = "hashbrown" version = "0.11.2" Loading Loading @@ -111,21 +145,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f" [[package]] name = "memchr" version = "2.4.1" version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b" [[package]] name = "num-integer" version = "0.1.44" version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ "autocfg", "num-traits", Loading Loading @@ -163,8 +191,26 @@ name = "os_str_bytes" version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64" [[package]] name = "output_vt100" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" dependencies = [ "winapi", ] [[package]] name = "pretty_assertions" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c89f989ac94207d048d92db058e4f6ec7342b0971fc58d1271ca148b799b3563" dependencies = [ "memchr", "ansi_term", "ctor", "diff", "output_vt100", ] [[package]] Loading Loading @@ -193,18 +239,18 @@ dependencies = [ [[package]] name = "proc-macro2" version = "1.0.32" version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" dependencies = [ "unicode-xid", ] [[package]] name = "quote" version = "1.0.10" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" dependencies = [ "proc-macro2", ] Loading @@ -218,6 +264,7 @@ dependencies = [ "clap", "lazy_static", "ordinal", "pretty_assertions", "serde", "time", "toml", Loading @@ -225,18 +272,18 @@ dependencies = [ [[package]] name = "serde" version = "1.0.130" version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.130" version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" dependencies = [ "proc-macro2", "quote", Loading @@ -251,9 +298,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" version = "1.0.82" version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59" checksum = "7ff7c592601f11445996a06f8ad0c27f094a58857c2f89e97974ab9235b92c52" dependencies = [ "proc-macro2", "quote", Loading Loading @@ -287,18 +334,18 @@ dependencies = [ [[package]] name = "toml" version = "0.5.8" version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" dependencies = [ "serde", ] [[package]] name = "unicode-xid" version = "0.2.2" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" [[package]] name = "version_check" Loading
tools/sdk-lints/Cargo.toml +3 −0 Original line number Diff line number Diff line Loading @@ -19,3 +19,6 @@ serde = { version = "1", features = ["derive"]} lazy_static = "1.4.0" time = { version = "0.3.9", features = ["local-offset"]} ordinal = "0.3.2" [dev-dependencies] pretty_assertions = "1.2.1"
tools/sdk-lints/src/changelog.rs +265 −98 Original line number Diff line number Diff line Loading @@ -36,8 +36,29 @@ fn maintainers() -> Vec<&'static str> { .collect() } enum ChangelogEntry { HandAuthored(HandAuthoredEntry), AwsSdkModel(SdkModelEntry), } impl ChangelogEntry { fn hand_authored(&self) -> Option<&HandAuthoredEntry> { match self { ChangelogEntry::HandAuthored(hand_authored) => Some(hand_authored), _ => None, } } fn aws_sdk_model(&self) -> Option<&SdkModelEntry> { match self { ChangelogEntry::AwsSdkModel(sdk_model) => Some(sdk_model), _ => None, } } } #[derive(Deserialize)] struct ChangelogEntry { struct HandAuthoredEntry { message: String, meta: Meta, author: String, Loading @@ -45,6 +66,92 @@ struct ChangelogEntry { references: Vec<Reference>, } impl HandAuthoredEntry { /// Validate a changelog entry to ensure it follows standards fn validate(&self) -> Result<()> { if self.author.is_empty() { bail!("Author must be set (was empty)"); } if !self.author.chars().all(|c| c.is_alphanumeric() || c == '-') { bail!("Author must be valid GitHub username: [a-zA-Z0-9\\-]") } if self.references.is_empty() { bail!("Changelog entry must refer to at least one pull request or issue"); } Ok(()) } /// Write a changelog entry to [out] /// /// Example output: /// `- Add a feature (smithy-rs#123, @contributor)` fn render(&self, mut out: &mut String) { let mut meta = String::new(); if self.meta.bug { meta.push('🐛'); } if self.meta.breaking { meta.push('⚠'); } if self.meta.tada { meta.push('🎉'); } if !meta.is_empty() { meta.push(' '); } let mut references = self .references .iter() .map(Reference::to_md_link) .collect::<Vec<_>>(); if !maintainers().contains(&self.author.to_ascii_lowercase().as_str()) { references.push(format!("@{}", self.author.to_ascii_lowercase())); }; if !references.is_empty() { write!(meta, "({}) ", references.join(", ")).unwrap(); } write!( &mut out, "- {meta}{message}", meta = meta, message = indented_message(&self.message), ) .unwrap(); } } #[derive(Deserialize)] enum SdkModelChangeKind { Documentation, Feature, } #[derive(Deserialize)] struct SdkModelEntry { /// SDK module name (e.g., "aws-sdk-s3" for S3) module: String, /// SDK module version number (e.g., "0.14.0") version: String, /// What changed kind: SdkModelChangeKind, /// More details about the change message: String, } impl SdkModelEntry { fn render(&self, out: &mut String) { write!( out, "- `{module}` ({version}): {message}", module = self.module, version = self.version, message = self.message ) .unwrap(); } } struct Reference { repo: String, number: usize, Loading Loading @@ -93,46 +200,6 @@ impl FromStr for Reference { } } impl ChangelogEntry { /// Write a changelog entry to [out] /// /// Example output: /// `- Add a feature (smithy-rs#123, @contributor)` fn render(&self, mut out: &mut String) { let mut meta = String::new(); if self.meta.bug { meta.push('🐛'); } if self.meta.breaking { meta.push('⚠'); } if self.meta.tada { meta.push('🎉'); } if !meta.is_empty() { meta.push(' '); } let mut references = self .references .iter() .map(Reference::to_md_link) .collect::<Vec<_>>(); if !maintainers().contains(&self.author.to_ascii_lowercase().as_str()) { references.push(format!("@{}", self.author.to_ascii_lowercase())); }; if !references.is_empty() { write!(meta, "({}) ", references.join(", ")).unwrap(); } write!( &mut out, "- {meta}{message}", meta = meta, message = indented_message(&self.message), ) .unwrap(); } } fn indented_message(message: &str) -> String { let mut out = String::new(); for (idx, line) in message.lines().enumerate() { Loading @@ -158,9 +225,39 @@ struct Meta { pub(crate) struct Changelog { #[serde(rename = "smithy-rs")] #[serde(default)] smithy_rs: Vec<ChangelogEntry>, smithy_rs: Vec<HandAuthoredEntry>, #[serde(rename = "aws-sdk-rust")] #[serde(default)] aws_sdk_rust: Vec<HandAuthoredEntry>, #[serde(rename = "aws-sdk-model")] #[serde(default)] sdk_models: Vec<SdkModelEntry>, } impl Changelog { fn into_entries(mut self) -> ChangelogEntries { self.aws_sdk_rust.sort_by_key(|entry| !entry.meta.tada); self.sdk_models.sort_by(|a, b| a.module.cmp(&b.module)); self.smithy_rs.sort_by_key(|entry| !entry.meta.tada); ChangelogEntries { smithy_rs: self .smithy_rs .into_iter() .map(ChangelogEntry::HandAuthored) .collect(), aws_sdk_rust: self .aws_sdk_rust .into_iter() .map(ChangelogEntry::HandAuthored) .chain(self.sdk_models.into_iter().map(ChangelogEntry::AwsSdkModel)) .collect(), } } } struct ChangelogEntries { smithy_rs: Vec<ChangelogEntry>, aws_sdk_rust: Vec<ChangelogEntry>, } Loading @@ -187,11 +284,10 @@ fn no_uncommited_changes(path: &Path) -> Result<()> { pub(crate) fn update_changelogs( changelog_next: impl AsRef<Path>, smithy_rs: impl AsRef<Path>, aws_sdk_rust: impl AsRef<Path>, smithy_rs_version: &str, aws_sdk_rust_version: &str, date: &str, smithy_rs_path: impl AsRef<Path>, aws_sdk_rust_path: impl AsRef<Path>, smithy_rs_release_header: &str, aws_sdk_rust_release_header: &str, ) -> Result<()> { no_uncommited_changes(changelog_next.as_ref()).context( "CHANGELOG.next.toml had unstaged changes. Refusing to perform changelog update.", Loading @@ -202,47 +298,39 @@ pub(crate) fn update_changelogs( errs )) })?; for (entries, path, version) in [ (changelog.smithy_rs, smithy_rs.as_ref(), smithy_rs_version), let ChangelogEntries { 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), ( changelog.aws_sdk_rust, aws_sdk_rust.as_ref(), aws_sdk_rust_version, aws_sdk_rust, aws_sdk_rust_path.as_ref(), aws_sdk_rust_release_header, ), ] { no_uncommited_changes(path) .with_context(|| format!("{} had unstaged changes", path.display()))?; let mut update = USE_UPDATE_CHANGELOGS.to_string(); update.push('\n'); update.push_str(&render(entries, version, date)); update.push_str(&render(&entries, release_header)); let current = std::fs::read_to_string(path)?.replace(USE_UPDATE_CHANGELOGS, ""); update.push_str(¤t); std::fs::write(path, update)?; } std::fs::write(changelog_next.as_ref(), EXAMPLE_ENTRY.trim())?; eprintln!("Changelogs updated:\n SDK: {aws_sdk_rust_version}\n Smithy: {smithy_rs_version}\n Date: {date}"); eprintln!("Changelogs updated!"); Ok(()) } /// Convert a list of changelog entries into markdown fn render(mut entries: Vec<ChangelogEntry>, version: &str, date: &str) -> String { entries.sort_by_key(|ent| !ent.meta.tada); let mut out = String::new(); let header = format!("{version} ({date})", version = version, date = date); out.push_str(&header); out.push('\n'); for _ in 0..header.len() { out.push('='); } out.push('\n'); let (breaking, non_breaking) = entries .iter() .partition::<Vec<_>, _>(|entry| entry.meta.breaking); fn render_handauthored<'a>(entries: impl Iterator<Item = &'a HandAuthoredEntry>, out: &mut String) { let (breaking, non_breaking) = entries.partition::<Vec<_>, _>(|entry| entry.meta.breaking); if !breaking.is_empty() { out.push_str("**Breaking Changes:**\n"); for change in breaking { change.render(&mut out); change.render(out); out.push('\n'); } out.push('\n') Loading @@ -251,15 +339,59 @@ fn render(mut entries: Vec<ChangelogEntry>, version: &str, date: &str) -> String if !non_breaking.is_empty() { out.push_str("**New this release:**\n"); for change in non_breaking { change.render(&mut out); change.render(out); out.push('\n'); } out.push('\n'); } } fn render_sdk_model_entries<'a>( entries: impl Iterator<Item = &'a SdkModelEntry>, out: &mut String, ) { let (features, docs) = entries.partition::<Vec<_>, _>(|entry| matches!(entry.kind, SdkModelChangeKind::Feature)); if !features.is_empty() { out.push_str("**Service Features:**\n"); for entry in features { entry.render(out); out.push('\n'); } out.push('\n'); } if !docs.is_empty() { out.push_str("**Service Documentation:**\n"); for entry in docs { entry.render(out); out.push('\n'); } out.push('\n'); } } /// 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'); for _ in 0..release_header.len() { out.push('='); } out.push('\n'); render_handauthored( entries.iter().filter_map(ChangelogEntry::hand_authored), &mut out, ); render_sdk_model_entries( entries.iter().filter_map(ChangelogEntry::aws_sdk_model), &mut out, ); let mut external_contribs = entries .iter() .map(|entry| entry.author.to_ascii_lowercase()) .filter_map(|entry| entry.hand_authored().map(|e| e.author.to_ascii_lowercase())) .filter(|author| !maintainers().contains(&author.as_str())) .collect::<Vec<_>>(); external_contribs.sort(); Loading @@ -272,10 +404,18 @@ fn render(mut entries: Vec<ChangelogEntry>, version: &str, date: &str) -> String .iter() .filter(|entry| { entry .author .eq_ignore_ascii_case(contributor_handle.as_str()) .hand_authored() .map(|e| e.author.eq_ignore_ascii_case(contributor_handle.as_str())) .unwrap_or(false) }) .flat_map(|entry| { entry .hand_authored() .unwrap() .references .iter() .map(|it| it.to_md_link()) }) .flat_map(|entry| entry.references.iter().map(|it| it.to_md_link())) .collect::<Vec<_>>(); contribution_references.sort(); contribution_references.dedup(); Loading @@ -292,25 +432,6 @@ fn render(mut entries: Vec<ChangelogEntry>, version: &str, date: &str) -> String out } /// Validate a changelog entry to ensure it follows standards fn validate(entry: &ChangelogEntry) -> Result<()> { if entry.author.is_empty() { bail!("Author must be set (was empty)"); } if !entry .author .chars() .all(|c| c.is_alphanumeric() || c == '-') { bail!("Author must be valid GitHub username: [a-zA-Z0-9\\-]") } if entry.references.is_empty() { bail!("Changelog entry must refer to at least one pull request or issue"); } Ok(()) } pub(crate) struct ChangelogNext; impl Lint for ChangelogNext { fn name(&self) -> &str { Loading Loading @@ -341,7 +462,7 @@ fn check_changelog_next(path: impl AsRef<Path>) -> std::result::Result<Changelog .map_err(|e| vec![LintError::via_display(e)])?; let mut errors = vec![]; for entry in parsed.aws_sdk_rust.iter().chain(parsed.smithy_rs.iter()) { if let Err(e) = validate(entry) { if let Err(e) = entry.validate() { errors.push(LintError::via_display(e)) } } Loading @@ -354,7 +475,7 @@ fn check_changelog_next(path: impl AsRef<Path>) -> std::result::Result<Changelog #[cfg(test)] mod test { use crate::changelog::{render, Changelog}; use crate::changelog::{render, Changelog, ChangelogEntries}; #[test] fn end_to_end_changelog() { Loading Loading @@ -398,11 +519,33 @@ blah blah """ meta = { breaking = false, tada = true, bug = false } references = ["smithy-rs#446"] [[aws-sdk-model]] module = "aws-sdk-s3" version = "0.14.0" kind = "Feature" message = "Some new API to do X" [[aws-sdk-model]] module = "aws-sdk-ec2" version = "0.12.0" kind = "Documentation" message = "Updated some docs" [[aws-sdk-model]] module = "aws-sdk-ec2" version = "0.12.0" kind = "Feature" message = "Some API change" "#; let changelog: Changelog = toml::from_str(changelog_toml).expect("valid changelog"); let rendered = render(changelog.smithy_rs, "v0.3.0", "January 4th, 2022"); let ChangelogEntries { aws_sdk_rust, smithy_rs, } = changelog.into_entries(); let expected = r#" let smithy_rs_rendered = render(&smithy_rs, "v0.3.0 (January 4th, 2022)"); let smithy_rs_expected = r#" v0.3.0 (January 4th, 2022) ========================== **Breaking Changes:** Loading @@ -422,6 +565,30 @@ Thank you for your contributions! ❤ - @external-contrib ([smithy-rs#446](https://github.com/awslabs/smithy-rs/issues/446)) "# .trim_start(); assert_eq!(expected, 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_expected = r#" v0.1.0 (January 4th, 2022) ========================== **Breaking Changes:** - ⚠ ([smithy-rs#445](https://github.com/awslabs/smithy-rs/issues/445)) I made a major change to update the AWS SDK **New this release:** - 🎉 ([smithy-rs#446](https://github.com/awslabs/smithy-rs/issues/446), @external-contrib) I made a change to update the code generator **Service Features:** - `aws-sdk-ec2` (0.12.0): Some API change - `aws-sdk-s3` (0.14.0): Some new API to do X **Service Documentation:** - `aws-sdk-ec2` (0.12.0): Updated some docs **Contributors** Thank you for your contributions! ❤ - @external-contrib ([smithy-rs#446](https://github.com/awslabs/smithy-rs/issues/446)) "# .trim_start(); pretty_assertions::assert_str_eq!(aws_sdk_expected, aws_sdk_rust_rendered); } }
tools/sdk-lints/src/main.rs +43 −26 Original line number Diff line number Diff line Loading @@ -65,12 +65,9 @@ enum Args { dry_run: Option<bool>, }, UpdateChangelog { /// Whether or not independent crate versions are being used (defaults to false) #[clap(long)] smithy_version: Option<String>, #[clap(long)] sdk_version: Option<String>, #[clap(long)] date: Option<String>, independent_versioning: bool, }, } Loading Loading @@ -168,28 +165,56 @@ fn main() -> Result<()> { } } Args::UpdateChangelog { smithy_version, sdk_version, date, independent_versioning, } => { 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()?; changelog::update_changelogs( changelog_next_path, changelog_path, aws_changelog_path, &header, &header, )? } else { let auto = auto_changelog_meta()?; changelog::update_changelogs( repo_root().join("CHANGELOG.next.toml"), repo_root().join("CHANGELOG.md"), repo_root().join("aws/SDK_CHANGELOG.md"), &smithy_version.unwrap_or(auto.smithy_version), &sdk_version.unwrap_or(auto.sdk_version), &date.unwrap_or(auto.date), changelog_next_path, changelog_path, aws_changelog_path, &release_header_sync_versioned(&auto.smithy_version)?, &release_header_sync_versioned(&auto.sdk_version)?, )? } } } Ok(()) } struct ChangelogMeta { smithy_version: String, sdk_version: String, date: 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 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. Loading @@ -206,17 +231,9 @@ fn auto_changelog_meta() -> Result<ChangelogMeta> { }; let smithy_version = load_gradle_prop("smithy.rs.runtime.crate.version")?; let sdk_version = load_gradle_prop("aws.sdk.version")?; let now = OffsetDateTime::now_local()?; let date = format!( "{month} {day}, {year}", month = now.date().month(), day = Ordinal(now.date().day()), year = now.date().year() ); Ok(ChangelogMeta { smithy_version, sdk_version, date, }) } Loading