licensee 9.10.1 → 9.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/lib/licensee/commands/diff.rb +2 -7
  3. data/lib/licensee/content_helper.rb +169 -78
  4. data/lib/licensee/license.rb +5 -4
  5. data/lib/licensee/license_field.rb +1 -1
  6. data/lib/licensee/matchers/cabal.rb +1 -1
  7. data/lib/licensee/matchers/cargo.rb +1 -1
  8. data/lib/licensee/matchers/copyright.rb +2 -2
  9. data/lib/licensee/matchers/cran.rb +3 -3
  10. data/lib/licensee/matchers/dist_zilla.rb +1 -1
  11. data/lib/licensee/matchers/gemspec.rb +5 -5
  12. data/lib/licensee/matchers/matcher.rb +1 -1
  13. data/lib/licensee/matchers/npm_bower.rb +1 -1
  14. data/lib/licensee/matchers/reference.rb +1 -1
  15. data/lib/licensee/matchers/spdx.rb +1 -1
  16. data/lib/licensee/project_files/license_file.rb +7 -7
  17. data/lib/licensee/project_files/readme_file.rb +3 -3
  18. data/lib/licensee/projects/github_project.rb +1 -1
  19. data/lib/licensee/version.rb +1 -1
  20. data/spec/fixtures/detect.json +2 -2
  21. data/spec/fixtures/license-hashes.json +36 -0
  22. data/spec/licensee/commands/detect_spec.rb +1 -1
  23. data/spec/licensee/content_helper_spec.rb +131 -32
  24. data/spec/licensee/license_spec.rb +1 -1
  25. data/spec/licensee/matchers/dice_matcher_spec.rb +2 -2
  26. data/spec/licensee/project_files/license_file_spec.rb +1 -1
  27. data/spec/spec_helper.rb +10 -5
  28. data/spec/vendored_license_spec.rb +19 -2
  29. data/vendor/choosealicense.com/_data/meta.yml +0 -4
  30. data/vendor/choosealicense.com/_licenses/afl-3.0.txt +0 -1
  31. data/vendor/choosealicense.com/_licenses/agpl-3.0.txt +0 -1
  32. data/vendor/choosealicense.com/_licenses/apache-2.0.txt +0 -1
  33. data/vendor/choosealicense.com/_licenses/artistic-2.0.txt +0 -1
  34. data/vendor/choosealicense.com/_licenses/bsd-2-clause.txt +8 -6
  35. data/vendor/choosealicense.com/_licenses/bsd-3-clause-clear.txt +0 -1
  36. data/vendor/choosealicense.com/_licenses/bsd-3-clause.txt +11 -9
  37. data/vendor/choosealicense.com/_licenses/bsl-1.0.txt +0 -1
  38. data/vendor/choosealicense.com/_licenses/cc-by-4.0.txt +0 -1
  39. data/vendor/choosealicense.com/_licenses/cc-by-sa-4.0.txt +0 -1
  40. data/vendor/choosealicense.com/_licenses/cc0-1.0.txt +0 -1
  41. data/vendor/choosealicense.com/_licenses/ecl-2.0.txt +0 -1
  42. data/vendor/choosealicense.com/_licenses/epl-1.0.txt +0 -1
  43. data/vendor/choosealicense.com/_licenses/epl-2.0.txt +0 -1
  44. data/vendor/choosealicense.com/_licenses/eupl-1.1.txt +0 -1
  45. data/vendor/choosealicense.com/_licenses/eupl-1.2.txt +0 -1
  46. data/vendor/choosealicense.com/_licenses/gpl-2.0.txt +0 -1
  47. data/vendor/choosealicense.com/_licenses/gpl-3.0.txt +1 -2
  48. data/vendor/choosealicense.com/_licenses/isc.txt +0 -1
  49. data/vendor/choosealicense.com/_licenses/lgpl-2.1.txt +0 -1
  50. data/vendor/choosealicense.com/_licenses/lgpl-3.0.txt +1 -2
  51. data/vendor/choosealicense.com/_licenses/lppl-1.3c.txt +0 -1
  52. data/vendor/choosealicense.com/_licenses/mit.txt +0 -1
  53. data/vendor/choosealicense.com/_licenses/mpl-2.0.txt +0 -1
  54. data/vendor/choosealicense.com/_licenses/ms-pl.txt +0 -1
  55. data/vendor/choosealicense.com/_licenses/ms-rl.txt +0 -1
  56. data/vendor/choosealicense.com/_licenses/ncsa.txt +0 -1
  57. data/vendor/choosealicense.com/_licenses/ofl-1.1.txt +0 -1
  58. data/vendor/choosealicense.com/_licenses/osl-3.0.txt +0 -1
  59. data/vendor/choosealicense.com/_licenses/postgresql.txt +2 -3
  60. data/vendor/choosealicense.com/_licenses/unlicense.txt +0 -1
  61. data/vendor/choosealicense.com/_licenses/upl-1.0.txt +0 -1
  62. data/vendor/choosealicense.com/_licenses/wtfpl.txt +0 -1
  63. data/vendor/choosealicense.com/_licenses/zlib.txt +0 -1
  64. metadata +4 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 354a3c318aa962a9c184c71850ba04574618b17f28582f92660a69e5405218ee
