发布新的软件版本之前,开发人员和发布经理需要确保软件中包含的所有库(依赖项)已被授权用于商业用途。构建 Node.js 应用程序时,您可能使用数十个外部库,每个库都有不同的许可条款。收集所有这些库的信息可能很痛苦而且很耗时。本文介绍如何自动完成这些任务,并生成项目中所用全部依赖项的列表:列表中包含依赖项所遵守的许可,以及可在哪个网站或存储库中找到它们。
备注:本文选择 IBM Bluemix DevOps Services 作为例子,但是您可将同样的 Grunt 任务轻松集成到 Jenkins 作业中。您也可以修改我们所创建的代码,从而在 Gulp 中运行它,或者在 Grunt 以外的任何其他任务自动化框架中运行它。
相关主题: IBM Bluemix DevOps Services 简介 -- 利用交付管道自动部署到 Bluemix
如果已设置好 Grunt,可跳到下一步。如果项目中还不包含 Grunt,可执行以下步骤设置它。您需要 Grunt 的命令行接口 (CLI) 和 Grunt Node 模块。
相关主题: 在项目中设置 Grunt
sudo
(对于 OSX、Linux®、BSD 等)或以管理员身份(对于 Windows®)运行命令。 要将 grunt
命令放到系统路径中,并允许从任何目录运行它:
npm install -g grunt-cli
npm install grunt --save-dev
创建一个 Gruntfile.js 文件。这应该是一个有效的 JavaScript 文件,与 package.json 文件一起位于项目的根目录中,而且应与项目源代码一同提交。
下面一个 Gruntfile.js 文件示例:
module.exports = function(grunt) { grunt.initConfig({ pkg: grunt.file.readJSON('package.json') }); grunt.registerTask('default'); };
为项目设置好 Grunt 后,创建一个新的 Grunt 任务文件来包含由 Grunt 执行的自定义代码。
module.exports = function (grunt) { 'use strict'; grunt.registerTask('example', 'Example task', function () { }); };
npm install grunt --save-dev module.exports = function(grunt) { grunt.initConfig({ pkg: grunt.file.readJSON('package.json') }); grunt.task.loadTasks('build_tasks'); grunt.registerTask('default', ['example']); };
grunt
。
应该看到以下输出:
Running "example" task Done, without errors.
相关主题: npm 是什么?
grunt-license-finder
Node 模块添加到项目中。从项目的根目录运行以下命令: npm install grunt-license-finder –-save-dev
grunt.loadNpmTasks(‘grunt-license-finder');
license_finder
的节添加到已传入 grunt.initConfig()
中的数据对象中:
grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), license_finder: { } });
新的 developerWorks Premium 会员计划提供了一张访问强大的开发工具和资源的全通票,包括 500 篇通过 Safari Books Online 提供的顶级技术文章(数十篇文章是专门针对 Web 开发人员的)、最重要开发人员活动的大幅折扣,最新的 O'Reilly 大会的视频回放,等等。立即注册。
这个新节是指定各种选项的地方,这些选项用于生成报告。您可为服务器端依赖项创建两个不同的报告:一个用于生产依赖项,一个用于开发依赖项。
生产依赖项由应用程序在服务器运行时部署和使用。开发依赖项是仅在开发和自动化期间使用的库,比如刚刚添加到项目的 grunt
和 grunt-license-finder
。
将一个依赖项添加到 package.json 文件中时,可将它添加到开发依赖项或生产依赖项中。
license_finder
任务添加两个目标,每种类型的依赖项一个目标,以便它生成两个单独的文件并更容易识别生产和开发依赖项。为此,在 license_finder
节中添加两个目标。 grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), license_finder: { dev: { options: { production: false, out: 'npm-dev-licenses.csv', csv: true } }, prod: { options: { production: true, out: 'npm-prod-licenses.csv', csv: true } } } });
您可更改这些选项,使其与您想要的配置匹配。 production
选项指定它生成生产还是开发依赖项。 out
选项定义该报告的输出文件, csv
选项指定为报告生成一个 csv 文件。
license_finder
调用这两个任务:一个用于生产报告,一个用于开发报告。将以下代码行添加到 Gruntifle.js 文件中: grunt.registerTask('server-side-license', ['license_finder:dev', 'license_finder:prod']);
module.exports = function (grunt) { grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), license_finder: { dev: { options: { production: false, out: 'npm-dev-licenses.csv', csv: true } }, prod: { options: { production: true, out: 'npm-prod-licenses.csv', csv: true } } } }); grunt.task.loadTasks('build_tasks'); grunt.loadNpmTasks('grunt-license-finder'); grunt.registerTask('server-side-license', ['license_finder:dev', 'license_finder:prod']); };
server-side-license
任务。
grunt server-side-license
输出应该如下所示:
Running "license_finder:dev" (license_finder) task Retrieved license information License information written to: npm-dev-licenses.csv Running "license_finder:prod" (license_finder) task Retrieved license information License information written to: npm-prod-licenses.csv Done, without errors.
Bower 是一个工具,使用它可以轻松地管理客户端依赖项。无需访问您想要使用的每个项目的站点并下载必要的库文件,您可创建一个 Bower 配置文件,自动完成此流程。
如果环境中没有 Bower,可按照这篇 教程 中的步骤设置它。
为您的项目安装并配置 Bower 后,所有客户端依赖项都存储在一个名为 bower.json 的文件中。确保项目中使用的所有客户端依赖项都是通过 Bower 添加的,而不是直接添加到页面中,这样所生成的报告才是准确无误的。
bower-license
Node 模块添加到项目中。从项目的根目录中运行以下命令: npm install bower-license --save-dev
var license = require('bower-license');
bower-license
的自定义任务。创建一个名为 run_bower_license
的任务来收集所有依赖项并将结果输出到一个文件中。将以下代码添加到 licenses.js 文件中。 点击查看代码清单
关闭 [x]
var license = require('bower-license'), fs = require('fs'); module.exports = function (grunt) { 'use strict'; grunt.registerMultiTask('run_bower_license', 'Gather bower license report', function () { var options = this.options({ directory: 'bower_components', output: 'bower-license.csv' }), entry, item, prop, done, dependency; done = this.async(); console.log('Executing run_bower_license task'); // If output file already exists, will delete it if(grunt.file.exists(options.output)) { console.log('Output file already exists. Will delete it'); grunt.file.delete(options.output); } license.init(options, function (data) { for (entry in data) { item = { licenses: "", repository: "", homepage: "" }; for (prop in data[entry]) { if (prop === 'licenses') { item.licenses = data[entry][prop]; } else if (prop === 'repository') { item.repository = data[entry][prop]; if (item.repository.constructor === Object) { item.repository = item.repository.url; } } else if (prop === 'homepage') { item.homepage = data[entry][prop]; } } item.version = entry.substring(entry.indexOf('@') + 1, entry.length); item.name = entry.substring(0, entry.indexOf('@')); dependency = item.name + ',' + item.version + ',' + item.repository + ',' + item.licenses; if(item.homepage) { dependency += ',' + item.homepage; } fs.appendFileSync(options.output, dependency + '/r/n'); } console.log('End of run_bower_license task'); done(); }); }); };
此代码注册一个名为 run_bower_license
的 Grunt 任务,该任务接受两个参数—一个表示 Bower 组件存储在何处的目录,一个表示所生成的输出文件的名称。它删除输出文件(如果已存在)并调用 bower-license
库,该库从 Bower 读取所有客户端依赖项。
对于返回的每个依赖项, run_bower_license
尝试获得库的名称、版本、git 存储库 URL、许可和主页。对于一个给定的库,可能并非所有这些信息都有,但所拥有的信息都会放在输出文件中。
run_bower_license
任务的新目标任务。将它命名为 client-side-license
。 grunt.registerTask('client-side-license', ['run_bower_license:all']);
client-side-license
任务。
grunt client-side-license
输出应该如下所示:
Running "run_bower_license:all" (run_bower_license) task Executing run_bower_license task Output file already exists. Will delete it End of run_bower_license task Done, without errors.
get-licenses
的任务,该任务同时调用客户端和服务器端版本。 grunt.registerTask('client-side-license', ['run_bower_license:all']);
module.exports = function (grunt) { grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), license_finder: { dev: { options: { production: false, out: 'npm-dev-licenses.csv', csv: true } }, prod: { options: { production: true, out: 'npm-prod-licenses.csv', csv: true } } }, run_bower_license: { all: { options: { directory: 'bower_components', output: 'bower-license.csv' } } } }); grunt.task.loadTasks('build_tasks'); grunt.loadNpmTasks('grunt-license-finder'); grunt.registerTask('server-side-license', ['license_finder:dev', 'license_finder:prod']); grunt.registerTask('client-side-license', ['run_bower_license:all']); grunt.registerTask('get-licenses', ['server-side-license', 'client-side-license']); };
get-licenses
任务,获得客户端和服务器端报告。
grunt get-licenses
输出应该如下所示:
Running "license_finder:dev" (license_finder) task Retrieved license information License information written to: npm-dev-licenses.csv Running "license_finder:prod" (license_finder) task Retrieved license information License information written to: npm-prod-licenses.csv Running "run_bower_license:all" (run_bower_license) task Executing run_bower_license task Output file already exists. Will delete it End of run_bower_license task Done, without errors.
在本节中,您将学习如何自动执行 IBM Bluemix DevOps Services 上的许可任务。
进入项目的 Build & Deploy 视图后,您可看到该项目的管道。为您的项目启用此特性后,创建这个管道包含两个阶段:一个是执行构建工作,另一个将应用程序部署到 Bluemix。
点击查看大图
关闭 [x]
。
这意味着在构建阶段完成后,会复制所有构建工件,以便执行许可任务。
点击查看大图
关闭 [x]
点击查看大图
关闭 [x]
Build
。 点击查看大图
关闭 [x]
#!/bin/bash npm install node_modules/bower/bin/bower install grunt get-licenses
点击查看大图
关闭 [x]
点击查看大图
关闭 [x]
点击查看大图
关闭 [x]
bower-license.csv
、 npm-dev-licenses.csv
和 npm-prod-licenses.csv
)。 bower-license.csv
(客户端依赖项): 点击查看代码清单
关闭 [x]
angular-bootstrap,0.13.0,,MIT d3,undefined,,MIT* jquery,2.1.4,,MIT angular,1.4.1,git+https://github.com/angular/angular.js,MIT angular-animate,1.3.16,git+https://github.com/angular/angular.js,MIT angular-resource,1.3.16,git+https://github.com/angular/angular.js,MIT angular-route,1.3.16,git+https://github.com/angular/angular.js,MIT angular-sanitize,1.4.1,git+https://github.com/angular/angular.js,MIT bootstrap,3.1.1,git+https://github.com/twbs/bootstrap,MIT jquery-ui,1.11.4,https://github.com/jquery/jquery-ui,MIT angular-ui-bootstrap,0.13.0,git+https://github.com/angular-ui/bootstrap,MIT,https://github.com/angular-ui/bootstrap Flot,0.8.3,,UNKNOWN
npm-prod-license.csv
(服务器端依赖项): 点击查看代码清单
关闭 [x]
name,version,directory,repository,summary,from package.json,from license,from readme NodejsStarterApp,0.0.1,/home/fmariani/bluemix/MyTestLicensesApp,(none),MIT,,MIT,MIT accepts,1.2.12,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/express/node_modules/accepts,git+https://github.com/jshttp/accepts,MIT,MIT,MIT, argparse,1.0.2,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/cfenv/node_modules/js-yaml/node_modules/argparse,http://github.com/nodeca/argparse,MIT,MIT,MIT,MIT cfenv,1.0.0,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/cfenv,git+https://github.com/cloudfoundry-community/node-cfenv,Apache;Apache-2.0,Apache-2.0,Apache, content-disposition,0.5.0,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/express/node_modules/content-disposition,git+https://github.com/jshttp/content-disposition,MIT,MIT,MIT, content-type,1.0.1,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/express/node_modules/content-type,git+https://github.com/jshttp/content-type,MIT,MIT,MIT, cookie,0.1.2,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/express/node_modules/cookie,http://github.com/shtylman/node-cookie,MIT,,MIT, cookie-signature,1.0.6,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/express/node_modules/cookie-signature,git+https://github.com/visionmedia/node-cookie-signature,MIT,MIT,,MIT crc,3.2.1,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/express/node_modules/etag/node_modules/crc,http://github.com/alexgorbatchev/node-crc,MIT,MIT,MIT,MIT debug,2.2.0,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/express/node_modules/debug,http://github.com/visionmedia/debug,MIT,MIT,,MIT depd,1.0.1,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/express/node_modules/depd,git+https://github.com/dougwilson/nodejs-depd,MIT,MIT,MIT, destroy,1.0.3,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/express/node_modules/send/node_modules/destroy,git+https://github.com/stream-utils/destroy,MIT,MIT,, ee-first,1.1.0,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/express/node_modules/on-finished/node_modules/ee-first,git+https://github.com/jonathanong/ee-first,MIT,MIT,MIT, escape-html,1.0.1,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/express/node_modules/escape-html,git+https://github.com/component/escape-html,Unknown,,, esprima,2.0.0,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/cfenv/node_modules/js-yaml/node_modules/esprima,git+https://github.com/jquery/esprima,BSD,BSD,,BSD etag,1.6.0,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/express/node_modules/etag,git+https://github.com/jshttp/etag,MIT,MIT,MIT, express,4.12.4,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/express,git+https://github.com/strongloop/express,MIT,MIT,MIT, finalhandler,0.3.6,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/express/node_modules/finalhandler,git+https://github.com/pillarjs/finalhandler,MIT,MIT,MIT, forwarded,0.1.0,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/express/node_modules/proxy-addr/node_modules/forwarded,git+https://github.com/jshttp/forwarded,MIT,MIT,MIT, fresh,0.2.4,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/express/node_modules/fresh,git+https://github.com/jshttp/fresh,MIT,MIT,MIT, ipaddr.js,1.0.1,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/express/node_modules/proxy-addr/node_modules/ipaddr.js,http://github.com/whitequark/ipaddr.js,MIT,MIT,, js-yaml,3.2.7,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/cfenv/node_modules/js-yaml,http://github.com/nodeca/js-yaml,MIT,MIT,MIT, lodash,3.10.1,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/cfenv/node_modules/js-yaml/node_modules/argparse/node_modules/lodash,git+https://github.com/lodash/lodash,MIT,MIT,, media-typer,0.3.0,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/express/node_modules/type-is/node_modules/media-typer,git+https://github.com/jshttp/media-typer,MIT,MIT,MIT, merge-descriptors,1.0.0,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/express/node_modules/merge-descriptors,git+https://github.com/component/merge-descriptors,MIT,MIT,MIT, methods,1.1.1,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/express/node_modules/methods,git+https://github.com/jshttp/methods,MIT,MIT,MIT, mime,1.3.4,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/express/node_modules/send/node_modules/mime,git+https://github.com/broofa/node-mime,MIT,MIT,, mime-db,1.17.0,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/express/node_modules/accepts/node_modules/mime-types/node_modules/mime-db,git+https://github.com/jshttp/mime-db,MIT,MIT,MIT, mime-types,2.1.5,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/express/node_modules/accepts/node_modules/mime-types,git+https://github.com/jshttp/mime-types,MIT,MIT,MIT, ms,0.7.1,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/express/node_modules/debug/node_modules/ms,http://github.com/guille/ms.js,MIT,,MIT,MIT negotiator,0.5.3,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/express/node_modules/accepts/node_modules/negotiator,git+https://github.com/jshttp/negotiator,MIT,MIT,MIT, on-finished,2.2.1,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/express/node_modules/on-finished,git+https://github.com/jshttp/on-finished,MIT,MIT,MIT, parseurl,1.3.0,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/express/node_modules/parseurl,git+https://github.com/expressjs/parseurl,MIT,MIT,MIT, path-to-regexp,0.1.3,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/express/node_modules/path-to-regexp,git+https://github.com/component/path-to-regexp,Unknown,,, ports,1.1.0,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/cfenv/node_modules/ports,git+https://github.com/hoodiehq/node-ports,Apache 2.0,Apache 2.0,, proxy-addr,1.0.8,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/express/node_modules/proxy-addr,git+https://github.com/jshttp/proxy-addr,MIT,MIT,MIT, qs,2.4.2,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/express/node_modules/qs,git+https://github.com/hapijs/qs,BSD,BSD,, range-parser,1.0.2,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/express/node_modules/range-parser,git+https://github.com/jshttp/range-parser,MIT,MIT,MIT, send,0.12.3,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/express/node_modules/send,git+https://github.com/pillarjs/send,MIT,MIT,MIT, serve-static,1.9.3,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/express/node_modules/serve-static,git+https://github.com/expressjs/serve-static,MIT,MIT,MIT, sprintf-js,1.0.3,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/cfenv/node_modules/js-yaml/node_modules/argparse/node_modules/sprintf-js,git+https://github.com/alexei/sprintf.js,BSD;BSD-3-Clause,BSD-3-Clause,,BSD type-is,1.6.7,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/express/node_modules/type-is,git+https://github.com/jshttp/type-is,MIT,MIT,MIT, underscore,1.7.0,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/cfenv/node_modules/underscore,http://github.com/jashkenas/underscore,MIT,MIT,, utils-merge,1.0.0,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/express/node_modules/utils-merge,http://github.com/jaredhanson/utils-merge,MIT,MIT,MIT,MIT vary,1.0.1,/home/fmariani/bluemix/MyTestLicensesApp/node_modules/express/node_modules/vary,git+https://github.com/jshttp/vary,MIT,MIT,MIT name,version,directory,repository,summary,from package.json,from license,from readme
本文介绍了如何在发布新软件之前自动获取项目中所有依赖项的必要信息。在示例中,自动化工作是在 IBM Bluemix DevOps Services 上的项目管道中完成的,所以每次将一个包含代码更改的新提交推送到服务器时,都会再次执行整个管道并更新所有许可报告。我希望这个自动化流程可让您在收集应用程序法律信息的枯燥任务中节省一些时间,少些烦恼!