/
spdx.rb
177 lines (146 loc) 路 5.22 KB
/
spdx.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# typed: true
# frozen_string_literal: true
require "utils/curl"
require "utils/github"
# Helper module for updating SPDX license data.
#
# @api private
module SPDX
include Utils::Curl
extend Utils::Curl
module_function
DATA_PATH = (HOMEBREW_DATA_PATH/"spdx").freeze
API_URL = "https://api.github.com/repos/spdx/license-list-data/releases/latest"
ALLOWED_LICENSE_SYMBOLS = [
:public_domain,
:cannot_represent,
].freeze
def license_data
@license_data ||= JSON.parse (DATA_PATH/"spdx_licenses.json").read
end
def exception_data
@exception_data ||= JSON.parse (DATA_PATH/"spdx_exceptions.json").read
end
def latest_tag
@latest_tag ||= GitHub.open_api(API_URL)["tag_name"]
end
def download_latest_license_data!(to: DATA_PATH)
data_url = "https://raw.githubusercontent.com/spdx/license-list-data/#{latest_tag}/json/"
curl_download("#{data_url}licenses.json", to: to/"spdx_licenses.json", partial: false)
curl_download("#{data_url}exceptions.json", to: to/"spdx_exceptions.json", partial: false)
end
def parse_license_expression(license_expression)
licenses = T.let([], T::Array[T.any(String, Symbol)])
exceptions = T.let([], T::Array[String])
case license_expression
when String, Symbol
licenses.push license_expression
when Hash, Array
if license_expression.is_a? Hash
license_expression = license_expression.map do |key, value|
if key.is_a? String
licenses.push key
exceptions.push value[:with]
next
end
value
end.compact
end
license_expression.each do |license|
sub_license, sub_exception = parse_license_expression license
licenses += sub_license
exceptions += sub_exception
end
end
[licenses, exceptions]
end
def valid_license?(license)
return ALLOWED_LICENSE_SYMBOLS.include? license if license.is_a? Symbol
license = license.delete_suffix "+"
license_data["licenses"].any? { |spdx_license| spdx_license["licenseId"] == license }
end
def deprecated_license?(license)
return false if ALLOWED_LICENSE_SYMBOLS.include? license
return false unless valid_license?(license)
license_data["licenses"].none? do |spdx_license|
spdx_license["licenseId"] == license && !spdx_license["isDeprecatedLicenseId"]
end
end
def valid_license_exception?(exception)
exception_data["exceptions"].any? do |spdx_exception|
spdx_exception["licenseExceptionId"] == exception && !spdx_exception["isDeprecatedLicenseId"]
end
end
def license_expression_to_string(license_expression, bracket: false, hash_type: nil)
case license_expression
when String
license_expression
when Symbol
license_expression.to_s.tr("_", " ").titleize
when Hash
expressions = []
if license_expression.keys.length == 1
hash_type = license_expression.keys.first
if hash_type.is_a? String
expressions.push "#{hash_type} with #{license_expression[hash_type][:with]}"
else
expressions += license_expression[hash_type].map do |license|
license_expression_to_string license, bracket: true, hash_type: hash_type
end
end
else
bracket = false
license_expression.each do |expression|
expressions.push license_expression_to_string(Hash[*expression], bracket: true)
end
end
operator = if hash_type == :any_of
" or "
else
" and "
end
if bracket
"(#{expressions.join operator})"
else
expressions.join operator
end
end
end
def license_version_info(license)
return [license] if ALLOWED_LICENSE_SYMBOLS.include? license
match = license.match(/-(?<version>[0-9.]+)(?:-.*?)??(?<or_later>\+|-only|-or-later)?$/)
return [license] if match.blank?
license_name = license.split(match[0]).first
or_later = match["or_later"].present? && %w[+ -or-later].include?(match["or_later"])
# [name, version, later versions allowed?]
# e.g. GPL-2.0-or-later --> ["GPL", "2.0", true]
[license_name, match["version"], or_later]
end
def licenses_forbid_installation?(license_expression, forbidden_licenses)
case license_expression
when String, Symbol
forbidden_licenses_include? license_expression.to_s, forbidden_licenses
when Hash
key = license_expression.keys.first
case key
when :any_of
license_expression[key].all? { |license| licenses_forbid_installation? license, forbidden_licenses }
when :all_of
license_expression[key].any? { |license| licenses_forbid_installation? license, forbidden_licenses }
else
forbidden_licenses_include? key, forbidden_licenses
end
end
end
def forbidden_licenses_include?(license, forbidden_licenses)
return true if forbidden_licenses.key? license
name, version, = license_version_info license
forbidden_licenses.each do |_, license_info|
forbidden_name, forbidden_version, forbidden_or_later = *license_info
next unless forbidden_name == name
return true if forbidden_or_later && forbidden_version <= version
return true if forbidden_version == version
end
false
end
end