Skip to content

Commit

Permalink
Implement a solution for additional license exceptions.
Browse files Browse the repository at this point in the history
Implement a solution for additional license exceptions configuration
as described in EmbarkStudios#541
  • Loading branch information
dsully committed Aug 4, 2023
1 parent 16d0db8 commit 55ed9d8
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 3 deletions.
15 changes: 15 additions & 0 deletions docs/src/checks/licenses/cfg.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,21 @@ The name of the crate that you are adding an exception for

An optional version constraint specifying the range of crate versions you are excepting. Defaults to any version.

### Additional exceptions configuration file

In some cases it's useful to have global cargo-deny config and project-local exceptions. This can be accomplished with a project exceptions file in any of these locations relative to your top level `Cargo.toml` manifest file.

`cargo-deny` will look for the following files: `<cwd>/deny.exceptions.toml`, `<cwd>/.deny.exceptions.toml` and `<cwd>/.cargo/deny.exceptions.toml`

Only the exceptions field should be set:

```ini
exceptions = [
# Each entry is the crate and version constraint, and its specific allow list.
{ allow = ["CDDL-1.0"], name = "inferno", version = "*" },
]
```

#### The `allow` field

This is the exact same as the general `allow` field.
Expand Down
43 changes: 40 additions & 3 deletions src/cargo-deny/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ struct ValidConfig {
impl ValidConfig {
fn load(
cfg_path: Option<PathBuf>,
exceptions_cfg_path: Option<PathBuf>,
files: &mut Files,
log_ctx: crate::common::LogContext,
) -> Result<Self, Error> {
Expand All @@ -170,9 +171,44 @@ impl ValidConfig {
}
};

let cfg: Config = toml::from_str(&cfg_contents)
let mut cfg: Config = toml::from_str(&cfg_contents)
.with_context(|| format!("failed to deserialize config from '{cfg_path}'"))?;

// Allow for project-local exceptions. Relevant in corporate environments.
// https://github.com/EmbarkStudios/cargo-deny/issues/541
//
// This isn't the cleanest, but cfg.licenses isn't mutable, so appending/extending the Vec
// isn't possible. Similarly, the various Config/ValidConfig structs don't derive from
// Copy/Clone, so cloning and updating isn't possible either.
//
// This is the most minimally invasive approach I could come up with.
if let Some(exceptions_cfg_path) = exceptions_cfg_path {
// TOML can't have unnamed arrays at the root.
#[derive(Deserialize)]
pub struct ExceptionsConfig {
pub exceptions: Vec<licenses::cfg::Exception>,
}

let content = std::fs::read_to_string(&exceptions_cfg_path)
.with_context(|| format!("failed to read config from {exceptions_cfg_path}"))?;

let ex_cfg: ExceptionsConfig = toml::from_str(&content).with_context(|| {
format!("failed to deserialize config from '{exceptions_cfg_path}'")
})?;

if cfg.licenses.is_some() {
let l = cfg.licenses.unwrap_or_default();

let exceptions = l
.exceptions
.into_iter()
.chain(ex_cfg.exceptions.into_iter())
.collect();

cfg.licenses = Some(licenses::Config { exceptions, ..l });
}
};

log::info!("using config from {cfg_path}");

let id = files.add(&cfg_path, cfg_contents);
Expand Down Expand Up @@ -259,6 +295,7 @@ pub(crate) fn cmd(
features,
} = ValidConfig::load(
krate_ctx.get_config_path(args.config.clone()),
krate_ctx.get_local_exceptions_path(),
&mut files,
log_ctx,
)?;
Expand Down Expand Up @@ -321,7 +358,7 @@ pub(crate) fn cmd(
match cl {
CodeOrLevel::Code(code) => {
if let Some(current) = code_overrides.get(code.as_str()) {
anyhow::bail!("unable to override code '{code}' to '{severity:?}', it has already been overriden to '{current:?}'");
anyhow::bail!("unable to override code '{code}' to '{severity:?}', it has already been overridden to '{current:?}'");
}

code_overrides.insert(code.as_str(), severity);
Expand All @@ -337,7 +374,7 @@ pub(crate) fn cmd(
}
})
{
anyhow::bail!("unable to override level '{level:?}' to '{severity:?}', it has already been overriden to '{current:?}'");
anyhow::bail!("unable to override level '{level:?}' to '{severity:?}', it has already been overridden to '{current:?}'");
}

level_overrides.push((ls, severity));
Expand Down
31 changes: 31 additions & 0 deletions src/cargo-deny/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,37 @@ impl KrateContext {
}
}

// Allow for project-local exceptions. Relevant in corporate environments.
// https://github.com/EmbarkStudios/cargo-deny/issues/541
pub fn get_local_exceptions_path(&self) -> Option<PathBuf> {
let mut p = self.manifest_path.parent();

while let Some(parent) = p {
let mut config_path = parent.join("deny.exceptions.toml");

if config_path.exists() {
return Some(config_path);
}

config_path.pop();
config_path.push(".deny.exceptions.toml");

if config_path.exists() {
return Some(config_path);
}

config_path.pop();
config_path.push(".cargo/deny.exceptions.toml");
if config_path.exists() {
return Some(config_path);
}

p = parent.parent();
}

None
}

#[inline]
pub fn fetch_krates(&self) -> anyhow::Result<()> {
fetch(MetadataOptions {
Expand Down

0 comments on commit 55ed9d8

Please sign in to comment.