Skip to content

Commit 2460124

Browse files
authored
Refactor a bit (#23)
1 parent 33bf78a commit 2460124

File tree

18 files changed

+1215
-1434
lines changed

18 files changed

+1215
-1434
lines changed

Cargo.lock

+272-507
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,4 @@ log = "0.4.22"
4040
assert_cmd = "2.0.14"
4141
predicates = "3.1.0"
4242
serial_test = "3.2.0"
43+
rstest = "0.23.0"

src/check.rs

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
use crate::{fetch_license_infos, license_info::LicenseInfo, CheckOutput, CondaDenyCheckConfig};
2+
use anyhow::{Context, Result};
3+
use colored::Colorize;
4+
use log::debug;
5+
use std::io::Write;
6+
7+
fn check_license_infos(config: &CondaDenyCheckConfig) -> Result<CheckOutput> {
8+
let license_infos = fetch_license_infos(config.lockfile_or_prefix.clone())
9+
.with_context(|| "Fetching license information failed.")?;
10+
11+
if config.osi {
12+
debug!("Checking licenses for OSI compliance");
13+
Ok(license_infos.osi_check())
14+
} else {
15+
debug!("Checking licenses against specified whitelist");
16+
license_infos.check(config)
17+
}
18+
}
19+
20+
pub fn check<W: Write>(check_config: CondaDenyCheckConfig, mut out: W) -> Result<()> {
21+
let (safe_dependencies, unsafe_dependencies) = check_license_infos(&check_config)?;
22+
23+
writeln!(
24+
out,
25+
"{}",
26+
format_check_output(
27+
safe_dependencies,
28+
unsafe_dependencies.clone(),
29+
)
30+
)?;
31+
32+
if !unsafe_dependencies.is_empty() {
33+
Err(anyhow::anyhow!("Unsafe licenses found"))
34+
} else {
35+
Ok(())
36+
}
37+
}
38+
39+
pub fn format_check_output(
40+
safe_dependencies: Vec<LicenseInfo>,
41+
unsafe_dependencies: Vec<LicenseInfo>,
42+
) -> String {
43+
let mut output = String::new();
44+
45+
if !unsafe_dependencies.is_empty() {
46+
output.push_str(
47+
format!(
48+
"\n❌ {}:\n\n",
49+
"The following dependencies are unsafe".red()
50+
)
51+
.as_str(),
52+
);
53+
for license_info in &unsafe_dependencies {
54+
output.push_str(&license_info.pretty_print())
55+
}
56+
}
57+
58+
if unsafe_dependencies.is_empty() {
59+
output.push_str(&format!(
60+
"\n{}",
61+
"✅ No unsafe licenses found! ✅".to_string().green()
62+
));
63+
} else {
64+
output.push_str(&format!(
65+
"\n{}",
66+
"❌ Unsafe licenses found! ❌".to_string().red()
67+
));
68+
}
69+
70+
output.push_str(&format!(
71+
"\nThere were {} safe licenses and {} unsafe licenses.\n",
72+
safe_dependencies.len().to_string().green(),
73+
unsafe_dependencies.len().to_string().red()
74+
));
75+
76+
output.push('\n');
77+
78+
output
79+
}

src/cli.rs

+89-50
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
use std::path::PathBuf;
2+
13
use clap::ArgAction;
24
use clap_verbosity_flag::{ErrorLevel, Verbosity};
35

46
use clap::Parser;
7+
use rattler_conda_types::Platform;
58

69
#[derive(Parser, Debug)]
710
#[command(name = "conda-deny", about = "Check and list licenses of pixi and conda environments", version = env!("CARGO_PKG_VERSION"))]
@@ -10,94 +13,130 @@ pub struct Cli {
1013
pub verbose: Verbosity<ErrorLevel>,
1114

1215
#[command(subcommand)]
13-
pub command: Commands,
16+
pub command: CondaDenyCliConfig,
1417

18+
/// Path to the conda-deny config file
1519
#[arg(short, long, global = true)]
16-
pub config: Option<String>,
17-
18-
#[arg(long, global = true)]
19-
pub prefix: Option<Vec<String>>,
20-
21-
#[arg(short, long)]
22-
pub lockfile: Option<Vec<String>>,
23-
24-
#[arg(short, long)]
25-
pub platform: Option<Vec<String>>,
26-
27-
#[arg(short, long)]
28-
pub environment: Option<Vec<String>>,
20+
pub config: Option<PathBuf>,
2921
}
3022

3123
#[derive(clap::Subcommand, Debug)]
32-
pub enum Commands {
24+
pub enum CondaDenyCliConfig {
3325
Check {
34-
#[arg(short, long, action = ArgAction::SetTrue)]
35-
include_safe: bool,
26+
/// Path to the pixi lockfile(s)
27+
#[arg(short, long)]
28+
lockfile: Option<Vec<PathBuf>>,
3629

30+
/// Path to the conda prefix(es)
31+
#[arg(long, global = true)]
32+
prefix: Option<Vec<PathBuf>>,
33+
34+
/// Platform(s) to check
35+
#[arg(short, long)]
36+
platform: Option<Vec<Platform>>,
37+
38+
/// Pixi environment(s) to check
39+
#[arg(short, long)]
40+
environment: Option<Vec<String>>,
41+
42+
/// Check against OSI licenses instead of custom license whitelists.
3743
#[arg(short, long, action = ArgAction::SetTrue)]
38-
osi: bool,
44+
osi: Option<bool>,
3945

4046
/// Ignore when encountering pypi packages instead of failing.
4147
#[arg(long, action = ArgAction::SetTrue)]
42-
ignore_pypi: bool,
48+
ignore_pypi: Option<bool>,
49+
},
50+
List {
51+
/// Path to the pixi lockfile(s)
52+
#[arg(short, long)]
53+
lockfile: Option<Vec<PathBuf>>,
54+
55+
/// Path to the conda prefix(es)
56+
#[arg(long, global = true)]
57+
prefix: Option<Vec<PathBuf>>,
58+
59+
/// Platform(s) to list
60+
#[arg(short, long)]
61+
platform: Option<Vec<Platform>>,
62+
63+
/// Pixi environment(s) to list
64+
#[arg(short, long)]
65+
environment: Option<Vec<String>>,
66+
67+
/// Ignore when encountering pypi packages instead of failing.
68+
#[arg(long)]
69+
ignore_pypi: Option<bool>,
4370
},
44-
List {},
4571
}
4672

47-
#[cfg(test)]
48-
mod tests {
49-
use super::*;
73+
impl CondaDenyCliConfig {
74+
pub fn lockfile(&self) -> Option<Vec<PathBuf>> {
75+
match self {
76+
CondaDenyCliConfig::Check { lockfile, .. } => lockfile.clone(),
77+
CondaDenyCliConfig::List { lockfile, .. } => lockfile.clone(),
78+
}
79+
}
5080

51-
#[test]
52-
fn test_cli_check_include_safe() {
53-
let cli = Cli::try_parse_from(vec!["conda-deny", "check", "--include-safe"]).unwrap();
54-
match cli.command {
55-
Commands::Check { include_safe, .. } => {
56-
assert!(include_safe);
57-
}
58-
_ => panic!("Expected check subcommand with --include-safe"),
81+
pub fn prefix(&self) -> Option<Vec<PathBuf>> {
82+
match self {
83+
CondaDenyCliConfig::Check { prefix, .. } => prefix.clone(),
84+
CondaDenyCliConfig::List { prefix, .. } => prefix.clone(),
5985
}
6086
}
6187

88+
pub fn platform(&self) -> Option<Vec<Platform>> {
89+
match self {
90+
CondaDenyCliConfig::Check { platform, .. } => platform.clone(),
91+
CondaDenyCliConfig::List { platform, .. } => platform.clone(),
92+
}
93+
}
94+
95+
pub fn environment(&self) -> Option<Vec<String>> {
96+
match self {
97+
CondaDenyCliConfig::Check { environment, .. } => environment.clone(),
98+
CondaDenyCliConfig::List { environment, .. } => environment.clone(),
99+
}
100+
}
101+
102+
pub fn ignore_pypi(&self) -> Option<bool> {
103+
match self {
104+
CondaDenyCliConfig::Check { ignore_pypi, .. } => *ignore_pypi,
105+
CondaDenyCliConfig::List { ignore_pypi, .. } => *ignore_pypi,
106+
}
107+
}
108+
}
109+
110+
#[cfg(test)]
111+
mod tests {
112+
use super::*;
113+
62114
#[test]
63115
fn test_cli_with_config() {
64116
let cli =
65117
Cli::try_parse_from(vec!["conda-deny", "list", "--config", "custom.toml"]).unwrap();
66-
assert_eq!(cli.config.as_deref(), Some("custom.toml"));
118+
assert_eq!(cli.config, Some("custom.toml".into()));
67119
}
68120

69121
#[test]
70122
fn test_cli_with_config_new_order() {
71123
let cli =
72124
Cli::try_parse_from(vec!["conda-deny", "check", "--config", "custom.toml"]).unwrap();
73-
assert_eq!(cli.config.as_deref(), Some("custom.toml"));
125+
assert_eq!(cli.config, Some("custom.toml".into()));
74126
match cli.command {
75-
Commands::Check { include_safe, .. } => {
76-
assert!(!include_safe);
77-
}
127+
CondaDenyCliConfig::Check { .. } => {}
78128
_ => panic!("Expected check subcommand with --config"),
79129
}
80130
}
81131

82132
#[test]
83133
fn test_cli_with_check_arguments() {
84-
let cli = Cli::try_parse_from(vec!["conda-deny", "check", "--include-safe"]).unwrap();
85-
match cli.command {
86-
Commands::Check { include_safe, .. } => {
87-
assert!(include_safe);
88-
}
89-
_ => panic!("Expected check subcommand with --include-safe"),
90-
}
91-
}
92-
93-
#[test]
94-
fn test_cli_with_check_osi() {
95134
let cli = Cli::try_parse_from(vec!["conda-deny", "check", "--osi"]).unwrap();
96135
match cli.command {
97-
Commands::Check { osi, .. } => {
98-
assert!(osi);
136+
CondaDenyCliConfig::Check { osi, .. } => {
137+
assert_eq!(osi, Some(true));
99138
}
100-
_ => panic!("Expected check subcommand with --osi"),
139+
_ => panic!("Expected check subcommand with --include-safe"),
101140
}
102141
}
103142
}

0 commit comments

Comments
 (0)