Support unchanging versions in the versioner (#3414)

## Motivation and Context
The nested path dependencies in our generated runtime crates cause
issues when simulating a release. This strips those out in order to
support testing a release where some versions _don't_ change.

## Testing
https://github.com/smithy-lang/smithy-rs/actions/runs/7917462892


----

_By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice._
This commit is contained in:
Russell Cohen 2024-02-15 11:02:59 -05:00 committed by GitHub
parent 1ea9d055f0
commit 30421e1333
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 152 additions and 18 deletions

View File

@ -148,6 +148,16 @@ version = "1.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c"
[[package]]
name = "cargo_toml"
version = "0.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dc9f7a067415ab5058020f04c60ec7b557084dbec0e021217bbabc7a8d38d14"
dependencies = [
"serde",
"toml 0.8.8",
]
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.83" version = "1.0.83"
@ -903,6 +913,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"camino", "camino",
"cargo_toml",
"clap", "clap",
"crates-index", "crates-index",
"indicatif", "indicatif",
@ -911,6 +922,7 @@ dependencies = [
"tempfile", "tempfile",
"test-common", "test-common",
"toml 0.5.11", "toml 0.5.11",
"toml_edit 0.22.5",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
] ]
@ -1276,7 +1288,7 @@ dependencies = [
"serde", "serde",
"serde_spanned", "serde_spanned",
"toml_datetime", "toml_datetime",
"toml_edit", "toml_edit 0.21.0",
] ]
[[package]] [[package]]
@ -1298,7 +1310,18 @@ dependencies = [
"serde", "serde",
"serde_spanned", "serde_spanned",
"toml_datetime", "toml_datetime",
"winnow", "winnow 0.5.28",
]
[[package]]
name = "toml_edit"
version = "0.22.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99e68c159e8f5ba8a28c4eb7b0c0c190d77bb479047ca713270048145a9ad28a"
dependencies = [
"indexmap 2.1.0",
"toml_datetime",
"winnow 0.6.1",
] ]
[[package]] [[package]]
@ -1684,6 +1707,15 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "winnow"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d90f4e0f530c4c69f62b80d839e9ef3855edc9cba471a160c4d692deed62b401"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "winreg" name = "winreg"
version = "0.50.0" version = "0.50.0"

View File

@ -25,6 +25,9 @@ tempfile = "3.9.0"
toml = { version = "0.5.8", features = ["preserve_order"] } toml = { version = "0.5.8", features = ["preserve_order"] }
tracing = "0.1.40" tracing = "0.1.40"
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
# why both? cargo_toml can't write out to a file because of a toml_rs longstanding issue/bug
cargo_toml = "0.19.0"
toml_edit = "0.22"
[dev-dependencies] [dev-dependencies]
test-common = { path = "./test-common" } test-common = { path = "./test-common" }

View File

