Skip to content

Commit

Permalink
get basic update review report
Browse files Browse the repository at this point in the history
  • Loading branch information
nasifimtiazohi committed Jul 21, 2021
1 parent 79d5bf0 commit 7b12689
Showing 1 changed file with 245 additions and 4 deletions.
249 changes: 245 additions & 4 deletions depdive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//! the types for holding data has `tabled` trait
//! for pretty display
use anyhow::Result;
use anyhow::{anyhow, Result};
use guppy::graph::{PackageGraph, PackageMetadata};
use tabled::Tabled;

Expand All @@ -15,6 +15,8 @@ mod github;
mod super_toml;
mod update;

use update::{UpdateReviewReport, VersionConflict};

// Usage metrics pulled from crates.io API
#[derive(Tabled, Clone)]
pub struct TabledCratesioReport {
Expand Down Expand Up @@ -281,9 +283,248 @@ impl DependencyGraphAnalyzer {
pub struct UpdateAnalyzer;

impl UpdateAnalyzer {
pub fn update_review(prior_graph: &PackageGraph, post_graph: &PackageGraph) -> Result<()> {
pub fn run_update_analyzer(
prior_graph: &PackageGraph,
post_graph: &PackageGraph,
) -> Result<UpdateReviewReport> {
let update_analyzer = update::UpdateAnalyzer::new();
update_analyzer.analyze_updates(&prior_graph, &post_graph)?;
Ok(())
update_analyzer.analyze_updates(&prior_graph, &post_graph)
}

pub fn get_summary_report(
prior_graph: &PackageGraph,
post_graph: &PackageGraph,
) -> Result<String> {
let update_review_report = Self::run_update_analyzer(&prior_graph, &post_graph)?;
let mut summary = String::new();

// Write down info on updated dependencies
summary.push_str("\nUpdated dependencies:");
for report in &update_review_report.dep_update_review_reports {
summary
.push_str("\n-------------------------------------------------------------------");

// Version update info with crates.io downloads
summary.push_str(&format!(
"\n\n{} upgraded to {} [{} downloads] from {} [{} downloads]",
report.name,
report.updated_version.version,
report.updated_version.downloads,
report.prior_version.version,
report.prior_version.downloads
));

// Advisory
if !report.updated_version.known_advisories.is_empty() {
let ids: Vec<String> = report
.updated_version
.known_advisories
.iter()
.map(|a| a.id.clone())
.collect();
summary.push_str(&format!(
"\n\nWarning: The updated version contains known advisories: {}.",
ids.join(", ")
));
}
if !report.prior_version.known_advisories.is_empty() {
let ids: Vec<String> = report
.prior_version
.known_advisories
.iter()
.filter(|a| !report.updated_version.known_advisories.contains(a))
.map(|a| a.id.clone())
.collect();
summary.push_str(&format!(
"\n\nGood job! The old version contained advisories: {},that are not reported with the updated version.",
ids.join(", ")
));
}

// Diff summary
match &report.diff_stats {
None => summary.push_str(
"\n\nDepdive failed to get the diff between versions from crates.io :(",
),
Some(stats) => {
// Diff overview
summary.push_str(&format!(
"\n\nVersion diff summary:\ntotal files changed: {},\ntotal rust files changed: {},\ntotal loc change: {}",
stats.files_changed,
stats.rust_files_changed,
stats.insertions + stats.deletions,
));

// BUild script modification
summary.push_str(&format!(
"\n\nHas the build script changed?: {}",
!stats.modified_build_scripts.is_empty()
));
if !stats.modified_build_scripts.is_empty() {
let paths: Vec<String> =
stats.modified_build_scripts.iter().cloned().collect();
summary.push_str(&format!(
"\nModified build scipts paths are: {}.",
paths.join(", ")
));
}

// Change in unsafe code
summary.push_str(&format!(
"\n\nFiles changed that contains unsafe code: {}",
stats.unsafe_file_changed.len()
));
if !stats.unsafe_file_changed.is_empty() {
let paths: Vec<String> = stats
.unsafe_file_changed
.iter()
.map(|stats| stats.file.clone())
.collect();
summary.push_str(&format!(
"\nFilepaths with unsafe code: {}.",
paths.join(", ")
));
}
}
}

match report.updated_version.crate_source_diff_report.is_different {
None => summary.push_str(
"\n\nDepdive failed to analyze the git source for the updated version to compare with crates.io hosted code",
),
Some(f) => {
summary.push_str(&format!(
"\n\nDoes crates.io code and git source differ?: {}",
f
));
if f {
let changed_files = report
.updated_version
.crate_source_diff_report
.file_diff_stats
.as_ref()
.ok_or_else(|| {
anyhow!("Cannot locate file paths in git source diff report")
})?;
// Only added and modified files are of concern
let paths: Vec<String> = changed_files
.files_added
.union(&changed_files.files_modified)
.cloned()
.collect();
summary.push_str(&format!(
"\nMay wanna take a look at this files: {}.",
paths.join(", ")
));
}
}
}
}

if !update_review_report.version_conflicts.is_empty() {
summary.push_str("\n\nPossible dependency conflicts:");
for conflict in &update_review_report.version_conflicts {
match conflict {
VersionConflict::DirectTransitiveVersionConflict {
name,
direct_dep_version,
transitive_dep_version,
} => summary.push_str(&format!(
"\n{} has version {} as a transitive dep but version {} as a direct dep",
name, transitive_dep_version, direct_dep_version
)),
}
}
}
summary.push('\n');
Ok(summary)
}
}

#[cfg(test)]
mod test {
use super::*;
use git2::{build::CheckoutBuilder, Oid};
use guppy::MetadataCommand;

#[test]
fn test_update_review_report_for_diem() {
let da = diff::DiffAnalyzer::new().unwrap();
let repo = da
.get_git_repo("diem", "https://github.com/diem/diem.git")
.unwrap();
let path = repo.path().parent().unwrap();
let mut checkout_builder = CheckoutBuilder::new();
checkout_builder.force();

// Get prior_graph
repo.checkout_tree(
&repo
.find_object(
Oid::from_str("20da44ad0918e6f260e9f150a60f28ec3b8665b2").unwrap(),
None,
)
.unwrap(),
Some(&mut checkout_builder),
)
.unwrap();
let prior_graph = MetadataCommand::new()
.current_dir(path)
.build_graph()
.unwrap();

// Get post_graph
repo.checkout_tree(
&repo
.find_object(
Oid::from_str("2b2e529d96b6fbd9b5d111ecdd21acb61e95a28f").unwrap(),
None,
)
.unwrap(),
Some(&mut checkout_builder),
)
.unwrap();
let post_graph = MetadataCommand::new()
.current_dir(path)
.build_graph()
.unwrap();

let summary = UpdateAnalyzer::get_summary_report(&prior_graph, &post_graph).unwrap();
print!("{}", summary);

// Get prior_graph
repo.checkout_tree(
&repo
.find_object(
Oid::from_str("44ca183e6c9e758be95e90390a1c752c8b3c82fa").unwrap(),
None,
)
.unwrap(),
Some(&mut checkout_builder),
)
.unwrap();
let prior_graph = MetadataCommand::new()
.current_dir(path)
.build_graph()
.unwrap();

// Get post_graph
repo.checkout_tree(
&repo
.find_object(
Oid::from_str("118dc8d0511a09d76aa14801a9a17024541215e6").unwrap(),
None,
)
.unwrap(),
Some(&mut checkout_builder),
)
.unwrap();
let post_graph = MetadataCommand::new()
.current_dir(path)
.build_graph()
.unwrap();

let summary = UpdateAnalyzer::get_summary_report(&prior_graph, &post_graph).unwrap();
print!("{}", summary);
}
}

0 comments on commit 7b12689

Please sign in to comment.