How can I fail a build when a project depends on an 'unapproved' artifact?


(rhuberg) #1

We would like to control which artifacts are introduced into our product ‘war’. We maintain a list of all ‘approved’ third-party dependencies in a dependencies.gradle. This file contains a hashmap of allowed artifacts. We just want to ensure that a developer does not add a dependency to an artifact that was NOT approved (maybe contains a license or may cause a conflict).

dependencies.gradle:
ext { libraries = [:] }

libraries.aspects = [
‘org.aspectj:aspectjrt:1.8.4’,
‘org.aspectj:aspectjweaver:1.8.4’,
‘org.aspectj:aspectjtools:1.8.4’
]

libraries.batik = [
‘org.apache.xmlgraphics:batik-transcoder:1.7’
]

sample project:
project(’:myproject’) {
dependencies {
compile libraries.aspects // approved (we like this syntax)
compile ‘org.apache.xmlgraphics:batik-transcoder:1.7’ // approved, but would prefer use of libraries.batik
compile ‘highsoft:highsoft-highchart:3.0.7’ // NOT approved artifact, we want to fail
}
}

In the above example, we want to fail if any unapproved artifact is found (i.e. not defined in the libraries hashmap - found in dependencies.gradle).

In addition, we would like to ‘enforce’ (like a checkstyle) the use of libraries.xxxx.

Looking for ideas.

The following is an attempt, which almost provides the behavior to fail if an unapproved third-party artifact is requested (but not the checkstyle feature). The problem is that the resolutionStrategy is transitive. Anyway NOT to be transitive. I am in the ballpark?

if (project.hasProperty(‘checkDependencies’) && Boolean.valueOf(checkDependencies)) {
configurations.all {
resolutionStrategy {
eachDependency { details ->
if (!details.requested.group.startsWith(rootProject.name)) {
def requestedArtifact = “${details.requested.group}:${details.requested.name}:${details.requested.version}”.toString()
def approvedArtifact = libraries.find {
it.value.contains(requestedArtifact)
}
assert approvedArtifact != null : “The artifact $requestedArtifact is NOT approved”
}
}
}
}
}


(Stefan Oehme) #2

If you only care about what was explicitly specified in the buildscript, then you can use

configurations.all {
  dependencies.all { dependency ->
    //check dependency against whitelist
  }
}

The “checkstyle” feature could only be done by actually parsing and analyzing the buildscripts yourself. At runtime you just see a list and don’t know that this list came from one variable or the other.


(rhuberg) #3

Thanks for the tip.

I needed to test if a dependency.group was not null. It appeared to be of type SelfResolvingDependency. Is there a better way to get only ExternalModuleDependency or otherwise ignore (maybe instanceof)?

if (project.hasProperty(‘checkDependencies’) && Boolean.valueOf(checkDependencies)) {
configurations.all {
dependencies.all { dependency ->
if (dependency.group != null && !dependency.group.startsWith(rootProject.name)) {
def requestedDependency = "${dependency.group}:${dependency.name}:${dependency.version}"
def approvedArtifact = libraries.find {
it.value.find {
if (it instanceof String) {
it.startsWith(requestedDependency)
}
}
}
assert approvedArtifact != null : “The artifact $requestedDependency is NOT approved”
}
}
}
}


(rhuberg) #4

Changed the condition as below…

Replaced…
if (dependency.group != null && !dependency.group.startsWith(rootProject.name)) {
With
if (dependency instanceof ExternalModuleDependency) {


(Stefan Oehme) #5

Yep, that’s the way to go.

BTW, to post code snippets, use three ticks ( ```) around your code block to get proper formatting and highlighting.


(rhuberg) #6

Thanks for both tips.