@ -10,9 +10,11 @@ use crate::{
}; };
use anyhow::{bail, Context, Result}; use anyhow::{bail, Context, Result};
use camino::Utf8Path; use camino::Utf8Path;
use cargo_toml::Manifest;
use indicatif::{ProgressBar, ProgressStyle}; use indicatif::{ProgressBar, ProgressStyle};
use smithy_rs_tool_common::command::sync::CommandExt; use smithy_rs_tool_common::command::sync::CommandExt;
use std::{fs, time::Duration}; use std::{fs, time::Duration};
use toml_edit::Document;
pub fn patch(args: PatchRuntime) -> Result<()> { pub fn patch(args: PatchRuntime) -> Result<()> {
let smithy_rs = step("Resolving smithy-rs", || { let smithy_rs = step("Resolving smithy-rs", || {
@ -22,6 +24,11 @@ pub fn patch(args: PatchRuntime) -> Result<()> {
bail!("smithy-rs has a dirty working tree. Aborting."); bail!("smithy-rs has a dirty working tree. Aborting.");
} }
let aws_sdk_rust = step("Resolving aws-sdk-rust", || Repo::new(Some(&args.sdk_path)))?;
if is_dirty(&aws_sdk_rust)? {
bail!("aws-sdk-rust has a dirty working tree. Aborting.");
}
step( step(
"Patching smithy-rs/gradle.properties with given crate version numbers", "Patching smithy-rs/gradle.properties with given crate version numbers",
|| patch_gradle_properties(&smithy_rs, &args), || patch_gradle_properties(&smithy_rs, &args),
@ -76,7 +83,9 @@ pub fn patch_with(args: PatchRuntimeWith) -> Result<()> {
apply_version_only_dependencies(&aws_sdk_rust) apply_version_only_dependencies(&aws_sdk_rust)
})?; })?;
step("Patching aws-sdk-rust root Cargo.toml", || { step("Patching aws-sdk-rust root Cargo.toml", || {
patch_workspace_cargo_toml(&aws_sdk_rust, &args.runtime_crate_path) let crates_to_patch =
remove_unchanged_dependencies(&aws_sdk_rust, &args.runtime_crate_path)?;
patch_workspace_cargo_toml(&aws_sdk_rust, &args.runtime_crate_path, crates_to_patch)
})?; })?;
step("Running cargo update", || { step("Running cargo update", || {
aws_sdk_rust aws_sdk_rust
@ -127,27 +136,46 @@ fn apply_version_only_dependencies(aws_sdk_rust: &Repo) -> Result<()> {
Ok(()) Ok(())
} }
fn patch_workspace_cargo_toml(aws_sdk_rust: &Repo, runtime_crate_path: &Utf8Path) -> Result<()> { /// Determine if a given crate has a new version vs. the release we're comparing
let crates_to_patch = fs::read_dir(runtime_crate_path) fn crate_version_has_changed(
.context(format!( crate_name: &str,
"could list crates in directory {:?}", aws_sdk_rust: &Repo,
runtime_crate_path runtime_crate_path: &Utf8Path,
))? ) -> Result<bool> {
.map(|dir| dir.unwrap().file_name()) let sdk_cargo_toml = aws_sdk_rust
.map(|osstr| osstr.into_string().expect("invalid utf-8 directory")) .root
.filter(|name| name.starts_with("aws-")) .join("sdk")
.collect::<Vec<_>>(); .join(crate_name)
.join("Cargo.toml");
let to_patch_cargo_toml = runtime_crate_path.join(crate_name).join("Cargo.toml");
assert!(
sdk_cargo_toml.exists(),
"{:?} did not exist!",
sdk_cargo_toml
);
assert!(
to_patch_cargo_toml.exists(),
"{:?} did not exist!",
to_patch_cargo_toml
);
let sdk_cargo_toml = Manifest::from_path(sdk_cargo_toml).context("could not parse")?;
let to_patch_toml = Manifest::from_path(to_patch_cargo_toml).context("could not parse")?;
Ok(sdk_cargo_toml.package().version() != to_patch_toml.package().version())
}
fn patch_workspace_cargo_toml(
aws_sdk_rust: &Repo,
runtime_crate_path: &Utf8Path,
crates_to_patch: impl Iterator<Item = String>,
) -> Result<()> {
let patch_sections = crates_to_patch let patch_sections = crates_to_patch
.iter() .map(|crate_name| {
.map(|crte| { let path = runtime_crate_path.join(&crate_name);
let path = runtime_crate_path.join(crte);
assert!( assert!(
path.exists(), path.exists(),
"tried to reference a crate that did not exist!" "tried to reference a crate that did not exist!"
); );
format!( format!(
"{crte} = {{ path = '{}' }}", "{crate_name} = {{ path = '{}' }}",
path.canonicalize_utf8().unwrap() path.canonicalize_utf8().unwrap()
) )
}) })
@ -164,6 +192,77 @@ fn patch_workspace_cargo_toml(aws_sdk_rust: &Repo, runtime_crate_path: &Utf8Path
Ok(()) Ok(())
} }
/// Removes Path dependencies referring to unchanged crates & returns a list of crates to patch
fn remove_unchanged_dependencies(
aws_sdk_rust: &Repo,
runtime_crate_path: &Utf8Path,
) -> Result<impl Iterator<Item = String>> {
let all_crates = fs::read_dir(runtime_crate_path)
.context(format!(
"could list crates in directory {:?}",
runtime_crate_path
))?
.map(|dir| dir.unwrap().file_name())
.map(|osstr| osstr.into_string().expect("invalid utf-8 directory"))
.collect::<Vec<_>>();
let (crates_to_patch, unchanged_crates): (Vec<_>, Vec<_>) =
all_crates.clone().into_iter().partition(|crate_dir| {
crate_version_has_changed(crate_dir, aws_sdk_rust, runtime_crate_path)
.expect("failed to determine change-status")
});
for patched_crate in &all_crates {
remove_unchanged_path_dependencies(runtime_crate_path, &unchanged_crates, patched_crate)?;
}
Ok(crates_to_patch
.into_iter()
.filter(|crte| crte.starts_with("aws-")))
}
/// Remove `path = ...` from the dependency section for unchanged crates
///
/// If we leave these path dependencies in, we'll get an error when we try to patch because the
/// version numbers are the same.
fn remove_unchanged_path_dependencies(
runtime_crate_path: &Utf8Path,
unchanged_crates: &[String],
patched_crate: &String,
) -> Result<()> {
let path = runtime_crate_path.join(patched_crate).join("Cargo.toml");
let manifest = Manifest::from_path(&path)?;
let mut mutable_manifest = fs::read_to_string(&path)
.context("failed to read file")
.context(path.clone())?
.parse::<Document>()
.context("invalid toml in manifest!")?;
let mut updates = false;
let sections = [
(manifest.dependencies, "dependencies"),
(manifest.dev_dependencies, "dev-dependencies"),
];
for (deps_set, key) in sections {
for (dependency_name, dependency_metadata) in deps_set.iter() {
if unchanged_crates.iter().any(|crate_name| {
crate_name.as_str()
== dependency_metadata
.package()
.unwrap_or(dependency_name.as_str())
}) {
mutable_manifest[key][dependency_name]
.as_table_mut()
.unwrap()
.remove("path");
updates = true
}
}
}
if updates {
fs::write(&path, mutable_manifest.to_string()).context("failed to write back manifest")?
}
Ok(())
}
fn is_dirty(repo: &Repo) -> Result<bool> { fn is_dirty(repo: &Repo) -> Result<bool> {
let result = repo let result = repo
.git(["status", "--porcelain"]) .git(["status", "--porcelain"])