4
- data.tar.gz: 1a96874eaaff65c949ca73261aced972cf20ab53d67da7b42d887ac360810eaf
3
+ metadata.gz: 613429f0350489c8dbdbdc80973d2557ad655efd2b89786096f290cf52632344
4
+ data.tar.gz: 87fca6ceddb6972dd946720355e6efb2661793d671ff92ce8934e281affa6ad5
5
5
  SHA512:
6
- metadata.gz: 227b37abb076fd1d76d9fb296056bdf16b5a24b00ef2e95a0c8a883c7be6dee011bbea1df834b20d6ffe331815cc22f4726c94c1894400d04ae32944f01622dd
7
- data.tar.gz: 41582f813fd9e57fa3c497c7edfd11fc447a131e7414ba7a1c3c25266239ec2efb6b867b84adffac704a5ba96e992b72d3f87215222e0aca32f40305e285cc2d
6
+ metadata.gz: 7bcb1c30bdebf4cad0a84ef7fd9dd4afe922ba8acac1a9124bdd96dc64d4ef736787f1b70c4ab0d01c3c05ba3547300caa3c882b2d99ea15117f72335f56b0a5
7
+ data.tar.gz: bb2f30c4f70a4610f55a530c36b65852270558b8b36342b1fe8ad1113afa4dca711214545b3abbeb19cdba8d7e2d05643b0e138c8964a5b3f51106ce8c1bf3db
@@ -39,14 +39,9 @@ class LicenseeCLI < Thor
39
39
 
40
40
  def license_to_diff
41
41
  return options[:license_to_diff] if options[:license_to_diff]
42
- return project.license_file if remote?
42
+ return project.license_file if remote? || STDIN.tty? && project.license_file
43
43
 
44
44
  @license_to_diff ||= begin
45
- if STDIN.tty?
46
- error 'You must pipe license contents to the command via STDIN'
47
- exit 1
48
- end
49
-
50
45
  Licensee::ProjectFiles::LicenseFile.new(STDIN.read, 'LICENSE')
51
46
  end
52
47
  end
@@ -58,7 +53,7 @@ class LicenseeCLI < Thor
58
53
  if options[:license]
59
54
  error "#{options[:license]} is not a valid license"
60
55
  else
61
- error 'You must provide an expected license'
56
+ error 'Usage: provide a license to diff against with --license (spdx name)'
62
57
  end
63
58
 
64
59
  error "Valid licenses: #{Licensee::License.all(hidden: true).map(&:key).join(', ')}"
@@ -4,17 +4,88 @@ require 'digest'
4
4
  module Licensee
5
5
  module ContentHelper
6
6
  DIGEST = Digest::SHA1
