![]() |
Last week I set out to create such a list of embedded components for our product. This is a requirement for our Security Development Lifecycle (SDL). However, it’s not a fun task. As a developer, I want to write code, not update documents! So I turned to my friends Gradle and Groovy, with a little help from Jenkins and Confluence. Gradle DependenciesWe use Gradle to build our product, and Gradle maintains the dependencies we have on third-party components. Our build defines a list of names of configurations for embedded components, def externalDependencies() { copyBundleConfigurations.collectMany { configurations[it].allDependencies }.findAll { !(it instanceof ProjectDependency) && it.group && !it.group.startsWith('com.emc') } } Adding Required InformationHowever, Gradle dependencies don’t contain all the required information. For instance, we need the license under which the library is distributed, so that we can ask the Legal department permission for using it. So I added a simple XML file to hold the additional info. Combining that information with the dependencies that Gradle maintains is easy using Groovy’s XML support: ext.embeddedComponentsInfo = 'embeddedComponents.xml' def externalDependencyInfos() { def result = new TreeMap() def componentInfo = new XmlSlurper() .parse(embeddedComponentsInfo) externalDependencies().each { dependency -> def info = componentInfo.component.find { it.id == "$dependency.group:$dependency.name" && it.friendlyName?.text() } if (!info.isEmpty()) { def component = [ 'id': info.id, 'friendlyName': info.friendlyName.text(), 'version': dependency.version, 'latestVersion': info.latestVersion.text(), 'license': info.license.text(), 'licenseUrl': info.licenseUrl.text(), 'comment': info.comment.text() ] result.put component.friendlyName, component } } result.values() } I then created a Gradle task to write the information to an HTML file. Our Jenkins build executes this task, so that we always have an up-to-date list. I used Confluence’s Now our Wiki is always up-to-date. Automatically Looking Up Missing InformationThe next problem was to populate the XML file with additional information. Had we had this file from the start, adding that information manually would not have been a big deal. In our case, we already had over a hundred dependencies, so automation was in order. First I identified the components that miss the required information: def missingExternalDependencies() { def componentInfo = new XmlSlurper() .parse(embeddedComponentsInfo) externalDependencies().findAll { dependency -> componentInfo.component.find { it.id == "$dependency.group:$dependency.name" && it.friendlyName?.text() }.isEmpty() }.collect { "$it.group:$it.name" }.sort() } Next, I wanted to automatically look up the missing information and add it to the XML file (using Groovy’s project.afterEvaluate { def missingComponents = missingExternalDependencies() if (!missingComponents.isEmpty()) { def manualComponents = [] def writer = new StringWriter() def xml = new MarkupBuilder(writer) xml.expandEmptyElements = true println 'Looking up information on new dependencies:' xml.components { externalDependencyInfos().each { existingComponent -> component { id(existingComponent.id) friendlyName(existingComponent.friendlyName) latestVersion(existingComponent.latestVersion) license(existingComponent.license) licenseUrl(existingComponent.licenseUrl) approved(existingComponent.approved) comment(existingComponent.comment) } } missingComponents.each { missingComponent -> def lookedUpComponent = collectInfo(missingComponent) component { id(missingComponent) friendlyName(lookedUpComponent.friendlyName) latestVersion(lookedUpComponent.latestVersion) license(lookedUpComponent.license) licenseUrl(lookedUpComponent.licenseUrl) approved('?') comment(lookedUpComponent.comment) } if (!lookedUpComponent.friendlyName || !lookedUpComponent.latestVersion || !lookedUpComponent.license) { manualComponents.add lookedUpComponent.id println ' => Please enter information manually' } } } writer.close() def embeddedComponentsFile = project.file(embeddedComponentsInfo) embeddedComponentsFile.text = writer.toString() if (!manualComponents.isEmpty()) { throw new GradleException('Missing library information') } } } Anyone who adds a dependency in the future is now forced to add the required information. So all that is left to implement is the There are two primary sources that I used to look up the required information: the SpringSource Enterprise Bundle Repository holds OSGi bundle versions of common libraries, while Maven Central holds regular jars. Extracting information from those sources is a matter of downloading and parsing XML and HTML files. This is easy enough with Groovy’s ConclusionAll of this took me a couple of days to build, but I feel that the investment is well worth it, since I no longer have to worry about the list of used libraries being out of date. How do you maintain a list of used libraries? Please let me know in the comments. Filed under: Application Security, Software Development Tagged: Confluence, Gradle, Groovy, HTML, Jenkins, Maven, OSGi, regular expression, SDL, third-party libraries, Wiki, XML ![]() |
Update your feed preferences |
