Merge pull request #113 from benbalter/lgpl

Special case LICENSE.lesser to handle LGPL implementation instructions
This commit is contained in:
Ben Balter 2016-09-15 09:12:45 -04:00 committed by GitHub
commit 62b37a8b43
17 changed files with 115 additions and 24 deletions

View File

@ -111,6 +111,10 @@ module Licensee
meta['hidden']
end
def gpl?
key == 'gpl-2.0' || key == 'gpl-3.0'
end
# The license body (e.g., contents - frontmatter)
def content
@content ||= parts[2] if parts && parts[2]

View File

@ -23,8 +23,16 @@ module Licensee
def license_file
return @license_file if defined? @license_file
@license_file = begin
content, name = find_file { |n| LicenseFile.name_score(n) }
LicenseFile.new(content, name) if content && name
license_file = license_from_file { |n| LicenseFile.name_score(n) }
return license_file unless license_file && license_file.license
# Special case LGPL, which actuall lives in LICENSE.lesser, per the
# license instructions. See https://git.io/viwyK
lesser = if license_file.license.gpl?
license_from_file { |file| LicenseFile.lesser_gpl_score(file) }
end
lesser || license_file
end
end
@ -46,5 +54,31 @@ module Licensee
PackageInfo.new(content, name) if content && name
end
end
private
# Given a block, passes each filename to that block, and expects a numeric
# score in response. Returns an array of all files with a score > 0,
# sorted by file score descending
def find_files
return [] if files.empty? || files.nil?
found = files.each { |file| file[:score] = yield(file[:name]) }
found.select! { |file| file[:score] > 0 }
found.sort { |a, b| b[:score] <=> a[:score] }
end
# Given a block, passes each filename to that block, and expects a numeric
# score in response. Returns a hash representing the top scoring file
# or nil, if no file scored > 0
def find_file(&block)
return if files.empty? || files.nil?
file = find_files(&block).first
[load_file(file), file[:name]] if file
end
def license_from_file(&block)
content, name = find_file(&block)
LicenseFile.new(content, name) if content && name
end
end
end

View File

@ -20,6 +20,11 @@ module Licensee
return 0.5 if filename =~ /licen[sc]e/i
0.0
end
# case-insensitive block to determine if the given file is LICENSE.lesser
def self.lesser_gpl_score(filename)
filename.casecmp('copying.lesser').zero? ? 1 : 0
end
end
end
end

View File

@ -12,7 +12,9 @@ module Licensee
private
def find_file
# Returns an array of hashes representing the project's files.
# Hashes will have the :name key, with the relative path to the file
def files
files = []
if ::File.file?(path)
@ -24,17 +26,20 @@ module Licensee
Dir.glob(::File.join(path, pattern)) do |file|
next unless ::File.file?(file)
file = ::File.basename(file)
if (score = yield file) > 0
files.push(name: file, score: score)
end
files.push(name: ::File.basename(file))
end
return if files.empty?
files.sort! { |a, b| b[:score] <=> a[:score] }
files
end
f = files.first
[::File.read(::File.join(path, f[:name])), f[:name]]
# Retrieve a file's content from disk
#
# file - the file hash, with the :name key as the file's relative path
# path - the base path to the project
#
# Returns the fiel contents as a string
def load_file(file)
::File.read(::File.join(path, file[:name]))
end
end
end

View File

@ -36,24 +36,25 @@ module Licensee
MAX_LICENSE_SIZE = 64 * 1024
def load_blob_data(oid)
data, = Rugged::Blob.to_buffer(repository, oid, MAX_LICENSE_SIZE)
# Retrieve a file's content from the Git database
#
# file - the file hash, including the file's OID
#
# Returns a string representing the file's contents
def load_file(file)
data, = Rugged::Blob.to_buffer(repository, file[:oid], MAX_LICENSE_SIZE)
data
end
def find_file
files = commit.tree.map do |entry|
# Returns an array of hashes representing the project's files.
# Hashes will have the the following keys:
# :name - the file's path relative to the repo root
# :oid - the file's OID
def files
commit.tree.map do |entry|
next unless entry[:type] == :blob
if (score = yield entry[:name]) > 0
{ name: entry[:name], oid: entry[:oid], score: score }
end
{ name: entry[:name], oid: entry[:oid] }
end.compact
return if files.empty?
files.sort! { |a, b| b[:score] <=> a[:score] }
f = files.first
[load_blob_data(f[:oid]), f[:name]]
end
end
end

1
test/fixtures/lgpl.git/HEAD vendored Normal file
View File

@ -0,0 +1 @@
ref: refs/heads/master

5
test/fixtures/lgpl.git/config vendored Normal file
View File

@ -0,0 +1,5 @@
[core]
repositoryformatversion = 0
filemode = true
bare = true
ignorecase = true

View File

@ -0,0 +1,2 @@
x<01>ŽQjÃ0­b${åHPJñMVëc°å`+…Ü>Né ú7oà £ÛºÎ•ZŽuHX³p<34>ܵYwê•Ñå6r¶Öç±3wÙQ*qr9uˆ>õ#<23>¬¸Þfï9!^á%CaäQoÛN
²Tìô™Pšô¿§¹Þ©Ñmý"Ç×.„>„HËÖš³=?¾•ÿØfý¡eV”Íã8gt»?ç2ý¡yÊ5Q}

View File

@ -0,0 +1,2 @@
x<01><>[
Â0EýÎ*fÉC“BéNfš‰ 4 „tÿVÝ<56> 瞥–’;è`/½‰@ŒµgÍÉŽFº ¹dÅhFòBAXÐ*:úZ̲ÃL[—Ë>𗟯Ü׃‡¥–hçmÎß\Ñ!ªs=å[å=÷LünÔ7®<<3C>

View File

@ -0,0 +1 @@
1acd060ebbbeac294200008657d9502130f93465

View File

@ -55,4 +55,19 @@ class TestLicenseeLicenseFile < Minitest::Test
end
end
end
context 'LGPL scoring' do
{
'COPYING.lesser' => 1,
'copying.lesser' => 1,
'license.lesser' => 0,
'LICENSE.md' => 0,
'FOO.md' => 0
}.each do |filename, expected|
should "score a license named `#{filename}` as `#{expected}`" do
score = Licensee::Project::LicenseFile.lesser_gpl_score(filename)
assert_equal expected, score
end
end
end
end

View File

@ -117,6 +117,17 @@ class TestLicenseeLicense < Minitest::Test
assert_equal '750260c322080bab4c19fd55eb78bc73e1ae8f11', @license.hash
end
should "recognize GPL'd licenses" do
license = Licensee::License.new 'gpl-2.0'
assert license.gpl?
license = Licensee::License.new 'gpl-3.0'
assert license.gpl?
license = Licensee::License.new 'mit'
refute license.gpl?
end
describe 'name without version' do
should 'strip the version from the license name' do
expected = 'GNU Affero General Public License'

View File

@ -72,6 +72,11 @@ class TestLicenseeProject < Minitest::Test
project = make_project 'no-license.git'
assert_equal nil, project.license
end
should 'detect an LGPL licensed project with LICENSE.lesser' do
project = make_project 'lgpl.git'
assert_equal 'lgpl-3.0', project.license.key
end
end
end