Skip to content

Commit

Permalink
feat: Support for gitlab license scanning report
Browse files Browse the repository at this point in the history
  • Loading branch information
Kateřina Churanová committed Feb 9, 2023
1 parent 40e4992 commit 33b857a
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 3 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ semver = "1.0"
clap = { version = "3.1.17", features = ["derive"] }
atty = "0.2"
anyhow = "1"
spdx = "0.10.0"
itertools = "0.10.5"
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ OPTIONS:
--filter-platform <TRIPLE> Only include resolve dependencies matching the given
target-triple
-h, --help Print help information
-g, --gitlab Gitlab license scanner output
-j, --json Detailed output as JSON
--manifest-path <PATH> Path to Cargo.toml
--no-default-features Deactivate default features
Expand Down
78 changes: 78 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use anyhow::Result;
use cargo_metadata::{
DepKindInfo, DependencyKind, Metadata, MetadataCommand, Node, NodeDep, Package, PackageId,
};
use itertools::Itertools;
use semver::Version;
use serde_derive::Serialize;
use std::collections::{HashMap, HashSet};
use std::io;
Expand Down Expand Up @@ -78,6 +80,75 @@ impl DependencyDetails {
}
}

#[derive(Debug, Serialize, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]
struct GitlabDependency {
name: String,
version: Version,
package_manager: &'static str,
path: String,
licenses: Vec<&'static str>,
}

#[derive(Debug, Serialize, Clone, Hash, Ord, PartialOrd, Eq, PartialEq)]
struct GitlabLicense {
id: &'static str,
name: &'static str,
url: String,
}

impl GitlabLicense {
fn parse_licenses(dependency: &DependencyDetails) -> Result<HashSet<Self>> {
let Some(license) = &dependency.license else {return Ok(HashSet::new())};
let expression = spdx::Expression::parse_mode(license, spdx::ParseMode::LAX)?;
Ok(expression
.requirements()
.flat_map(|req| {
req.req.license.id().map(|license| Self {
id: license.name,
name: license.full_name,
url: Default::default(),
})
})
.collect())
}
}

#[derive(Debug, Serialize, Clone)]
struct GitlabLicenseScanningReport {
version: &'static str,
licenses: HashSet<GitlabLicense>,
dependencies: Vec<GitlabDependency>,
}

impl TryFrom<&[DependencyDetails]> for GitlabLicenseScanningReport {
type Error = anyhow::Error;
fn try_from(dependencies: &[DependencyDetails]) -> Result<Self> {
let mut licenses = HashSet::new();
let dependencies = dependencies
.iter()
.cloned()
.map(|dependency| {
let dep_licenses = GitlabLicense::parse_licenses(&dependency)?;
let license_ids = dep_licenses.iter().map(|license| license.id).collect();
licenses.extend(dep_licenses);
Ok::<_, Self::Error>(GitlabDependency {
name: dependency.name,
version: dependency.version,
package_manager: "cargo",
path: Default::default(),
licenses: license_ids,
})
})
.try_collect()?;

Ok(GitlabLicenseScanningReport {
version: "2.1",
dependencies,
licenses,
})
}
}

#[derive(Default)]
pub struct GetDependenciesOpt {
pub avoid_dev_deps: bool,
Expand Down Expand Up @@ -171,6 +242,13 @@ pub fn write_json(dependencies: &[DependencyDetails]) -> Result<()> {
Ok(())
}

pub fn write_gitlab(dependencies: &[DependencyDetails]) -> Result<()> {
let dependencies = GitlabLicenseScanningReport::try_from(dependencies)?;
println!("{}", serde_json::to_string_pretty(&dependencies)?);

Ok(())
}

#[cfg(test)]
mod test {
use super::*;
Expand Down
13 changes: 10 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use ansi_term::Colour::Green;
use ansi_term::Style;
use anyhow::Result;
use cargo_license::{
get_dependencies_from_cargo_lock, write_json, write_tsv, DependencyDetails, GetDependenciesOpt,
get_dependencies_from_cargo_lock, write_gitlab, write_json, write_tsv, DependencyDetails,
GetDependenciesOpt,
};
use cargo_metadata::{CargoOpt, MetadataCommand};
use clap::Parser;
Expand Down Expand Up @@ -104,7 +105,7 @@ fn one_license_per_line(
}
}

fn colored<'a, 'b>(s: &'a str, style: &'b Style, enable_color: bool) -> Cow<'a, str> {
fn colored<'a>(s: &'a str, style: &Style, enable_color: bool) -> Cow<'a, str> {
if enable_color {
Cow::Owned(format!("{}", style.paint(s)))
} else {
Expand Down Expand Up @@ -143,6 +144,10 @@ struct Opt {
/// Detailed output as JSON.
json: bool,

#[clap(short, long)]
/// Gitlab license scanner output
gitlab: bool,

#[clap(long)]
/// Exclude development dependencies
avoid_dev_deps: bool,
Expand Down Expand Up @@ -244,6 +249,8 @@ fn run() -> Result<()> {
write_tsv(&dependencies)?;
} else if opt.json {
write_json(&dependencies)?;
} else if opt.gitlab {
write_gitlab(&dependencies)?;
} else if opt.do_not_bundle {
one_license_per_line(dependencies, opt.authors, enable_color);
} else {
Expand All @@ -257,7 +264,7 @@ fn main() {
Ok(_) => 0,
Err(e) => {
for cause in e.chain() {
eprintln!("{}", cause);
eprintln!("{cause}");
}
1
}
Expand Down

0 comments on commit 33b857a

Please sign in to comment.