"Open-source software (OSS) is great. Anyone can use virtually any open-source code in their projects."
Well, it depends. Licenses can make things difficult, especially when you are developing closed-source software. Since some OSS licenses even require the employing application to be open-sourced as well (looking at you, GPL), you cannot use such software in a closed-source project.
To be sure on this, we have developed a project-level integration of Pivotal's excellent license_finder Show archive.org snapshot that validates all dependencies during a regular test run. It will protect you from accidentally adding libraries with problematic licenses.
Installation
- Add
gem 'license_finder'
to the develoment dependencies of your project and runbundle install
- Add
spec/license_finder_spec.rb
with
describe 'license_finder' do
describe 'action_items' do
before :all do # rubocop:disable RSpec/BeforeAfterAll
@stdout, @stderr, @status = Open3.capture3('license_finder action_items')
end
it 'activates all expected package managers' do
package_managers = @stdout.scan(/^LicenseFinder::(\w+).*is active/).flatten
expect(package_managers).to contain_exactly 'Bundler', 'Please list all expected package managers here'
end
it 'has no dependencies with unapproved licenses' do
expect(@status).to be_success, @stdout + @stderr
end
end
end
The first spec ensures no package manager is accidentally skipped (especially in the fragile Bower integration). Whenever a library with an unapproved (or restricted) license is added to the project, the second spec will fail.
Run the spec now and update its list of package managers.
-
Optional: Create an alias to simplify running LicenseFinder.
echo "alias lf='bundle exec license_finder'" >> ~/.bash_aliases # Store alias source ~/.bash_aliases # Load stored aliases
-
For current versions of yarn (>=2) you will have to install a plugin Show archive.org snapshot to have the
yarn licenses
command available
We have a separate card on useful defaults to getting started with License Finder.
Excluding dependency groups
LicenseFinder can exclude certain dependency groups from its report, for example development
and test
(add devDependencies
for yarn
). This only works with
certain package managers (among which Bundler and Yarn)
Show archive.org snapshot
. You can do so with:
bundle exec license_finder ignored_groups add $GROUP --why $REASON
Running the "action items" report
LicenseFinder runs as part of the test suite. Run manually with
bundle exec license_finder
It will detect all known package managers Show archive.org snapshot automatically (among which Bundler and Yarn) and check the license of all dependencies.
This is how the output may look when all dependencies are approved:
This is how it may look when dependencies need approval (it is also the output of the failing licenses spec):
Read on to learn what to do about the output. All decisions are written to doc/dependency_decisions.yml. Please always state the reason for your decision with --why
.
Permitting a license
When you are absolutely sure that a license can be used in any circumstances, you may permit it. Ask someone if you are not sure. Github license pages Show archive.org snapshot or tldrlegal.com Show archive.org snapshot give a good overview about a license's permissions, limitations and conditions.
Permit a license with
bundle exec license_finder permitted_licenses add $LICENSE --why $REASON
Dependencies with this license will automatically be considered approved, and will no more show up in the action items list. If you know there is a license that can never be used in your project, restrict it with
bundle exec license_finder restricted_licenses add AGPL --why 'Requires to open-source the whole application'
Approving a single library
Some libraries will bring a license that has not been white listed. If your usage is valid with regard to that library's license, you can approve a single library without generally white-listing its license. GPL-licensed libraries are a candidate for this.
bundle exec license_finder approvals add $DEPENDENCY --why $REASON
Setting the license when unknown
When a license is reported as "unknown", you need to detect it manually. Check if current versions sport a license. Check package.json
, composer.json
, README
.
Once you know the license, tell LicenseFinder with
bundle exec license_finder licenses add $DEPENDENCY $LICENSE --why $LICENSE_FILE_URL
Hard-to-find licenses
- Rubygem ntlm-http:
bundle exec license_finder licenses add ntlm-http ruby --why https://github.com/trampoline/ntlm-http/blob/9a61eaf20bd93ba035c17a79df0e9c814ffe6f2e/lib/net/ntlm_http.rb
Adding a hidden dependency
Sometimes there may be a dependency the package manager does not know about, e.g. a copied tracking snippet or some nested dependency. Tell LicenseFinder with
bundle exec license_finder dependencies add $DEPENDENCY $LICENSE $VERSION --why $REASON
Use sparingly, as manually added dependencies are not updated automatically (obviously).
Remarks
- License identifiers (e.g. LGPL-2.1) follow the SPDX definitions
- Some license identifiers are followed by an asterisk. This denotes an inferred license Show archive.org snapshot .
- While it is possible to approve specific versions only, we decided to approve libraries as a whole. This is to keep maintenance efforts to a minimum.
- LicenseFinder also has a concept of decision inheritance Show archive.org snapshot . While we're using it in few projects only, we don't need this functionality.
- You can specify a
--who $NAME
to each decision to state who made the decision. However, Git logs will usually reveal that. - Most "add" commands above offer a "list" command that prints all registered entries.
- Use
license_finder help [command]
for help. - If you made a mistake (e.g. forgot to pass a reason), you can open doc/dependency_decisions.yml and edit the last entry.
A word on bower-rails
Bower-rails is a Bower wrapper that simplifies Rails integration. Unfortunately, it makes it harder to integrate Bower with LicenseFinder. To get things running, follow these steps:
- Symlink bower-rails's bower.json to the project root:
ln -s vendor/assets/bower.json
- Create a
.bowerrc
file at the project root with{ "comment": "Together with the bower.json symlink, this file integrates bower-rails with license_finder.", "directory": "vendor/assets/bower_components" }
- Remember to add Bower to the expected package managers in spec/license_finder_spec.rb.
A word on vendor/asset-libs
Manually vendored libraries in vendor/asset-libs/ are not discovered by LicenseFinder. Convert each such library to a local Ruby gem by following these steps:
- Move all contents of
vendor/asset-libs/$library-$version
to a new sub-directoryvendor/asset-libs/$library-$version/lib/assets/javascripts
. That's simply a path sprockets will consider, it is no problem if it contains non-JS files. - Create
$library-$version/local-$library.gemspec
withThis is a minimal gemspec.Gem::Specification.new do |s| s.name = 'local-$library' s.summary = 'local copy of $library' s.homepage = $homepage s.version = $version s.licenses = [$license] s.authors = [$author] s.files = Dir.chdir(__dir__) { Dir.glob('**/*') } s.add_runtime_dependency 'rails', '< 100' # Silence warnings by adding version end
- Create
$library-$version/lib/local-$library.rb
withHaving the gem contain a Rails engine activates Sprockets, which will retrieve assets from the gem.module Local$Library class Engine < ::Rails::Engine end end
- Rename
$library-$version
tolocal-$library
- Find a suitable section in your Gemfile, then add
gem 'local-$library', path: 'vendor/asset-libs/local-$library'
Finally, remove the asset path customization from config/initializers/assets.rb:
-# Load asset libs folders
-Rails.application.config.assets.paths += Dir[Rails.root.join('vendor/asset-libs/*')].sort_by { |dir| -dir.size }