Skip to content

Commit

Permalink
❇️ add a new option to use spdx identifier in reports
Browse files Browse the repository at this point in the history
 - SPDX identifier is essential to make license match between CI tools.
 - Using the proper --use_spdx_licenses, the user can get those licenses
in the proper format to use reports in tools such as Sonar License Check plugins
  • Loading branch information
etiennecadicidean committed Feb 1, 2022
1 parent cfbc7d3 commit a71763b
Show file tree
Hide file tree
Showing 12 changed files with 59 additions and 10 deletions.
3 changes: 2 additions & 1 deletion lib/license_finder/cli/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ def license_finder_config
:recursive,
:sbt_include_groups,
:conda_bash_setup_script,
:composer_check_require_only
:composer_check_require_only,
:use_spdx_id
).merge(
logger: logger_mode
)
Expand Down
7 changes: 6 additions & 1 deletion lib/license_finder/cli/main.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ def self.shared_options
method_option :columns,
desc: "For text or CSV reports, which columns to print. Pick from: #{CsvReport::AVAILABLE_COLUMNS}",
type: :array

method_option :use_spdx_id,
type: :boolean,
desc: 'For reports, use the SPDX identifier instead of license name (useful to match license with other standard tools)',
default: false
end

desc 'project_roots', 'List project directories to be scanned'
Expand Down Expand Up @@ -210,7 +215,7 @@ def save_report(content, file_name)
def report_of(content)
report = FORMATS[config.format] || FORMATS['text']
report = MergedReport if report == CsvReport && config.aggregate_paths
report.of(content, columns: config.columns, project_name: decisions.project_name || config.project_path.basename.to_s, write_headers: config.write_headers)
report.of(content, columns: config.columns, project_name: decisions.project_name || config.project_path.basename.to_s, write_headers: config.write_headers, use_spdx_id: config.use_spdx_id)
end

def save?
Expand Down
4 changes: 4 additions & 0 deletions lib/license_finder/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ def columns
get(:columns)
end

def use_spdx_id
get(:use_spdx_id)
end

def sbt_include_groups
get(:sbt_include_groups)
end
Expand Down
8 changes: 7 additions & 1 deletion lib/license_finder/license.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def find_by_text(text)
def initialize(settings)
@short_name = settings.fetch(:short_name)
@pretty_name = settings.fetch(:pretty_name, short_name)
@spdx_id = settings.fetch(:spdx_id, '')
@other_names = settings.fetch(:other_names, [])
@url = settings.fetch(:url)
@matcher = settings.fetch(:matcher) { Matcher.from_template(Template.named(short_name)) }
Expand All @@ -51,6 +52,10 @@ def name
pretty_name
end

def standard_id
spdx_id
end

def stripped_name(name)
name.sub(/^The /i, '')
end
Expand All @@ -77,7 +82,7 @@ def unrecognized_matcher?

private

attr_reader :short_name, :pretty_name, :other_names
attr_reader :short_name, :pretty_name, :other_names, :spdx_id
attr_reader :matcher

