上次我分享了一些有关如何良好使用库的提示。 现在,我想更深入地研究其中之一: 知道您使用的是什么图书馆 。 上周,我着手为我们的产品创建了这样的嵌入式组件列表。 这是我们的安全开发生命周期 (SDL)的要求。 但是,这不是一个有趣的任务。 作为开发人员,我想编写代码,而不是更新文档! 因此,在詹金斯和Confluence的帮助下,我求助于我的朋友Gradle和Groovy 。
Gradle依赖
我们使用Gradle构建产品,并且Gradle维护了对第三方组件的依赖关系 。
我们的构建定义了嵌入式组件的配置名称列表copyBundleConfigurations
,用于将其复制到分发目录。 从那里,我使用Groovy的collection方法了解外部依赖关系:
def externalDependencies() {
copyBundleConfigurations.collectMany {
configurations[it].allDependencies
}.findAll {
!(it instanceof ProjectDependency) && it.group &&
!it.group.startsWith('com.emc')
}
}
添加所需信息
但是,Gradle依赖项并不包含所有必需的信息。 例如,我们需要分发该库所依据的许可证,以便我们可以请求法务部门使用它的许可。 因此,我添加了一个简单的XML文件来保存其他信息。 使用Groovy的XML支持,可以轻松地将该信息与Gradle维护的依赖项结合起来:
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()
}
然后,我创建了Gradle任务,将信息写入HTML文件。 我们的Jenkins构建会执行此任务,因此我们始终具有最新列表。 我使用Confluence的html-include
宏将HTML文件包含在我们的Wiki中。 现在,我们的Wiki始终是最新的。
自动查找丢失的信息
下一个问题是用其他信息填充XML文件。 如果我们从一开始就拥有此文件,那么手动添加该信息就没什么大不了的了。 在我们的案例中,我们已经有一百多个依赖项,因此自动化是有条理的。 首先,我确定了缺少必需信息的组件:
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()
}
接下来,我想自动查找缺少的信息并将其添加到XML文件(使用Groovy的MarkupBuilder
)。 如果找不到所需的信息,构建将失败:
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')
}
}
}
现在,将来添加依赖项的任何人都必须添加所需的信息。 因此,剩下collectInfo()
就是collectInfo()
方法。 我用来查找所需信息的主要来源有两个: SpringSource Enterprise Bundle Repository保存公共库的OSGi bundle版本,而Maven Central保存常规jar。
从这些源中提取信息是下载和解析XML和HTML文件的问题。 使用Groovy的String.toURL()
和URL.eachLine()
方法以及对正则表达式的支持,这非常容易。
结论
所有这些花费了我几天的时间,但是我觉得这笔投资是值得的,因为我不再需要担心二手图书馆的列表已经过时。
参考: Secure Software Development博客上我们JCG合作伙伴 Remon Sinnema的《懒惰的开发者获取最新图书馆列表的方法》 。
翻译自: https://www.javacodegeeks.com/2013/01/the-lazy-developers-way-to-an-up-to-date-libraries-list.html