diff options
| author | Ulf Lilleengen <[email protected]> | 2025-08-18 14:21:02 +0200 |
|---|---|---|
| committer | Ulf Lilleengen <[email protected]> | 2025-08-25 19:44:51 +0200 |
| commit | 1c2f87b4fef0377afb47a09b0c51a08cb73d349e (patch) | |
| tree | 23a41e2c087a0812a02a062fc8dcc602200e8bba | |
| parent | 650319f0af0e0edf31155fcc27ada662e5898f46 (diff) | |
fix: build rustdoc for baseline as well
| -rw-r--r-- | release/Cargo.toml | 8 | ||||
| -rw-r--r-- | release/src/semver_check.rs | 93 |
2 files changed, 86 insertions, 15 deletions
diff --git a/release/Cargo.toml b/release/Cargo.toml index 7875d088a..9701a76e5 100644 --- a/release/Cargo.toml +++ b/release/Cargo.toml | |||
| @@ -18,10 +18,10 @@ log = "0.4" | |||
| 18 | simple_logger = "5.0.0" | 18 | simple_logger = "5.0.0" |
| 19 | temp-file = "0.1.9" | 19 | temp-file = "0.1.9" |
| 20 | flate2 = "1.1.1" | 20 | flate2 = "1.1.1" |
| 21 | 21 | crates-index = "3.11.0" | |
| 22 | [patch.crates-io] | 22 | tar = "0.4" |
| 23 | cargo-semver-checks = { git = "https://github.com/lulf/cargo-semver-checks.git", rev="385f274edcbb6bf5156e30a94315852b27a527e6" } | 23 | reqwest = { version = "0.12", features = ["blocking"] } |
| 24 | #cargo-semver-checks = { path = "../../cargo-semver-checks" } | 24 | cargo-manifest = "0.19.1" |
| 25 | 25 | ||
| 26 | [package.metadata.embassy] | 26 | [package.metadata.embassy] |
| 27 | skip = true | 27 | skip = true |
diff --git a/release/src/semver_check.rs b/release/src/semver_check.rs index a4a9e77b5..4cfa26ec0 100644 --- a/release/src/semver_check.rs +++ b/release/src/semver_check.rs | |||
| @@ -1,6 +1,11 @@ | |||
| 1 | use std::collections::HashSet; | ||
| 1 | use std::path::PathBuf; | 2 | use std::path::PathBuf; |
| 3 | use std::{env, fs}; | ||
| 2 | 4 | ||
| 3 | use cargo_semver_checks::{Check, GlobalConfig, LintConfig, LintLevel, ReleaseType, RequiredSemverUpdate, Rustdoc}; | 5 | use anyhow::anyhow; |
| 6 | use cargo_semver_checks::{Check, GlobalConfig, ReleaseType, RequiredSemverUpdate, Rustdoc}; | ||
| 7 | use flate2::read::GzDecoder; | ||
| 8 | use tar::Archive; | ||
| 4 | 9 | ||
| 5 | use crate::cargo::CargoArgsBuilder; | 10 | use crate::cargo::CargoArgsBuilder; |
| 6 | use crate::types::{BuildConfig, Crate}; | 11 | use crate::types::{BuildConfig, Crate}; |
| @@ -11,12 +16,18 @@ pub fn minimum_update(krate: &Crate) -> Result<ReleaseType, anyhow::Error> { | |||
| 11 | let config = krate.configs.first().unwrap(); // TODO | 16 | let config = krate.configs.first().unwrap(); // TODO |
| 12 | 17 | ||
| 13 | let package_name = krate.name.clone(); | 18 | let package_name = krate.name.clone(); |
| 14 | let current_path = build_doc_json(krate, config)?; | 19 | let baseline_path = download_baseline(&package_name, &krate.version)?; |
| 20 | let mut baseline_krate = krate.clone(); | ||
| 21 | baseline_krate.path = baseline_path; | ||
| 15 | 22 | ||
| 16 | // TODO: Prevent compiler panic on current compiler version | 23 | // Compare features as it's not covered by semver-checks |
| 17 | std::env::set_var("RUSTFLAGS", "--cap-lints=warn"); | 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)?; | ||
| 18 | 29 | ||
| 19 | let baseline = Rustdoc::from_registry_latest_crate_version(); | 30 | let baseline = Rustdoc::from_path(&baseline_path); |
| 20 | let doc = Rustdoc::from_path(¤t_path); | 31 | let doc = Rustdoc::from_path(¤t_path); |
| 21 | let mut semver_check = Check::new(doc); | 32 | let mut semver_check = Check::new(doc); |
| 22 | semver_check.with_default_features(); | 33 | semver_check.with_default_features(); |
| @@ -29,12 +40,8 @@ pub fn minimum_update(krate: &Crate) -> Result<ReleaseType, anyhow::Error> { | |||
| 29 | semver_check.set_build_target(target.clone()); | 40 | semver_check.set_build_target(target.clone()); |
| 30 | } | 41 | } |
| 31 | let mut cfg = GlobalConfig::new(); | 42 | let mut cfg = GlobalConfig::new(); |
| 32 | cfg.set_log_level(Some(log::Level::Trace)); | 43 | cfg.set_log_level(Some(log::Level::Info)); |
| 33 | 44 | ||
| 34 | let mut lint_cfg = LintConfig::new(); | ||
| 35 | // Disable this lint because we provide the rustdoc json only, so it can't do feature comparison. | ||
| 36 | lint_cfg.set("feature_missing", LintLevel::Allow, RequiredSemverUpdate::Minor, 0); | ||
| 37 | cfg.set_lint_config(lint_cfg); | ||
| 38 | let result = semver_check.check_release(&mut cfg)?; | 45 | let result = semver_check.check_release(&mut cfg)?; |
| 39 | 46 | ||
| 40 | let mut min_required_update = ReleaseType::Patch; | 47 | let mut min_required_update = ReleaseType::Patch; |
| @@ -51,7 +58,71 @@ pub fn minimum_update(krate: &Crate) -> Result<ReleaseType, anyhow::Error> { | |||
| 51 | Ok(min_required_update) | 58 | Ok(min_required_update) |
| 52 | } | 59 | } |
| 53 | 60 | ||
| 54 | pub(crate) fn build_doc_json(krate: &Crate, config: &BuildConfig) -> Result<PathBuf, anyhow::Error> { | 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> { | ||
| 55 | let target_dir = std::env::var("CARGO_TARGET_DIR"); | 126 | let target_dir = std::env::var("CARGO_TARGET_DIR"); |
| 56 | 127 | ||
| 57 | let target_path = if let Ok(target) = target_dir { | 128 | let target_path = if let Ok(target) = target_dir { |
