diff options
Diffstat (limited to 'release/src/semver_check.rs')
| -rw-r--r-- | release/src/semver_check.rs | 178 |
1 files changed, 0 insertions, 178 deletions
diff --git a/release/src/semver_check.rs b/release/src/semver_check.rs deleted file mode 100644 index 6255260f3..000000000 --- a/release/src/semver_check.rs +++ /dev/null | |||
| @@ -1,178 +0,0 @@ | |||
| 1 | use std::collections::HashSet; | ||
| 2 | use std::env; | ||
| 3 | use std::path::PathBuf; | ||
| 4 | |||
| 5 | use anyhow::anyhow; | ||
| 6 | use cargo_semver_checks::{Check, GlobalConfig, ReleaseType, Rustdoc}; | ||
| 7 | use flate2::read::GzDecoder; | ||
| 8 | use tar::Archive; | ||
| 9 | |||
| 10 | use crate::cargo::CargoArgsBuilder; | ||
| 11 | use crate::types::{BuildConfig, Crate}; | ||
| 12 | |||
| 13 | /// Return the minimum required bump for the next release. | ||
| 14 | /// Even if nothing changed this will be [ReleaseType::Patch] | ||
| 15 | pub fn minimum_update(krate: &Crate) -> Result<ReleaseType, anyhow::Error> { | ||
| 16 | let config = krate.configs.first().unwrap(); // TODO | ||
| 17 | |||
| 18 | let package_name = krate.name.clone(); | ||
| 19 | let baseline_path = download_baseline(&package_name, &krate.version)?; | ||
| 20 | let mut baseline_krate = krate.clone(); | ||
| 21 | baseline_krate.path = baseline_path; | ||
| 22 | |||
| 23 | // Compare features as it's not covered by semver-checks | ||
| 24 | if compare_features(&baseline_krate, &krate)? { | ||
| 25 | return Ok(ReleaseType::Minor); | ||
| 26 | } | ||
| 27 | let baseline_path = build_doc_json(&baseline_krate, config)?; | ||
| 28 | let current_path = build_doc_json(krate, config)?; | ||
| 29 | |||
| 30 | let baseline = Rustdoc::from_path(&baseline_path); | ||
| 31 | let doc = Rustdoc::from_path(¤t_path); | ||
| 32 | let mut semver_check = Check::new(doc); | ||
| 33 | semver_check.with_default_features(); | ||
| 34 | semver_check.set_baseline(baseline); | ||
| 35 | semver_check.set_packages(vec![package_name]); | ||
| 36 | let extra_current_features = config.features.clone(); | ||
| 37 | let extra_baseline_features = config.features.clone(); | ||
| 38 | semver_check.set_extra_features(extra_current_features, extra_baseline_features); | ||
| 39 | if let Some(target) = &config.target { | ||
| 40 | semver_check.set_build_target(target.clone()); | ||
| 41 | } | ||
| 42 | let mut cfg = GlobalConfig::new(); | ||
| 43 | cfg.set_log_level(Some(log::Level::Info)); | ||
| 44 | |||
| 45 | let result = semver_check.check_release(&mut cfg)?; | ||
| 46 | |||
| 47 | let mut min_required_update = ReleaseType::Patch; | ||
| 48 | for (_, report) in result.crate_reports() { | ||
| 49 | if let Some(required_bump) = report.required_bump() { | ||
| 50 | let required_is_stricter = | ||
| 51 | (min_required_update == ReleaseType::Patch) || (required_bump == ReleaseType::Major); | ||
| 52 | if required_is_stricter { | ||
| 53 | min_required_update = required_bump; | ||
| 54 | } | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | Ok(min_required_update) | ||
| 59 | } | ||
| 60 | |||
| 61 | fn compare_features(old: &Crate, new: &Crate) -> Result<bool, anyhow::Error> { | ||
| 62 | let mut old = read_features(&old.path)?; | ||
| 63 | let new = read_features(&new.path)?; | ||
| 64 | |||
| 65 | old.retain(|r| !new.contains(r)); | ||
| 66 | log::info!("Features removed in new: {:?}", old); | ||
| 67 | Ok(!old.is_empty()) | ||
| 68 | } | ||
| 69 | |||
| 70 | fn download_baseline(name: &str, version: &str) -> Result<PathBuf, anyhow::Error> { | ||
| 71 | let config = crates_index::IndexConfig { | ||
| 72 | dl: "https://crates.io/api/v1/crates".to_string(), | ||
| 73 | api: Some("https://crates.io".to_string()), | ||
| 74 | }; | ||
| 75 | |||
| 76 | let url = | ||
| 77 | config | ||
| 78 | .download_url(name, version) | ||
| 79 | .ok_or(anyhow!("unable to download baseline for {}-{}", name, version))?; | ||
| 80 | |||
| 81 | let parent_dir = env::var("RELEASER_CACHE").map_err(|_| anyhow!("RELEASER_CACHE not set"))?; | ||
| 82 | |||
| 83 | let extract_path = PathBuf::from(&parent_dir).join(format!("{}-{}", name, version)); | ||
| 84 | |||
| 85 | if extract_path.exists() { | ||
| 86 | return Ok(extract_path); | ||
| 87 | } | ||
| 88 | |||
| 89 | let response = reqwest::blocking::get(url)?; | ||
| 90 | let bytes = response.bytes()?; | ||
| 91 | |||
| 92 | let decoder = GzDecoder::new(&bytes[..]); | ||
| 93 | let mut archive = Archive::new(decoder); | ||
| 94 | archive.unpack(&parent_dir)?; | ||
| 95 | |||
| 96 | Ok(extract_path) | ||
| 97 | } | ||
| 98 | |||
| 99 | fn read_features(crate_path: &PathBuf) -> Result<HashSet<String>, anyhow::Error> { | ||
| 100 | let cargo_toml_path = crate_path.join("Cargo.toml"); | ||
| 101 | |||
| 102 | if !cargo_toml_path.exists() { | ||
| 103 | return Err(anyhow!("Cargo.toml not found at {:?}", cargo_toml_path)); | ||
| 104 | } | ||
| 105 | |||
| 106 | let manifest = cargo_manifest::Manifest::from_path(&cargo_toml_path)?; | ||
| 107 | |||
| 108 | let mut set = HashSet::new(); | ||
| 109 | if let Some(features) = manifest.features { | ||
| 110 | for f in features.keys() { | ||
| 111 | set.insert(f.clone()); | ||
| 112 | } | ||
| 113 | } | ||
| 114 | if let Some(deps) = manifest.dependencies { | ||
| 115 | for (k, v) in deps.iter() { | ||
| 116 | if v.optional() { | ||
| 117 | set.insert(k.clone()); | ||
| 118 | } | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | Ok(set) | ||
| 123 | } | ||
| 124 | |||
| 125 | fn build_doc_json(krate: &Crate, config: &BuildConfig) -> Result<PathBuf, anyhow::Error> { | ||
| 126 | let target_dir = std::env::var("CARGO_TARGET_DIR"); | ||
| 127 | |||
| 128 | let target_path = if let Ok(target) = target_dir { | ||
| 129 | PathBuf::from(target) | ||
| 130 | } else { | ||
| 131 | PathBuf::from(&krate.path).join("target") | ||
| 132 | }; | ||
| 133 | |||
| 134 | let current_path = target_path; | ||
| 135 | let current_path = if let Some(target) = &config.target { | ||
| 136 | current_path.join(target.clone()) | ||
| 137 | } else { | ||
| 138 | current_path | ||
| 139 | }; | ||
| 140 | let current_path = current_path | ||
| 141 | .join("doc") | ||
| 142 | .join(format!("{}.json", krate.name.to_string().replace("-", "_"))); | ||
| 143 | |||
| 144 | std::fs::remove_file(¤t_path).ok(); | ||
| 145 | let features = config.features.clone(); | ||
| 146 | |||
| 147 | log::info!("Building doc json for {} with features: {:?}", krate.name, features); | ||
| 148 | |||
| 149 | let envs = vec![( | ||
| 150 | "RUSTDOCFLAGS", | ||
| 151 | "--cfg docsrs --cfg not_really_docsrs --cfg semver_checks", | ||
| 152 | )]; | ||
| 153 | |||
| 154 | // always use `specific nightly` toolchain so we don't have to deal with potentially | ||
| 155 | // different versions of the doc-json | ||
| 156 | let cargo_builder = CargoArgsBuilder::default() | ||
| 157 | .toolchain("nightly-2025-06-29") | ||
| 158 | .subcommand("rustdoc") | ||
| 159 | .features(&features); | ||
| 160 | let cargo_builder = if let Some(target) = &config.target { | ||
| 161 | cargo_builder.target(target.clone()) | ||
| 162 | } else { | ||
| 163 | cargo_builder | ||
| 164 | }; | ||
| 165 | |||
| 166 | let cargo_builder = cargo_builder | ||
| 167 | .arg("-Zunstable-options") | ||
| 168 | .arg("-Zhost-config") | ||
| 169 | .arg("-Ztarget-applies-to-host") | ||
| 170 | .arg("--lib") | ||
| 171 | .arg("--output-format=json") | ||
| 172 | .arg("-Zbuild-std=alloc,core") | ||
| 173 | .arg("--config=host.rustflags=[\"--cfg=instability_disable_unstable_docs\"]"); | ||
| 174 | let cargo_args = cargo_builder.build(); | ||
| 175 | log::debug!("{cargo_args:#?}"); | ||
| 176 | crate::cargo::run_with_env(&cargo_args, &krate.path, envs, false)?; | ||
| 177 | Ok(current_path) | ||
| 178 | } | ||