def names
Expand All @@ -93,6 +98,7 @@ def initialize(name, operator = AndLicense.operator)
@short_name = name
@pretty_name = name
@url = nil
@spdx_id = nil
@matcher = NoneMatcher.new
# removes heading and trailing parentesis and splits
name = name[1..-2] if name.start_with?('(')
Expand Down
22 changes: 22 additions & 0 deletions lib/license_finder/license/definitions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ def apache1_1
License.new(
short_name: 'Apache1_1',
pretty_name: 'Apache 1.1',
spdx_id: 'Apache-1.0',
other_names: [
'Apache-1.1',
'The Apache Software License, Version 1.1'
Expand All @@ -58,6 +59,7 @@ def apache2
License.new(
short_name: 'Apache2',
pretty_name: 'Apache 2.0',
spdx_id: 'Apache-2.0',
other_names: [
'Apache-2.0',
'Apache Software License',
Expand All @@ -79,6 +81,7 @@ def apache2
def bsd
License.new(
short_name: 'BSD',
spdx_id: 'BSD-4-Clause',
other_names: ['BSD4', 'bsd-old', '4-clause BSD', 'BSD-4-Clause', 'BSD 4-Clause', 'BSD License'],
url: 'http://en.wikipedia.org/wiki/BSD_licenses#4-clause_license_.28original_.22BSD_License.22.29'
)
Expand All @@ -87,6 +90,7 @@ def bsd
def cc01
License.new(
short_name: 'CC01',
spdx_id: 'CC0-1.0',
pretty_name: 'CC0 1.0 Universal',
other_names: ['CC0 1.0'],
url: 'http://creativecommons.org/publicdomain/zero/1.0'
Expand All @@ -96,6 +100,7 @@ def cc01
def cddl1
License.new(
short_name: 'CDDL1',
spdx_id: 'CDDL-1.0',
pretty_name: 'Common Development and Distribution License 1.0',
other_names: [
'CDDL-1.0',
Expand All @@ -109,6 +114,7 @@ def cddl1
def eclipse1
License.new(
short_name: 'EPL1',
spdx_id: 'EPL-1.0',
pretty_name: 'Eclipse Public License 1.0',
other_names: [
'EPL-1.0',
Expand All @@ -122,6 +128,7 @@ def eclipse1
def gplv2
License.new(
short_name: 'GPLv2',
spdx_id: 'GPL-2.0-only',
other_names: ['GPL V2', 'gpl-v2', 'GNU GENERAL PUBLIC LICENSE Version 2'],
url: 'http://www.gnu.org/licenses/gpl-2.0.txt'
)
Expand All @@ -130,6 +137,7 @@ def gplv2
def gplv3
License.new(
short_name: 'GPLv3',
spdx_id: 'GPL-3.0-only',
other_names: ['GPL V3', 'gpl-v3', 'GNU GENERAL PUBLIC LICENSE Version 3'],
url: 'http://www.gnu.org/licenses/gpl-3.0.txt'
)
Expand All @@ -138,13 +146,15 @@ def gplv3
def isc
License.new(
short_name: 'ISC',
spdx_id: 'ISC',
url: 'http://en.wikipedia.org/wiki/ISC_license'
)
end

def lgpl
License.new(
short_name: 'LGPL',
spdx_id: 'LGPL-3.0-only',
other_names: ['LGPL-3', 'LGPLv3', 'LGPL-3.0'],
url: 'http://www.gnu.org/licenses/lgpl.txt'
)
Expand All @@ -153,6 +163,7 @@ def lgpl
def lgpl2_1
License.new(
short_name: 'LGPL2_1',
spdx_id: 'LGPL-2.1-only',
pretty_name: 'GNU Lesser General Public License version 2.1',
other_names: [
'LGPL-2.1-only',
Expand All @@ -178,6 +189,7 @@ def mit

License.new(
short_name: 'MIT',
spdx_id: 'MIT',
other_names: ['Expat', 'MIT license', 'MIT License', 'The MIT License (MIT)'],
url: 'http://opensource.org/licenses/mit-license',
matcher: matcher
Expand All @@ -197,6 +209,7 @@ def mpl1_1

License.new(
short_name: 'MPL1_1',
spdx_id: 'MPL-1.1',
pretty_name: 'Mozilla Public License 1.1',
other_names: [
'MPL-1.1',
Expand All @@ -218,6 +231,7 @@ def mpl2

License.new(
short_name: 'MPL2',
spdx_id: 'MPL-2.0',
pretty_name: 'Mozilla Public License 2.0',
other_names: [
'MPL-2.0',
Expand All @@ -243,6 +257,7 @@ def newbsd

License.new(
short_name: 'NewBSD',
spdx_id: 'BSD-3-Clause',
pretty_name: 'New BSD',
other_names: [
'Modified BSD',
Expand All @@ -266,6 +281,7 @@ def newbsd
def ofl
License.new(
short_name: 'OFL',
spdx_id: 'OFL-1.1',
pretty_name: 'SIL OPEN FONT LICENSE Version 1.1',
other_names: [
'OPEN FONT LICENSE Version 1.1'
Expand All @@ -277,6 +293,7 @@ def ofl
def python
License.new(
short_name: 'Python',
spdx_id: 'PSF-2.0',
pretty_name: 'Python Software Foundation License',
other_names: [
'PSF',
Expand All @@ -297,6 +314,7 @@ def ruby

License.new(
short_name: 'Ruby',
spdx_id: 'Ruby',
pretty_name: 'ruby',
url: url,
matcher: matcher
Expand All @@ -306,6 +324,7 @@ def ruby
def simplifiedbsd
License.new(
short_name: 'SimplifiedBSD',
spdx_id: 'BSD-2-Clause',
pretty_name: 'Simplified BSD',
other_names: [
'FreeBSD',
Expand All @@ -321,6 +340,7 @@ def simplifiedbsd
def wtfpl
License.new(
short_name: 'WTFPL',
spdx_id: 'WTFPL',
pretty_name: 'WTFPL',
other_names: [
'WTFPL V2',
Expand All @@ -337,6 +357,7 @@ def zerobsd

License.new(
short_name: '0BSD',
spdx_id: '0BSD',
pretty_name: 'BSD Zero Clause License',
other_names: [
'0-Clause BSD',
Expand All @@ -354,6 +375,7 @@ def zerobsd
def zlib
License.new(
short_name: 'Zlib',
spdx_id: 'Zlib',
pretty_name: 'zlib/libpng license',
other_names: [
'zlib License'
Expand Down
3 changes: 2 additions & 1 deletion lib/license_finder/report.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ def self.of(dependencies, options)
def initialize(dependencies, options)
@dependencies = dependencies
@project_name = options[:project_name]
@use_spdx_id = options[:use_spdx_id]
end

private

attr_reader :dependencies, :project_name
attr_reader :dependencies, :project_name, :use_spdx_id

def sorted_dependencies
dependencies.sort
Expand Down
2 changes: 1 addition & 1 deletion lib/license_finder/reports/csv_report.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def format_licenses(dep)
if dep.missing?
MISSING_DEPENDENCY_TEXT
else
dep.licenses.map(&:name).join(self.class::COMMA_SEP)
dep.licenses.map(&@use_spdx_id ? :standard_id : :name).join(self.class::COMMA_SEP)
end
end

Expand Down
4 changes: 2 additions & 2 deletions lib/license_finder/reports/erb_report.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def grouped_dependencies
end

def link_to_license(license)
link_to_maybe license.name, license.url
link_to_maybe (@use_spdx_id ? license.standard_id : license.name), license.url
end

def link_to_dependency(dependency)
Expand All @@ -42,7 +42,7 @@ def link_to(text, link = "##{text}")
end

def license_names(dependency)
dependency.licenses.map(&:name).sort.join ', '
dependency.licenses.map(&@use_spdx_id? :standard_id : :name).sort.join ', '
end

def license_links(dependency)
Expand Down
3 changes: 2 additions & 1 deletion lib/license_finder/reports/json_report.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ def build_deps
end

def format_licenses(dep)
dep.missing? ? [] : dep.licenses.map(&:name)
dep.missing? ? [] :
dep.licenses.map(&(@use_spdx_id ? :standard_id : :name))
end
end
end
3 changes: 2 additions & 1 deletion spec/lib/license_finder/cli/main_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ module CLI

it 'passes the config options to the new LicenseFinder::LicenseAggregator instance' do
stubs = { valid_project_path?: true, project_path: Pathname('../other_project'),
decisions_file_path: Pathname('../other_project'), aggregate_paths: nil, recursive: false, format: nil, columns: nil, strict_matching: false, write_headers: false }
decisions_file_path: Pathname('. ./other_project'), aggregate_paths: nil, recursive: false,
format: nil, columns: nil, strict_matching: false, write_headers: false, use_spdx_id: false }
configuration = double(:configuration, stubs)
allow(LicenseFinder::Configuration).to receive(:with_optional_saved_config).and_return(configuration)
expect(LicenseFinder::LicenseAggregator).to receive(:new).with(configuration, anything).and_return(license_finder_instance)
Expand Down
2 changes: 1 addition & 1 deletion spec/lib/license_finder/license_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ module LicenseFinder
end
end
end
describe AndLicense do
describe OrLicense do
describe '.find_by_name' do
# sub licenses case already tested on and, since this is subclass
it 'should create a compound OR license' do
Expand Down
8 changes: 8 additions & 0 deletions spec/lib/license_finder/reports/csv_report_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ module LicenseFinder
expect(subject.to_s).to eq("gem_a,MIT,#{mit.url}\n")
end

it 'use license spdx identifier instead of name' do
dep = Package.new('gem_a', '1.0')
license = License.find_by_name('Apache 2.0')
dep.decide_on_license(license)
subject = described_class.new([dep], use_spdx_id: true)
expect(subject.to_s).to eq("gem_a,1.0,Apache-2.0\n")
end

it 'does not include columns that should only be in merged reports' do
dep = Package.new('gem_a', '1.0')
subject = described_class.new([dep], columns: %w[aggregate_paths])
Expand Down

0 comments on commit a71763b

Please sign in to comment.