aboutsummaryrefslogtreecommitdiff
path: root/release/src/semver_check.rs
diff options
context:
space:
mode:
Diffstat (limited to 'release/src/semver_check.rs')
-rw-r--r--release/src/semver_check.rs178
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 @@
1use std::collections::HashSet;
2use std::env;
3use std::path::PathBuf;
4
5use anyhow::anyhow;
6use cargo_semver_checks::{Check, GlobalConfig, ReleaseType, Rustdoc};
7use flate2::read::GzDecoder;
8use tar::Archive;
9
10use crate::cargo::CargoArgsBuilder;
11use 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]
15pub 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(&current_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
61fn 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
70fn 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
99fn 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
125fn 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(&current_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}