7
- END_OF_TERMS_REGEX = /^[\s#*_]*end of terms and conditions\s*$/i
8
- HR_REGEX = /[=\-\*][=\-\*\s]{3,}/
7
+ START_REGEX = /\A\s*/.freeze
8
+ END_OF_TERMS_REGEX = /^[\s#*_]*end of terms and conditions\s*$/i.freeze
9
9
  ALT_TITLE_REGEX = License::ALT_TITLE_REGEX
10
- ALL_RIGHTS_RESERVED_REGEX = /\Aall rights reserved\.?$/i
11
- WHITESPACE_REGEX = /\s+/
12
- MARKDOWN_HEADING_REGEX = /\A\s*#+/
13
- VERSION_REGEX = /\Aversion.*$/i
14
- MARKUP_REGEX = /[#_*=~\[\]()`|>]+/
15
- DEVELOPED_BY_REGEX = /\Adeveloped by:.*?\n\n/im
16
- QUOTE_BEGIN_REGEX = /[`'"‘“]/
17
- QUOTE_END_REGEX = /['"’”]/
10
+ REGEXES = {
11
+ hrs: /^\s*[=\-\*]{3,}\s*$/,
12
+ all_rights_reserved: /#{START_REGEX}all rights reserved\.?$/i,
13
+ whitespace: /\s+/,
14
+ markdown_headings: /#{START_REGEX}#+/,
15
+ version: /#{START_REGEX}version.*$/i,
16
+ span_markup: /[_*~]+(.*?)[_*~]+/,
17
+ link_markup: /\[(.+?)\]\(.+?\)/,
18
+ block_markup: /^\s*>/,
19
+ border_markup: /^[\*-](.*?)[\*-]$/,
20
+ comment_markup: %r{^\s*?[/\*]{1,2}},
21
+ url: %r{#{START_REGEX}https?://[^ ]+\n},
22
+ bullet: /\n\n\s*(?:[*-]|\(?[\da-z]{1,2}[)\.])\s+/i,
23
+ developed_by: /#{START_REGEX}developed by:.*?\n\n/im,
24
+ quote_begin: /[`'"‘“]/,
25
+ quote_end: /[`'"’”]/
26
+ }.freeze
27
+ NORMALIZATIONS = {
28
+ lists: { from: /^\s*(?:\d\.|\*)\s+([^\n])/, to: '- \1' },
29
+ https: { from: /http:/, to: 'https:' },
30
+ ampersands: { from: '&', to: 'and' },
31
+ dashes: { from: /(?<!^)([—–-]+)(?!$)/, to: '-' },
32
+ quotes: {
33
+ from: /#{REGEXES[:quote_begin]}+([\w -]*?\w)#{REGEXES[:quote_end]}+/,
34
+ to: '"\1"'
35
+ }
36
+ }.freeze
37
+
38
+ # Legally equivalent words that schould be ignored for comparison
39
+ # See https://spdx.org/spdx-license-list/matching-guidelines
40
+ VARIETAL_WORDS = {
41
+ 'acknowledgment' => 'acknowledgement',
42
+ 'analogue' => 'analog',
43
+ 'analyse' => 'analyze',
44
+ 'artefact' => 'artifact',
45
+ 'authorisation' => 'authorization',
46
+ 'authorised' => 'authorized',
47
+ 'calibre' => 'caliber',
48
+ 'cancelled' => 'canceled',
49
+ 'capitalisations' => 'capitalizations',
50
+ 'catalogue' => 'catalog',
51
+ 'categorise' => 'categorize',
52
+ 'centre' => 'center',
53
+ 'emphasised' => 'emphasized',
54
+ 'favour' => 'favor',
55
+ 'favourite' => 'favorite',
56
+ 'fulfil' => 'fulfill',
57
+ 'fulfilment' => 'fulfillment',
58
+ 'initialise' => 'initialize',
59
+ 'judgment' => 'judgement',
60
+ 'labelling' => 'labeling',
61
+ 'labour' => 'labor',
62
+ 'licence' => 'license',
63
+ 'maximise' => 'maximize',
64
+ 'modelled' => 'modeled',
65
+ 'modelling' => 'modeling',
66
+ 'offence' => 'offense',
67
+ 'optimise' => 'optimize',
68
+ 'organisation' => 'organization',
69
+ 'organise' => 'organize',
70
+ 'practise' => 'practice',
71
+ 'programme' => 'program',
72
+ 'realise' => 'realize',
73
+ 'recognise' => 'recognize',
74
+ 'signalling' => 'signaling',
75
+ 'sub-license' => 'sublicense',
76
+ 'sub license' => 'sublicense',
77
+ 'utilisation' => 'utilization',
78
+ 'whilst' => 'while',
79
+ 'wilful' => 'wilfull',
80
+ 'non-commercial' => 'noncommercial',
81
+ 'cent' => 'percent',
82
+ 'owner' => 'holder'
83
+ }.freeze
84
+ STRIP_METHODS = %i[
85
+ hrs markdown_headings borders title version url copyright
86
+ block_markup span_markup link_markup
87
+ all_rights_reserved developed_by end_of_terms whitespace
88
+ ].freeze
18
89
 
19
90
  # A set of each word in the license, without duplicates
20
91
  def wordset
@@ -60,35 +131,21 @@ module Licensee
60
131
  # content with attribution first to detect attribuion in LicenseFile
61
132
  def content_without_title_and_version
62
133
  @content_without_title_and_version ||= begin
63
- string = content.strip
64
- string = strip_markdown_headings(string)
65
- string = strip_hrs(string)
66
- string = strip_title(string) while string =~ ContentHelper.title_regex
67
- strip_version(string).strip
134
+ @_content = nil
135
+ ops = %i[hrs comments markdown_headings title version]
136
+ ops.each { |op| strip(op) }
137
+ _content
68
138
  end
69
139
  end
70
140
 
71
- # Content without title, version, copyright, whitespace, or insturctions
72
- #
73
- # wrap - Optional width to wrap the content
74
- #
75
- # Returns a string
76
141
  def content_normalized(wrap: nil)
77
- return unless content
78
-
79
142
  @content_normalized ||= begin
80
- string = content_without_title_and_version.downcase
81
- while string =~ Matchers::Copyright::REGEX
82
- string = strip_copyright(string)
83
- end
84
- string = strip_all_rights_reserved(string)
85
- string = strip_developed_by(string)
86
- string, _partition, _instructions = string.partition(END_OF_TERMS_REGEX)
87
- string = normalize_lists(string)
88
- string = normalize_quotes(string)
89
- string = normalize_https(string)
90
- string = strip_markup(string)
91
- strip_whitespace(string)
143
+ @_content = content_without_title_and_version.downcase
144
+
145
+ (NORMALIZATIONS.keys + %i[spelling bullets]).each { |op| normalize(op) }
146
+ STRIP_METHODS.each { |op| strip(op) }
147
+
148
+ _content
92
149
  end
93
150
 
94
151
  if wrap.nil?
@@ -98,15 +155,24 @@ module Licensee
98
155
  end
99
156
  end
100
157
 
158
+ # Backwards compatibalize constants to avoid a breaking change
159
+ def self.const_missing(const)
160
+ key = const.to_s.downcase.gsub('_regex', '').to_sym
161
+ REGEXES[key] || super
162
+ end
163
+
101
164
  # Wrap text to the given line length
102
165
  def self.wrap(text, line_width = 80)
103
166
  return if text.nil?
104
167
 
105
168
  text = text.clone
169
+ text.gsub!(REGEXES[:bullet]) { |m| "\n#{m}\n" }
106
170
  text.gsub!(/([^\n])\n([^\n])/, '\1 \2')
107
171
 
108
172
  text = text.split("\n").collect do |line|
109
- if line.length > line_width
173
+ if line =~ REGEXES[:hrs]
174
+ line
175
+ elsif line.length > line_width
110
176
  line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1\n").strip
111
177
  else
112
178
  line
@@ -121,79 +187,104 @@ module Licensee
121
187
  end
122
188
 
123
189
  def self.title_regex
124
- licenses = Licensee::License.all(hidden: true, psuedo: false)
125
- titles = licenses.map(&:title_regex)
190
+ @title_regex ||= begin
191
+ licenses = Licensee::License.all(hidden: true, psuedo: false)
192
+ titles = licenses.map(&:title_regex)
126
193
 
127
- # Title regex must include the version to support matching within
128
- # families, but for sake of normalization, we can be less strict
129
- without_versions = licenses.map do |license|
130
- next if license.title == license.name_without_version
194
+ # Title regex must include the version to support matching within
195
+ # families, but for sake of normalization, we can be less strict
196
+ without_versions = licenses.map do |license|
197
+ next if license.title == license.name_without_version
131
198
 
132
- Regexp.new Regexp.escape(license.name_without_version), 'i'
133
- end
134
- titles.concat(without_versions.compact)
199
+ Regexp.new Regexp.escape(license.name_without_version), 'i'
200
+ end
201
+ titles.concat(without_versions.compact)
135
202
 
136
- /\A\s*\(?(the )?#{Regexp.union titles}.*$/i
203
+ /#{START_REGEX}\(?(?:the )?#{Regexp.union titles}.*?$/i
204
+ end
137
205
  end
138
206
 
139
207
  private
140
208
 
141
- def strip_title(string)
142
- strip(string, ContentHelper.title_regex)
209
+ # rubocop:disable Naming/MemoizedInstanceVariableName
210
+ def _content
211
+ @_content ||= content.to_s.dup.strip
143
212
  end
213
+ # rubocop:enable Naming/MemoizedInstanceVariableName
144
214
 
145
- def strip_version(string)
146
- strip(string, VERSION_REGEX)
147
- end
215
+ def strip(regex_or_sym)
216
+ return unless _content
148
217
 
149
- def strip_copyright(string)
150
- strip(string, Matchers::Copyright::REGEX)
218
+ if regex_or_sym.is_a?(Symbol)
219
+ meth = "strip_#{regex_or_sym}"
220
+ return send(meth) if respond_to?(meth, true)
221
+
222
+ unless REGEXES[regex_or_sym]
223
+ raise ArgumentError, "#{regex_or_sym} is an invalid regex reference"
224
+ end
225
+
226
+ regex_or_sym = REGEXES[regex_or_sym]
227
+ end
228
+
229
+ @_content = _content.gsub(regex_or_sym, ' ').squeeze(' ').strip
151
230
  end
152
231
 
153
- # Strip HRs from MPL
154
- def strip_hrs(string)
155
- strip(string, HR_REGEX)
232
+ def strip_title
233
+ while _content =~ ContentHelper.title_regex
234
+ strip(ContentHelper.title_regex)
235
+ end
156
236
  end
157
237
 
158
- # Strip leading #s from the document
159
- def strip_markdown_headings(string)
160
- strip(string, MARKDOWN_HEADING_REGEX)
238
+ def strip_borders
239
+ normalize(REGEXES[:border_markup], '\1')
161
240
  end
162
241
 
163
- def strip_whitespace(string)
164
- strip(string, WHITESPACE_REGEX)
242
+ def strip_comments
243
+ lines = _content.split("\n")
244
+ return if lines.count == 1
245
+ return unless lines.all? { |line| line =~ REGEXES[:comment_markup] }
246
+
247
+ strip(:comment_markup)
165
248
  end
166
249
 
167
- def strip_all_rights_reserved(string)
168
- strip(string, ALL_RIGHTS_RESERVED_REGEX)
250
+ def strip_copyright
251
+ regex = Matchers::Copyright::REGEX
252
+ strip(regex) while _content =~ regex
169
253
  end
170
254
 
171
- def strip_markup(string)
172
- strip(string, MARKUP_REGEX)
255
+ def strip_end_of_terms
256
+ body, _partition, _instructions = _content.partition(END_OF_TERMS_REGEX)
257
+ @_content = body
173
258
  end
174
259
 
175
- def strip_developed_by(string)
176
- strip(string, DEVELOPED_BY_REGEX)
260
+ def strip_span_markup
261
+ normalize(REGEXES[:span_markup], '\1')
177
262
  end
178
263
 
179
- def strip(string, regex)
180
- string.gsub(regex, ' ').squeeze(' ').strip
264
+ def strip_link_markup
265
+ normalize(REGEXES[:link_markup], '\1')
181
266
  end
182
267
 
183
- # Replace all enclosing quotes with double quotes
184
- # Single versus double quotes don't alter the meaning, and it's easier to
185
- # strip double quotes if we still want to allow possessives
186
- def normalize_quotes(string)
187
- string.gsub(/#{QUOTE_BEGIN_REGEX}+([\w -]*?\w)#{QUOTE_END_REGEX}+/,
188
- '"\1"')
268
+ def normalize(from_or_key, to = nil)
269
+ operation = { from: from_or_key, to: to } if to
270
+ operation ||= NORMALIZATIONS[from_or_key]
271
+
272
+ if operation
273
+ @_content = _content.gsub operation[:from], operation[:to]
274
+ elsif respond_to?("normalize_#{from_or_key}", true)
275
+ send("normalize_#{from_or_key}")
276
+ else
277
+ raise ArgumentError, "#{from_or_key} is an invalid normalization"
278
+ end
189
279
  end
190
280
 
191
- def normalize_https(string)
192
- string.gsub(/http:/, 'https:')
281
+ def normalize_spelling
282
+ normalize(/\b#{Regexp.union(VARIETAL_WORDS.keys)}\b/, VARIETAL_WORDS)
193
283
  end
194
284
 
195
- def normalize_lists(string)
196
- string.gsub(/^\s*(\d\.|\*)/, '-')
285
+ def normalize_bullets
286
+ normalize(REGEXES[:bullet], "\n\n* ")
287
+ normalize(/\)\s+\(/, ')(')
197
288
  end
198
289
  end
199
290
  end
@@ -23,6 +23,7 @@ module Licensee
23
23
  output = licenses.dup
24
24
  output.reject!(&:hidden?) unless options[:hidden]
25
25
  output.reject!(&:pseudo_license?) unless options[:pseudo]
26
+ output.sort_by!(&:key)
26
27
  return output if options[:featured].nil?
27
28
 
28
29
  output.select { |l| l.featured? == options[:featured] }
@@ -95,8 +96,8 @@ module Licensee
95
96
  'bsd-3-clause-clear' => /(?:clear bsd|bsd 3-clause(?: clear)?)/i
96
97
  }.freeze
97
98
 
98
- SOURCE_PREFIX = %r{https?://(?:www\.)?}i
99
- SOURCE_SUFFIX = %r{(?:\.html?|\.txt|\/)(?:\?[^\s]*)?}i
99
+ SOURCE_PREFIX = %r{https?://(?:www\.)?}i.freeze
100
+ SOURCE_SUFFIX = %r{(?:\.html?|\.txt|\/)(?:\?[^\s]*)?}i.freeze
100
101
 
101
102
  HASH_METHODS = %i[
102
103
  key spdx_id meta url rules fields other? gpl? lgpl? cc?
@@ -145,11 +146,11 @@ module Licensee
145
146
  title_regex ||= begin
146
147
  string = name.downcase.sub('*', 'u')
147
148
  string.sub!(/\Athe /i, '')
148
- string.sub!(/ license\z/i, '')
149
149
  string.sub!(/,? version /, ' ')
150
150
  string.sub!(/v(\d+\.\d+)/, '\1')
151
151
  string = Regexp.escape(string)
152
- string = string.sub(/ (\d+\\.\d+)/, ',?\ (?:version\ |v)?\1')
152
+ string = string.sub(/\\ licen[sc]e/i, '(?:\ licen[sc]e)?')
153
+ string = string.sub(/\\ (\d+\\.\d+)/, ',?\s+(?:version\ |v(?:\. )?)?\1')
153
154
  string = string.sub(/\bgnu\\ /, '(?:GNU )?')
154
155
  Regexp.new string, 'i'
155
156
  end
@@ -45,7 +45,7 @@ module Licensee
45
45
  end
46
46
 
47
47
  alias key name
48
- FIELD_REGEX = /\[(#{Regexp.union(LicenseField.keys)})\]/
48
+ FIELD_REGEX = /\[(#{Regexp.union(LicenseField.keys)})\]/.freeze
49
49
 
50
50
  # The human-readable field name
51
51
  def label
@@ -3,7 +3,7 @@ module Licensee
3
3
  class Cabal < Licensee::Matchers::Package
4
4
  # While we could parse the cabal file, prefer
5
5
  # a lenient regex for speed and security. Moar parsing moar problems.
6
- LICENSE_REGEX = /^\s*license\s*\:\s*([a-z\-0-9\.]+)\s*$/ix
6
+ LICENSE_REGEX = /^\s*license\s*\:\s*([a-z\-0-9\.]+)\s*$/ix.freeze
7
7
 
8
8
  private
9
9
 
@@ -3,7 +3,7 @@ module Licensee
3
3
  class Cargo < Licensee::Matchers::Package
4
4
  LICENSE_REGEX = %r{
5
5
  ^\s*[\'\"]?license[\'\"]?\s*=\s*[\'\"]([a-z\-0-9\. +()\/]+)[\'\"]\s*
6
- }ix
6
+ }ix.freeze
7
7
 
8
8
  private
9
9
 
@@ -5,12 +5,12 @@ module Licensee
5
5
 
6
6
  # rubocop:disable Metrics/LineLength
7
7
  COPYRIGHT_SYMBOLS = Regexp.union([/copyright/i, /\(c\)/i, "\u00A9", "\xC2\xA9"])
8
- REGEX = /\A(\s*#{COPYRIGHT_SYMBOLS}.*$)+/i
8
+ REGEX = /#{ContentHelper::START_REGEX}(?:portions )?(\s*#{COPYRIGHT_SYMBOLS}.*$)+$/i.freeze
9
9
  # rubocop:enable Metrics/LineLength
10
10
 
11
11
  def match
12
12
  # Note: must use content, and not content_normalized here
13
- if @file.content.strip =~ /\A#{REGEX}\z/i
13
+ if file.content.strip =~ /#{REGEX}+\z/i
14
14
  Licensee::License.find('no-license')
15
15
  end
16
16
  rescue Encoding::CompatibilityError
@@ -5,9 +5,9 @@ module Licensee
5
5
 
6
6
  # While we could parse the DESCRIPTION file, prefer
7
7
  # a lenient regex for speed and security. Moar parsing moar problems.
8
- LICENSE_FIELD_REGEX = /^license:\s*(.+)/i
9
- PLUS_FILE_LICENSE_REGEX = /\s*\+\s*file\s+LICENSE$/i
10
- GPL_VERSION_REGEX = /^GPL(?:-([23])|\s*\(\s*>=\s*([23])\s*\))$/i
8
+ LICENSE_FIELD_REGEX = /^license:\s*(.+)/i.freeze
9
+ PLUS_FILE_LICENSE_REGEX = /\s*\+\s*file\s+LICENSE$/i.freeze
10
+ GPL_VERSION_REGEX = /^GPL(?:-([23])|\s*\(\s*>=\s*([23])\s*\))$/i.freeze
11
11
 
12
12
  private
13
13
 
@@ -3,7 +3,7 @@ module Licensee
3
3
  class DistZilla < Licensee::Matchers::Package
4
4
  attr_reader :file
5
5
 
6
- LICENSE_REGEX = /^license\s*=\s*([a-z\-0-9\._]+)/i
6
+ LICENSE_REGEX = /^license\s*=\s*([a-z\-0-9\._]+)/i.freeze
7
7
 
8
8
  private
9
9