From 9e34752a0078049b3bac9b878b33b40b57370f44 Mon Sep 17 00:00:00 2001 From: Michael O'Neil Date: Thu, 20 Oct 2022 18:09:43 -0400 Subject: [PATCH 1/3] Add ability to specify package version when setting a manual license Currently, adding a license for a package applies it to all versions of that package. However, packages sometimes change their license between versions. This change allows the --version option to be specified when invoking `license_finder licenses [add | remove]`. Similar to how approvals work, once licenses for specific versions of a given dependency are added, any licenses added for that dependency without the --version option are ignored. --- .../configure/assign_licenses_spec.rb | 26 +++++++ lib/license_finder/cli/licenses.rb | 14 +++- lib/license_finder/decision_applier.rb | 2 +- lib/license_finder/decisions.rb | 43 +++++++++-- .../license_finder/decision_applier_spec.rb | 10 +++ spec/lib/license_finder/decisions_spec.rb | 71 +++++++++++++++++++ 6 files changed, 158 insertions(+), 8 deletions(-) diff --git a/features/features/configure/assign_licenses_spec.rb b/features/features/configure/assign_licenses_spec.rb index 3607ffea2..535debe07 100644 --- a/features/features/configure/assign_licenses_spec.rb +++ b/features/features/configure/assign_licenses_spec.rb @@ -34,4 +34,30 @@ developer.run_license_finder expect(developer).to be_seeing_something_like /mislicensed_dep.*Default/ end + + specify 'can be assigned and removed by package version' do + project = developer.create_ruby_app + dep_a_gem = developer.create_gem 'dep_a', version: '2.0.0', license: 'Unknown' + dep_b_gem = developer.create_gem 'dep_b', version: '3.0.0', license: 'Unknown' + project.depend_on dep_a_gem + project.depend_on dep_b_gem + + developer.execute_command 'license_finder licenses add dep_a GPL --version=1.0.0' + developer.execute_command 'license_finder licenses add dep_a MIT --version=2.0.0' + developer.execute_command 'license_finder licenses add dep_a GPL --version=3.0.0' + + developer.execute_command 'license_finder licenses add dep_b GPL --version=1.0.0' + developer.execute_command 'license_finder licenses add dep_b GPL --version=2.0.0' + developer.execute_command 'license_finder licenses add dep_b Apache-2.0 --version=3.0.0' + + developer.run_license_finder + expect(developer).to be_seeing_line 'dep_a, 2.0.0, MIT' + expect(developer).to be_seeing_line 'dep_b, 3.0.0, "Apache 2.0"' + + developer.execute_command 'license_finder licenses remove dep_a MIT --version=2.0.0' + + developer.run_license_finder + expect(developer).to be_seeing_line 'dep_a, 2.0.0, Unknown' + expect(developer).to be_seeing_line 'dep_b, 3.0.0, "Apache 2.0"' + end end diff --git a/lib/license_finder/cli/licenses.rb b/lib/license_finder/cli/licenses.rb index f99edc181..9d876a39c 100644 --- a/lib/license_finder/cli/licenses.rb +++ b/lib/license_finder/cli/licenses.rb @@ -7,19 +7,29 @@ class Licenses < Base include MakesDecisions auditable + method_option :version, desc: 'The version associated with the license' desc 'add DEPENDENCY LICENSE', "Set a dependency's licenses, overwriting any license_finder has found" def add(name, license) modifying { decisions.license(name, license, txn) } - printer.say "The #{name} dependency has been marked as using #{license} license!", :green + if options[:version] + printer.say "The #{name} dependency with version #{options[:version]} has been marked as using #{license} license!", :green + else + printer.say "The #{name} dependency has been marked as using #{license} license!", :green + end end auditable + method_option :version, desc: 'The version associated with the license' desc 'remove DEPENDENCY LICENSE', 'Remove a manually set license' def remove(dep, lic) modifying { decisions.unlicense(dep, lic, txn) } - printer.say "The dependency #{dep} no longer has a manual license" + if options[:version] + printer.say "The dependency #{dep} with version #{options[:version]} no longer has a manual license of #{lic}" + else + printer.say "The dependency #{dep} no longer has a manual license of #{lic}" + end end end end diff --git a/lib/license_finder/decision_applier.rb b/lib/license_finder/decision_applier.rb index e0e3d2e27..8a49956be 100644 --- a/lib/license_finder/decision_applier.rb +++ b/lib/license_finder/decision_applier.rb @@ -44,7 +44,7 @@ def ignored?(package) end def with_decided_licenses(package) - decisions.licenses_of(package.name).each do |license| + decisions.licenses_of(package.name, package.version).each do |license| package.decide_on_license license end package diff --git a/lib/license_finder/decisions.rb b/lib/license_finder/decisions.rb index 3f673d528..f53a60f36 100644 --- a/lib/license_finder/decisions.rb +++ b/lib/license_finder/decisions.rb @@ -11,8 +11,20 @@ class Decisions attr_reader :packages, :permitted, :restricted, :ignored, :ignored_groups, :project_name, :inherited_decisions - def licenses_of(name) - @licenses[name] + ALL_VERSIONS = :all + private_constant :ALL_VERSIONS + + def licenses_of(name, version = nil) + return Set.new unless @licenses.key?(name) + + has_licenses_for_all_versions = @licenses[name].key?(ALL_VERSIONS) && @licenses[name].size == 1 + licenses_for_all_versions = has_licenses_for_all_versions ? @licenses[name][ALL_VERSIONS] : Set.new + + if version.nil? + licenses_for_all_versions + else + @licenses[name][version] || licenses_for_all_versions + end end def homepage_of(name) @@ -76,7 +88,7 @@ def self.from_hash(txn, versions) def initialize @decisions = [] @packages = Set.new - @licenses = Hash.new { |h, k| h[k] = Set.new } + @licenses = {} @homepages = {} @approvals = {} @permitted = Set.new @@ -100,13 +112,34 @@ def remove_package(name, txn = {}) def license(name, lic, txn = {}) add_decision [:license, name, lic, txn] - @licenses[name] << License.find_by_name(lic) + + versions = txn[:versions] + versions = [ALL_VERSIONS] if versions.nil? || versions.empty? + + versions.each do |version| + @licenses[name] ||= {} + @licenses[name][version] ||= Set.new + @licenses[name][version] << License.find_by_name(lic) + end + self end def unlicense(name, lic, txn = {}) add_decision [:unlicense, name, lic, txn] - @licenses[name].delete(License.find_by_name(lic)) + + if @licenses[name] + versions = txn[:versions] + versions = [ALL_VERSIONS] if versions.nil? || versions.empty? + + versions.each do |version| + if @licenses[name][version] + @licenses[name][version].delete(License.find_by_name(lic)) + @licenses[name].delete(version) if @licenses[name][version].empty? + end + end + end + self end diff --git a/spec/lib/license_finder/decision_applier_spec.rb b/spec/lib/license_finder/decision_applier_spec.rb index 68b79c9d5..5ec8d04f3 100644 --- a/spec/lib/license_finder/decision_applier_spec.rb +++ b/spec/lib/license_finder/decision_applier_spec.rb @@ -38,6 +38,16 @@ module LicenseFinder expect(decision_applier.acknowledged.last.licenses).to eq Set.new([License.find_by_name('MIT')]) end + it 'applies decided licenses for specific versions' do + decisions = Decisions.new + .add_package('manual', '2.0.0') + .license('manual', 'MIT', { versions: ['1.0.0'] }) + .license('manual', 'GPL', { versions: ['2.0.0'] }) + .license('manual', 'Apache-2.0', { versions: ['3.0.0'] }) + decision_applier = described_class.new(decisions: decisions, packages: []) + expect(decision_applier.acknowledged.last.licenses).to eq Set.new([License.find_by_name('GPL')]) + end + it 'applies decided homepage' do decisions = Decisions.new .add_package('manual', nil) diff --git a/spec/lib/license_finder/decisions_spec.rb b/spec/lib/license_finder/decisions_spec.rb index 0d1c6cf83..2b8d97246 100644 --- a/spec/lib/license_finder/decisions_spec.rb +++ b/spec/lib/license_finder/decisions_spec.rb @@ -42,6 +42,14 @@ module LicenseFinder expect(license).to eq License.find_by_name('MIT') end + it 'will report license for a dependency of any version' do + license = subject + .license('dep', 'MIT') + .licenses_of('dep', '1.0.0') + .first + expect(license).to eq License.find_by_name('MIT') + end + it 'will report multiple licenses' do licenses = subject .license('dep', 'MIT') @@ -60,6 +68,41 @@ module LicenseFinder .first expect(license).to eq License.find_by_name('MIT') end + + it 'supports specifying a package version' do + subject.license('dep', 'MIT', { versions: ['1.0.0'] }) + + expect(subject.licenses_of('dep', '1.0.0')).to eq [License.find_by_name('MIT')].to_set + expect(subject.licenses_of('dep', '2.0.0')).to be_empty + expect(subject.licenses_of('dep', nil)).to be_empty + expect(subject.licenses_of('dep')).to be_empty + end + + it 'supports different licenses for different package versions' do + subject.license('dep', 'MIT', { versions: ['1.0.0'] }) + subject.license('dep', 'GPL', { versions: ['1.0.0'] }) + subject.license('dep', 'Apache-2.0', { versions: ['2.0.0'] }) + + expect(subject.licenses_of('dep', '1.0.0')).to eq [ + License.find_by_name('MIT'), + License.find_by_name('GPL') + ].to_set + expect(subject.licenses_of('dep', '2.0.0')).to eq [License.find_by_name('Apache-2.0')].to_set + end + + it 'ignores a license applied to all versions if any version-specific licenses are also defined' do + subject.license('dep', 'MIT', { versions: [] }) + + expect(subject.licenses_of('dep', '1.0.0')).to eq [License.find_by_name('MIT')].to_set + expect(subject.licenses_of('dep', '2.0.0')).to eq [License.find_by_name('MIT')].to_set + expect(subject.licenses_of('dep', nil)).to eq [License.find_by_name('MIT')].to_set + + subject.license('dep', 'GPL', { versions: ['1.0.0'] }) + + expect(subject.licenses_of('dep', '1.0.0')).to eq [License.find_by_name('GPL')].to_set + expect(subject.licenses_of('dep', '2.0.0')).to be_empty + expect(subject.licenses_of('dep', nil)).to be_empty + end end describe '.unlicense' do @@ -89,6 +132,34 @@ module LicenseFinder .first expect(license).to eq License.find_by_name('MIT') end + + it 'removes the license for the correct version' do + subject.license('dep', 'MIT', { versions: ['1.0.0'] }) + expect(subject.licenses_of('dep', '1.0.0')).to eq [License.find_by_name('MIT')].to_set + + subject.unlicense('dep', 'MIT', { versions: [] }) + expect(subject.licenses_of('dep', '1.0.0')).to eq [License.find_by_name('MIT')].to_set + + subject.unlicense('dep', 'MIT', { versions: ['2.0.0'] }) + expect(subject.licenses_of('dep', '1.0.0')).to eq [License.find_by_name('MIT')].to_set + + subject.unlicense('dep', 'MIT', { versions: ['1.0.0'] }) + expect(subject.licenses_of('dep', '1.0.0')).to be_empty + end + + it 'reverts to the previous license for all versions after unlicensing all specific versions' do + subject.license('dep', 'MIT', { versions: [] }) + expect(subject.licenses_of('dep', '1.0.0')).to eq [License.find_by_name('MIT')].to_set + + subject.license('dep', 'GPL', { versions: ['1.0.0'] }) + expect(subject.licenses_of('dep', '1.0.0')).to eq [License.find_by_name('GPL')].to_set + + subject.unlicense('dep', 'GPL', { versions: ['1.0.0'] }) + expect(subject.licenses_of('dep', '1.0.0')).to eq [License.find_by_name('MIT')].to_set + + subject.unlicense('dep', 'MIT', { versions: [] }) + expect(subject.licenses_of('dep', '1.0.0')).to be_empty + end end describe '.homepage' do From 5502b129553a5778347a83463708dd7546931626 Mon Sep 17 00:00:00 2001 From: Michael O'Neil Date: Thu, 10 Nov 2022 11:50:08 -0500 Subject: [PATCH 2/3] Change implementation of how removing a license should work This makes the license optional when removing a license. When omitted, all licenses will be removed from the relevant package versions. --- .../configure/assign_licenses_spec.rb | 38 +++++++-- lib/license_finder/cli/licenses.rb | 17 ++-- lib/license_finder/decisions.rb | 40 +++------- lib/license_finder/manual_licenses.rb | 79 +++++++++++++++++++ spec/lib/license_finder/decisions_spec.rb | 66 +++++++++++----- 5 files changed, 177 insertions(+), 63 deletions(-) create mode 100644 lib/license_finder/manual_licenses.rb diff --git a/features/features/configure/assign_licenses_spec.rb b/features/features/configure/assign_licenses_spec.rb index 535debe07..a44fef4e1 100644 --- a/features/features/configure/assign_licenses_spec.rb +++ b/features/features/configure/assign_licenses_spec.rb @@ -14,6 +14,7 @@ gem = developer.create_gem 'mislicensed_dep', license: 'Unknown' project.depend_on gem developer.execute_command 'license_finder licenses add mislicensed_dep Known' + expect(developer).to be_seeing('The mislicensed_dep dependency has been marked as using Known license!') developer.run_license_finder expect(developer).not_to be_seeing_something_like /mislicensed_dep.*Unknown/ @@ -22,17 +23,33 @@ specify 'can be removed, revealing the default license for a dependency' do project = developer.create_ruby_app - gem = developer.create_gem 'mislicensed_dep', license: 'Default' - project.depend_on gem - developer.execute_command 'license_finder licenses add mislicensed_dep Manual_license' + gem_a = developer.create_gem 'mislicensed_dep_a', license: 'Default' + gem_b = developer.create_gem 'mislicensed_dep_b', license: 'Default' + project.depend_on gem_a + project.depend_on gem_b + developer.execute_command 'license_finder licenses add mislicensed_dep_a Manual_license_a' + developer.execute_command 'license_finder licenses add mislicensed_dep_b Manual_license_b' + + developer.run_license_finder + expect(developer).to be_seeing_something_like /mislicensed_dep_a.*Manual_license_a/ + expect(developer).to be_seeing_something_like /mislicensed_dep_b.*Manual_license_b/ + + developer.execute_command 'license_finder licenses remove mislicensed_dep_a Manual_license_a' + expect(developer).to be_seeing('The dependency mislicensed_dep_a no longer has a manual license of Manual_license_a') + + developer.execute_command 'license_finder licenses remove mislicensed_dep_b Incorrect_license' + expect(developer).to be_seeing('The dependency mislicensed_dep_b no longer has a manual license of Incorrect_license') developer.run_license_finder - expect(developer).to be_seeing_something_like /mislicensed_dep.*Manual_license/ + expect(developer).to be_seeing_something_like /mislicensed_dep_a.*Default/ + expect(developer).to be_seeing_something_like /mislicensed_dep_b.*Manual_license_b/ - developer.execute_command 'license_finder licenses remove mislicensed_dep Manual_license' + developer.execute_command 'license_finder licenses remove mislicensed_dep_b' + expect(developer).to be_seeing('The dependency mislicensed_dep_b no longer has a manual license') developer.run_license_finder - expect(developer).to be_seeing_something_like /mislicensed_dep.*Default/ + expect(developer).to be_seeing_something_like /mislicensed_dep_a.*Default/ + expect(developer).to be_seeing_something_like /mislicensed_dep_b.*Default/ end specify 'can be assigned and removed by package version' do @@ -45,6 +62,7 @@ developer.execute_command 'license_finder licenses add dep_a GPL --version=1.0.0' developer.execute_command 'license_finder licenses add dep_a MIT --version=2.0.0' developer.execute_command 'license_finder licenses add dep_a GPL --version=3.0.0' + expect(developer).to be_seeing('The dep_a dependency with version 3.0.0 has been marked as using GPL license!') developer.execute_command 'license_finder licenses add dep_b GPL --version=1.0.0' developer.execute_command 'license_finder licenses add dep_b GPL --version=2.0.0' @@ -55,9 +73,17 @@ expect(developer).to be_seeing_line 'dep_b, 3.0.0, "Apache 2.0"' developer.execute_command 'license_finder licenses remove dep_a MIT --version=2.0.0' + expect(developer).to be_seeing('The dependency dep_a with version 2.0.0 no longer has a manual license of MIT') developer.run_license_finder expect(developer).to be_seeing_line 'dep_a, 2.0.0, Unknown' expect(developer).to be_seeing_line 'dep_b, 3.0.0, "Apache 2.0"' + + developer.execute_command 'license_finder licenses remove dep_b --version=3.0.0' + expect(developer).to be_seeing('The dependency dep_b with version 3.0.0 no longer has a manual license') + + developer.run_license_finder + expect(developer).to be_seeing_line 'dep_a, 2.0.0, Unknown' + expect(developer).to be_seeing_line 'dep_b, 3.0.0, Unknown' end end diff --git a/lib/license_finder/cli/licenses.rb b/lib/license_finder/cli/licenses.rb index 9d876a39c..d34c98498 100644 --- a/lib/license_finder/cli/licenses.rb +++ b/lib/license_finder/cli/licenses.rb @@ -12,24 +12,19 @@ class Licenses < Base def add(name, license) modifying { decisions.license(name, license, txn) } - if options[:version] - printer.say "The #{name} dependency with version #{options[:version]} has been marked as using #{license} license!", :green - else - printer.say "The #{name} dependency has been marked as using #{license} license!", :green - end + version_info = options[:version] ? " with version #{options[:version]}" : '' + printer.say "The #{name} dependency#{version_info} has been marked as using #{license} license!", :green end auditable method_option :version, desc: 'The version associated with the license' desc 'remove DEPENDENCY LICENSE', 'Remove a manually set license' - def remove(dep, lic) + def remove(dep, lic = nil) modifying { decisions.unlicense(dep, lic, txn) } - if options[:version] - printer.say "The dependency #{dep} with version #{options[:version]} no longer has a manual license of #{lic}" - else - printer.say "The dependency #{dep} no longer has a manual license of #{lic}" - end + version_info = options[:version] ? " with version #{options[:version]}" : '' + suffix = lic ? " of #{lic}" : '' + printer.say "The dependency #{dep}#{version_info} no longer has a manual license#{suffix}" end end end diff --git a/lib/license_finder/decisions.rb b/lib/license_finder/decisions.rb index 294e43a45..e1bc4a756 100644 --- a/lib/license_finder/decisions.rb +++ b/lib/license_finder/decisions.rb @@ -2,6 +2,7 @@ require 'open-uri' require 'license_finder/license' +require 'license_finder/manual_licenses' module LicenseFinder class Decisions @@ -11,20 +12,8 @@ class Decisions attr_reader :packages, :permitted, :restricted, :ignored, :ignored_groups, :project_name, :inherited_decisions - ALL_VERSIONS = :all - private_constant :ALL_VERSIONS - def licenses_of(name, version = nil) - return Set.new unless @licenses.key?(name) - - has_licenses_for_all_versions = @licenses[name].key?(ALL_VERSIONS) && @licenses[name].size == 1 - licenses_for_all_versions = has_licenses_for_all_versions ? @licenses[name][ALL_VERSIONS] : Set.new - - if version.nil? - licenses_for_all_versions - else - @licenses[name][version] || licenses_for_all_versions - end + @manual_licenses.licenses_of(name, version) end def homepage_of(name) @@ -88,7 +77,7 @@ def self.from_hash(txn, versions) def initialize @decisions = [] @packages = Set.new - @licenses = {} + @manual_licenses = ManualLicenses.new @homepages = {} @approvals = {} @permitted = Set.new @@ -114,12 +103,11 @@ def license(name, lic, txn = {}) add_decision [:license, name, lic, txn] versions = txn[:versions] - versions = [ALL_VERSIONS] if versions.nil? || versions.empty? - versions.each do |version| - @licenses[name] ||= {} - @licenses[name][version] ||= Set.new - @licenses[name][version] << License.find_by_name(lic) + if versions.nil? || versions.empty? + @manual_licenses.assign_to_all_versions(name, lic) + else + @manual_licenses.assign_to_specific_versions(name, lic, versions) end self @@ -128,16 +116,12 @@ def license(name, lic, txn = {}) def unlicense(name, lic, txn = {}) add_decision [:unlicense, name, lic, txn] - if @licenses[name] - versions = txn[:versions] - versions = [ALL_VERSIONS] if versions.nil? || versions.empty? + versions = txn[:versions] - versions.each do |version| - if @licenses[name][version] - @licenses[name][version].delete(License.find_by_name(lic)) - @licenses[name].delete(version) if @licenses[name][version].empty? - end - end + if versions.nil? || versions.empty? + @manual_licenses.unassign_from_all_versions(name, lic) + else + @manual_licenses.unassign_from_specific_versions(name, lic, versions) end self diff --git a/lib/license_finder/manual_licenses.rb b/lib/license_finder/manual_licenses.rb new file mode 100644 index 000000000..b8aac94d0 --- /dev/null +++ b/lib/license_finder/manual_licenses.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +module LicenseFinder + class ManualLicenses + def initialize + @all_versions = {} + @specific_versions = {} + end + + def licenses_of(name, version = nil) + return @all_versions[name] if @all_versions[name] + + if version && @specific_versions[name] && @specific_versions[name][version] + @specific_versions[name][version] + else + Set.new + end + end + + def assign_to_all_versions(name, lic) + # Ex: licenses add foo_gem MIT => Adds MIT at "all" versions for this gem + + @all_versions[name] ||= Set.new + @all_versions[name] << to_license(lic) + + @specific_versions.delete(name) + end + + def assign_to_specific_versions(name, lic, versions) + # Ex: licenses add foo_gem MIT --version=1.0 => Adds MIT at only 1.0 for this gem + + @specific_versions[name] ||= {} + versions.each do |version| + @specific_versions[name][version] ||= Set.new + @specific_versions[name][version] << to_license(lic) + end + + @all_versions.delete(name) + end + + def unassign_from_all_versions(name, lic = nil) + if lic + # Ex: licenses remove foo_gem MIT => Removes MIT at all versions for this gem + @all_versions[name]&.delete(to_license(lic)) + + @specific_versions[name]&.each do |_version, licenses| + licenses.delete(to_license(lic)) + end + else + # Ex: licenses remove foo_gem => Removes all licenses for all versions of the gem + @all_versions.delete(name) + @specific_versions.delete(name) + end + end + + def unassign_from_specific_versions(name, lic, versions) + return unless @specific_versions[name] + + versions.each do |version| + if @specific_versions[name][version] + if lic + # Ex: licenses remove foo_gem MIT --version=1.0 => Removes MIT at only 1.0 for this gem + @specific_versions[name][version].delete(to_license(lic)) + @specific_versions[name].delete(version) if @specific_versions[name][version].empty? + else + # Ex: licenses remove foo_gem --version=1.0 => Removes all licenses at only 1.0 for the gem + @specific_versions[name].delete(version) + end + end + end + end + + private + + def to_license(lic) + License.find_by_name(lic) + end + end +end diff --git a/spec/lib/license_finder/decisions_spec.rb b/spec/lib/license_finder/decisions_spec.rb index dfacbd0af..d4238e82a 100644 --- a/spec/lib/license_finder/decisions_spec.rb +++ b/spec/lib/license_finder/decisions_spec.rb @@ -123,6 +123,14 @@ module LicenseFinder expect(licenses).to eq [License.find_by_name('GPL')].to_set end + it 'will remove the license from all package versions' do + licenses = subject + .license('dep', 'MIT', { versions: ['1.0.0'] }) + .unlicense('dep', 'MIT') + .licenses_of('dep', '1.0.0') + expect(licenses).to be_empty + end + it 'is cumulative' do license = subject .license('dep', 'MIT') @@ -133,32 +141,54 @@ module LicenseFinder expect(license).to eq License.find_by_name('MIT') end - it 'removes the license for the correct version' do - subject.license('dep', 'MIT', { versions: ['1.0.0'] }) - expect(subject.licenses_of('dep', '1.0.0')).to eq [License.find_by_name('MIT')].to_set + it 'removes the license from a specific version' do + subject.license('dep_a', 'MIT') + subject.license('dep_b', 'MIT', { versions: ['1.0.0'] }) + subject.license('dep_b', 'GPL', { versions: ['1.0.0'] }) - subject.unlicense('dep', 'MIT', { versions: [] }) - expect(subject.licenses_of('dep', '1.0.0')).to eq [License.find_by_name('MIT')].to_set + expect(subject.licenses_of('dep_a', '1.0.0')).to eq [License.find_by_name('MIT')].to_set + expect(subject.licenses_of('dep_b', '1.0.0')).to eq [ + License.find_by_name('MIT'), + License.find_by_name('GPL') + ].to_set - subject.unlicense('dep', 'MIT', { versions: ['2.0.0'] }) - expect(subject.licenses_of('dep', '1.0.0')).to eq [License.find_by_name('MIT')].to_set + subject.unlicense('dep_a', 'MIT', { versions: ['1.0.0'] }) + subject.unlicense('dep_b', 'MIT', { versions: ['1.0.0'] }) - subject.unlicense('dep', 'MIT', { versions: ['1.0.0'] }) - expect(subject.licenses_of('dep', '1.0.0')).to be_empty + expect(subject.licenses_of('dep_a', '1.0.0')).to eq [License.find_by_name('MIT')].to_set + expect(subject.licenses_of('dep_b', '1.0.0')).to eq [License.find_by_name('GPL')].to_set end - it 'reverts to the previous license for all versions after unlicensing all specific versions' do - subject.license('dep', 'MIT', { versions: [] }) - expect(subject.licenses_of('dep', '1.0.0')).to eq [License.find_by_name('MIT')].to_set + it 'removes all licenses for all versions when license is omitted' do + subject.license('dep_a', 'MIT') + subject.license('dep_b', 'MIT', { versions: ['1.0.0'] }) - subject.license('dep', 'GPL', { versions: ['1.0.0'] }) - expect(subject.licenses_of('dep', '1.0.0')).to eq [License.find_by_name('GPL')].to_set + expect(subject.licenses_of('dep_a')).to eq [License.find_by_name('MIT')].to_set + expect(subject.licenses_of('dep_b', '1.0.0')).to eq [License.find_by_name('MIT')].to_set - subject.unlicense('dep', 'GPL', { versions: ['1.0.0'] }) - expect(subject.licenses_of('dep', '1.0.0')).to eq [License.find_by_name('MIT')].to_set + subject.unlicense('dep_a', nil, { versions: [] }) + subject.unlicense('dep_b', nil, { versions: [] }) + + expect(subject.licenses_of('dep_a')).to be_empty + expect(subject.licenses_of('dep_b', '1.0.0')).to be_empty + end + + it 'removes all licenses for a specific version when license is omitted' do + subject.license('dep_a', 'MIT') + subject.license('dep_b', 'MIT', { versions: ['1.0.0'] }) + subject.license('dep_b', 'GPL', { versions: ['1.0.0'] }) + + expect(subject.licenses_of('dep_a')).to eq [License.find_by_name('MIT')].to_set + expect(subject.licenses_of('dep_b', '1.0.0')).to eq [ + License.find_by_name('MIT'), + License.find_by_name('GPL') + ].to_set + + subject.unlicense('dep_a', nil, { versions: ['1.0.0'] }) + subject.unlicense('dep_b', nil, { versions: ['1.0.0'] }) - subject.unlicense('dep', 'MIT', { versions: [] }) - expect(subject.licenses_of('dep', '1.0.0')).to be_empty + expect(subject.licenses_of('dep_a')).to eq [License.find_by_name('MIT')].to_set + expect(subject.licenses_of('dep_b', '1.0.0')).to be_empty end end From 3561c8436199bd1b2e97d13ad132fa93a51957eb Mon Sep 17 00:00:00 2001 From: Michael O'Neil Date: Fri, 11 Nov 2022 13:48:03 -0500 Subject: [PATCH 3/3] Update documentation on adding/removing manual licenses --- README.md | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fdd6768c6..8dc450a10 100644 --- a/README.md +++ b/README.md @@ -345,12 +345,40 @@ you should manually research what the actual license is. When you have established the real license, you can record it with: ```sh -$ license_finder licenses add my_unknown_dependency MIT --homepage="www.unknown-code.org" +$ license_finder licenses add my_unknown_dependency MIT ``` -This command would assign the MIT license to the dependency -`my_unknown_dependency`. It will also set its homepage to `www.unknown-code.org`. +This command would assign the MIT license to all versions of the dependency +`my_unknown_dependency`. If you prefer, you could instead assign the license +to only a specific version of the dependency: +```sh +$ license_finder licenses add my_unknown_dependency MIT --version=1.0.0 +``` + +Please note that adding a license to a specific version of a dependency will +cause any licenses previously added to all versions of that dependency to be +forgotten. Similarly, adding a license to all versions of a dependency will +override any licenses previously added to specific versions of that dependency. + +There are several ways in which you can remove licenses that were previously +added through the `licenses add` command: + +```sh +# Removes all licenses from any version of the dependency +$ license_finder licenses remove my_unknown_dependency + +# Removes just the MIT license from any version of the dependency +$ license_finder licenses remove my_unknown_dependency MIT + +# Removes all licenses from only version 1.0.0 of the dependency +# This has no effect if you had last added a license to all versions of the dependency +$ license_finder licenses remove my_unknown_dependency --version=1.0.0 + +# Removes just the MIT license from only version 1.0.0 of the dependency +# This has no effect if you had last added a license to all versions of the dependency +$ license_finder licenses remove my_unknown_dependency MIT --version=1.0.0 +``` ### Adding Hidden Dependencies