Skip to content

Commit

Permalink
Merge pull request #953 from mtoneil/add-license-version-option
Browse files Browse the repository at this point in the history
Add ability to specify package version when setting a manual license
  • Loading branch information
xtreme-shane-lattanzio authored Nov 22, 2022
2 parents bdf82bb + 3561c84 commit 2d2e5d4
Show file tree
Hide file tree
Showing 8 changed files with 310 additions and 18 deletions.
34 changes: 31 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
64 changes: 58 additions & 6 deletions features/features/configure/assign_licenses_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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/
Expand All @@ -22,16 +23,67 @@

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_a.*Default/
expect(developer).to be_seeing_something_like /mislicensed_dep_b.*Manual_license_b/

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_a.*Default/
expect(developer).to be_seeing_something_like /mislicensed_dep_b.*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'
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'
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'
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_something_like /mislicensed_dep.*Manual_license/
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 mislicensed_dep Manual_license'
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_something_like /mislicensed_dep.*Default/
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
11 changes: 8 additions & 3 deletions lib/license_finder/cli/licenses.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,24 @@ 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
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) }

printer.say "The dependency #{dep} no longer has a manual license"
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
Expand Down
2 changes: 1 addition & 1 deletion lib/license_finder/decision_applier.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
27 changes: 22 additions & 5 deletions lib/license_finder/decisions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

require 'open-uri'
require 'license_finder/license'
require 'license_finder/manual_licenses'

module LicenseFinder
class Decisions
Expand All @@ -11,8 +12,8 @@ class Decisions

attr_reader :packages, :permitted, :restricted, :ignored, :ignored_groups, :project_name, :inherited_decisions

def licenses_of(name)
@licenses[name]
def licenses_of(name, version = nil)
@manual_licenses.licenses_of(name, version)
end

def homepage_of(name)
Expand Down Expand Up @@ -76,7 +77,7 @@ def self.from_hash(txn, versions)
def initialize
@decisions = []
@packages = Set.new
@licenses = Hash.new { |h, k| h[k] = Set.new }
@manual_licenses = ManualLicenses.new
@homepages = {}
@approvals = {}
@permitted = Set.new
Expand All @@ -100,13 +101,29 @@ 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]

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
end

def unlicense(name, lic, txn = {})
add_decision [:unlicense, name, lic, txn]
@licenses[name].delete(License.find_by_name(lic))

versions = txn[:versions]

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
end

Expand Down
79 changes: 79 additions & 0 deletions lib/license_finder/manual_licenses.rb
Original file line number Diff line number Diff line change
@@ -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
10 changes: 10 additions & 0 deletions spec/lib/license_finder/decision_applier_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading

0 comments on commit 2d2e5d4

Please sign in to comment.