licensed 2.9.0 → 2.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/release.yml +11 -12
- data/.github/workflows/test.yml +69 -50
- data/.gitignore +2 -0
- data/CHANGELOG.md +42 -4
- data/README.md +11 -7
- data/docs/commands.md +6 -0
- data/docs/configuration.md +20 -0
- data/docs/sources/nuget.md +14 -0
- data/lib/licensed/cli.rb +7 -0
- data/lib/licensed/commands.rb +1 -0
- data/lib/licensed/commands/cache.rb +47 -17
- data/lib/licensed/commands/notices.rb +35 -0
- data/lib/licensed/configuration.rb +12 -2
- data/lib/licensed/dependency.rb +1 -1
- data/lib/licensed/reporters.rb +1 -0
- data/lib/licensed/reporters/cache_reporter.rb +10 -10
- data/lib/licensed/reporters/list_reporter.rb +5 -5
- data/lib/licensed/reporters/notices_reporter.rb +77 -0
- data/lib/licensed/reporters/reporter.rb +3 -0
- data/lib/licensed/reporters/status_reporter.rb +7 -7
- data/lib/licensed/sources.rb +1 -0
- data/lib/licensed/sources/cabal.rb +2 -2
- data/lib/licensed/sources/go.rb +2 -2
- data/lib/licensed/sources/npm.rb +1 -0
- data/lib/licensed/sources/nuget.rb +212 -0
- data/lib/licensed/version.rb +1 -1
- data/licensed.gemspec +3 -2
- data/script/source-setup/nuget +17 -0
- metadata +25 -6
@@ -15,20 +15,20 @@ module Licensed
|
|
15
15
|
result = yield report
|
16
16
|
|
17
17
|
all_reports = report.all_reports
|
18
|
-
errored_reports = all_reports.select { |
|
18
|
+
errored_reports = all_reports.select { |r| r.errors.any? }.to_a
|
19
19
|
|
20
|
-
dependency_count = all_reports.select { |
|
21
|
-
error_count = errored_reports.sum { |
|
20
|
+
dependency_count = all_reports.select { |r| r.target.is_a?(Licensed::Dependency) }.size
|
21
|
+
error_count = errored_reports.sum { |r| r.errors.size }
|
22
22
|
|
23
23
|
if error_count > 0
|
24
24
|
shell.newline
|
25
25
|
shell.error "Errors:"
|
26
|
-
errored_reports.each do |
|
27
|
-
display_metadata =
|
26
|
+
errored_reports.each do |r|
|
27
|
+
display_metadata = r.map { |k, v| "#{k}: #{v}" }.join(", ")
|
28
28
|
|
29
|
-
shell.error "* #{
|
29
|
+
shell.error "* #{r.name}"
|
30
30
|
shell.error " #{display_metadata}" unless display_metadata.empty?
|
31
|
-
|
31
|
+
r.errors.each do |error|
|
32
32
|
shell.error " - #{error}"
|
33
33
|
end
|
34
34
|
shell.newline
|
data/lib/licensed/sources.rb
CHANGED
@@ -11,6 +11,7 @@ module Licensed
|
|
11
11
|
require "licensed/sources/go"
|
12
12
|
require "licensed/sources/manifest"
|
13
13
|
require "licensed/sources/npm"
|
14
|
+
require "licensed/sources/nuget"
|
14
15
|
require "licensed/sources/pip"
|
15
16
|
require "licensed/sources/pipenv"
|
16
17
|
require "licensed/sources/gradle"
|
@@ -111,11 +111,11 @@ module Licensed
|
|
111
111
|
info = package_info_command(id).strip
|
112
112
|
return missing_package(id) if info.empty?
|
113
113
|
|
114
|
-
info.lines.each_with_object({}) do |line,
|
114
|
+
info.lines.each_with_object({}) do |line, hsh|
|
115
115
|
key, value = line.split(":", 2).map(&:strip)
|
116
116
|
next unless key && value
|
117
117
|
|
118
|
-
|
118
|
+
hsh[key] = value
|
119
119
|
end
|
120
120
|
end
|
121
121
|
|
data/lib/licensed/sources/go.rb
CHANGED
@@ -120,12 +120,12 @@ module Licensed
|
|
120
120
|
return go_mod["Version"] if go_mod
|
121
121
|
|
122
122
|
package_directory = package["Dir"]
|
123
|
-
return unless package_directory
|
123
|
+
return unless package_directory && File.exist?(package_directory)
|
124
124
|
|
125
125
|
# find most recent git SHA for a package, or nil if SHA is
|
126
126
|
# not available
|
127
127
|
Dir.chdir package_directory do
|
128
|
-
contents_version
|
128
|
+
contents_version(*contents_version_arguments)
|
129
129
|
end
|
130
130
|
end
|
131
131
|
|
data/lib/licensed/sources/npm.rb
CHANGED
@@ -48,6 +48,7 @@ module Licensed
|
|
48
48
|
# package name to it's metadata
|
49
49
|
def recursive_dependencies(dependencies, result = {})
|
50
50
|
dependencies.each do |name, dependency|
|
51
|
+
next if dependency["peerMissing"]
|
51
52
|
next if yarn_lock_present && dependency["missing"]
|
52
53
|
(result[name] ||= []) << dependency
|
53
54
|
recursive_dependencies(dependency["dependencies"] || {}, result)
|
@@ -0,0 +1,212 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "json"
|
3
|
+
require "reverse_markdown"
|
4
|
+
|
5
|
+
module Licensed
|
6
|
+
module Sources
|
7
|
+
# Only supports ProjectReference (project.assets.json) style restore used in .NET Core.
|
8
|
+
# Does not currently support packages.config style restore.
|
9
|
+
class NuGet < Source
|
10
|
+
def self.type
|
11
|
+
"nuget"
|
12
|
+
end
|
13
|
+
|
14
|
+
class NuGetDependency < Licensed::Dependency
|
15
|
+
LICENSE_FILE_REGEX = /<license\s*type\s*=\s*\"\s*file\s*\"\s*>\s*(.*)\s*<\/license>/ix.freeze
|
16
|
+
LICENSE_URL_REGEX = /<licenseUrl>\s*(.*)\s*<\/licenseUrl>/ix.freeze
|
17
|
+
PROJECT_URL_REGEX = /<projectUrl>\s*(.*)\s*<\/projectUrl>/ix.freeze
|
18
|
+
PROJECT_DESC_REGEX = /<description>\s*(.*)\s*<\/description>/ix.freeze
|
19
|
+
|
20
|
+
# Returns the metadata that represents this dependency. This metadata
|
21
|
+
# is written to YAML in the dependencys cached text file
|
22
|
+
def license_metadata
|
23
|
+
super.tap do |record_metadata|
|
24
|
+
record_metadata["homepage"] = project_url if project_url
|
25
|
+
record_metadata["summary"] = description if description
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def nuspec_path
|
30
|
+
name = @metadata["name"]
|
31
|
+
File.join(self.path, "#{name.downcase}.nuspec")
|
32
|
+
end
|
33
|
+
|
34
|
+
def nuspec_contents
|
35
|
+
return @nuspec_contents if defined?(@nuspec_contents)
|
36
|
+
@nuspec_contents = begin
|
37
|
+
return unless nuspec_path && File.exist?(nuspec_path)
|
38
|
+
File.read(nuspec_path)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def project_url
|
43
|
+
return @project_url if defined?(@project_url)
|
44
|
+
@project_url = begin
|
45
|
+
return unless nuspec_contents
|
46
|
+
match = nuspec_contents.match PROJECT_URL_REGEX
|
47
|
+
match[1] if match && match[1]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def description
|
52
|
+
return @description if defined?(@description)
|
53
|
+
@description = begin
|
54
|
+
return unless nuspec_contents
|
55
|
+
match = nuspec_contents.match PROJECT_DESC_REGEX
|
56
|
+
match[1] if match && match[1]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def project_files
|
61
|
+
@nuget_project_files ||= begin
|
62
|
+
files = super().flatten.compact
|
63
|
+
|
64
|
+
# Only include the local file if it's a file licensee didn't already detect
|
65
|
+
nuspec_license_filename = File.basename(nuspec_local_license_file.filename) if nuspec_local_license_file
|
66
|
+
if nuspec_license_filename && files.none? { |file| File.basename(file.filename) == nuspec_license_filename }
|
67
|
+
files.push(nuspec_local_license_file)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Only download licenseUrl if no recognized license was found locally
|
71
|
+
if files.none? { |file| file.license && file.license.key != "other" }
|
72
|
+
files.push(nuspec_remote_license_file)
|
73
|
+
end
|
74
|
+
|
75
|
+
files.compact
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Look for a <license type="file"> element in the nuspec that points to an
|
80
|
+
# on-disk license file (which licensee may not find due to a non-standard filename)
|
81
|
+
def nuspec_local_license_file
|
82
|
+
return @nuspec_local_license_file if defined?(@nuspec_local_license_file)
|
83
|
+
return unless nuspec_contents
|
84
|
+
|
85
|
+
match = nuspec_contents.match LICENSE_FILE_REGEX
|
86
|
+
return unless match && match[1]
|
87
|
+
|
88
|
+
license_path = File.join(File.dirname(nuspec_path), match[1])
|
89
|
+
return unless File.exist?(license_path)
|
90
|
+
|
91
|
+
license_data = File.read(license_path)
|
92
|
+
@nuspec_local_license_file = Licensee::ProjectFiles::LicenseFile.new(license_data, license_path)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Look for a <licenseUrl> element in the nuspec that either is known to contain a license identifier
|
96
|
+
# in the URL, or points to license text on the internet that can be downloaded.
|
97
|
+
def nuspec_remote_license_file
|
98
|
+
return @nuspec_remote_license_file if defined?(@nuspec_remote_license_file)
|
99
|
+
return unless nuspec_contents
|
100
|
+
|
101
|
+
match = nuspec_contents.match LICENSE_URL_REGEX
|
102
|
+
return unless match && match[1]
|
103
|
+
|
104
|
+
# Attempt to fetch the license content
|
105
|
+
license_content = self.class.retrieve_license(match[1])
|
106
|
+
@nuspec_remote_license_file = Licensee::ProjectFiles::LicenseFile.new(license_content, { uri: match[1] }) if license_content
|
107
|
+
end
|
108
|
+
|
109
|
+
class << self
|
110
|
+
def strip_html(html)
|
111
|
+
return unless html
|
112
|
+
|
113
|
+
return html unless html.downcase.include?("<html")
|
114
|
+
ReverseMarkdown.convert(html, unknown_tags: :bypass)
|
115
|
+
end
|
116
|
+
|
117
|
+
def ignored_url?(url)
|
118
|
+
# Many Microsoft packages that now use <license> use this for <licenseUrl>
|
119
|
+
# No need to fetch this page - it just contains NuGet documentation
|
120
|
+
url == "https://aka.ms/deprecateLicenseUrl"
|
121
|
+
end
|
122
|
+
|
123
|
+
def text_content_url(url)
|
124
|
+
# Convert github file URLs to raw URLs
|
125
|
+
return url unless match = url.match(/https?:\/\/(?:www\.)?github.com\/([^\/]+)\/([^\/]+)\/blob\/(.*)/i)
|
126
|
+
"https://github.com/#{match[1]}/#{match[2]}/raw/#{match[3]}"
|
127
|
+
end
|
128
|
+
|
129
|
+
def retrieve_license(url)
|
130
|
+
return unless url
|
131
|
+
return if ignored_url?(url)
|
132
|
+
|
133
|
+
# Transform URLs that are known to return HTML but have a corresponding text-based URL
|
134
|
+
text_url = text_content_url(url)
|
135
|
+
|
136
|
+
raw_content = fetch_content(text_url)
|
137
|
+
strip_html(raw_content)
|
138
|
+
end
|
139
|
+
|
140
|
+
def fetch_content(url, redirect_limit = 5)
|
141
|
+
url = URI.parse(url) if url.instance_of? String
|
142
|
+
return @response_by_url[url] if (@response_by_url ||= {}).key?(url)
|
143
|
+
return if redirect_limit == 0
|
144
|
+
|
145
|
+
begin
|
146
|
+
response = Net::HTTP.get_response(url)
|
147
|
+
case response
|
148
|
+
when Net::HTTPSuccess then
|
149
|
+
@response_by_url[url] = response.body
|
150
|
+
when Net::HTTPRedirection then
|
151
|
+
redirect_url = URI.parse(response["location"])
|
152
|
+
if redirect_url.relative?
|
153
|
+
redirect_url = url + redirect_url
|
154
|
+
end
|
155
|
+
# The redirect might be to a URL that requires transformation, i.e. a github file
|
156
|
+
redirect_url = text_content_url(redirect_url.to_s)
|
157
|
+
@response_by_url[url] = fetch_content(redirect_url, redirect_limit - 1)
|
158
|
+
end
|
159
|
+
rescue
|
160
|
+
# Host might no longer exist or some other error, ignore
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def project_assets_file_path
|
167
|
+
File.join(config.pwd, "project.assets.json")
|
168
|
+
end
|
169
|
+
|
170
|
+
def project_assets_file
|
171
|
+
return @project_assets_file if defined?(@project_assets_file)
|
172
|
+
@project_assets_file = File.read(project_assets_file_path)
|
173
|
+
end
|
174
|
+
|
175
|
+
def enabled?
|
176
|
+
File.exist?(project_assets_file_path)
|
177
|
+
end
|
178
|
+
|
179
|
+
# Inspect project.assets.json files for package references.
|
180
|
+
# Ideally we'd use `dotnet list package` instead, but its output isn't
|
181
|
+
# easily machine readable and doesn't contain everything we need.
|
182
|
+
def enumerate_dependencies
|
183
|
+
json = JSON.parse(project_assets_file)
|
184
|
+
nuget_packages_dir = json["project"]["restore"]["packagesPath"]
|
185
|
+
json["targets"].each_with_object({}) do |(_, target), dependencies|
|
186
|
+
target.each do |reference_key, reference|
|
187
|
+
# Ignore project references
|
188
|
+
next unless reference["type"] == "package"
|
189
|
+
package_id_parts = reference_key.partition("/")
|
190
|
+
name = package_id_parts[0]
|
191
|
+
version = package_id_parts[-1]
|
192
|
+
id = "#{name}-#{version}"
|
193
|
+
|
194
|
+
# Already know this package from another target
|
195
|
+
next if dependencies.key?(id)
|
196
|
+
|
197
|
+
path = File.join(nuget_packages_dir, json["libraries"][reference_key]["path"])
|
198
|
+
dependencies[id] = NuGetDependency.new(
|
199
|
+
name: id,
|
200
|
+
version: version,
|
201
|
+
path: path,
|
202
|
+
metadata: {
|
203
|
+
"type" => NuGet.type,
|
204
|
+
"name" => name
|
205
|
+
}
|
206
|
+
)
|
207
|
+
end
|
208
|
+
end.values
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
data/lib/licensed/version.rb
CHANGED
data/licensed.gemspec
CHANGED
@@ -23,13 +23,14 @@ Gem::Specification.new do |spec|
|
|
23
23
|
|
24
24
|
spec.required_ruby_version = ">= 2.3.0"
|
25
25
|
|
26
|
-
spec.add_dependency "licensee", ">= 9.
|
27
|
-
spec.add_dependency "thor", "
|
26
|
+
spec.add_dependency "licensee", ">= 9.14.0", "< 10.0.0"
|
27
|
+
spec.add_dependency "thor", ">= 0.19"
|
28
28
|
spec.add_dependency "pathname-common_prefix", "~> 0.0.1"
|
29
29
|
spec.add_dependency "tomlrb", "~> 1.2"
|
30
30
|
spec.add_dependency "bundler", ">= 1.10"
|
31
31
|
spec.add_dependency "ruby-xxHash", "~> 0.4"
|
32
32
|
spec.add_dependency "parallel", ">= 0.18.0"
|
33
|
+
spec.add_dependency "reverse_markdown", "~> 1.0"
|
33
34
|
|
34
35
|
spec.add_development_dependency "rake", ">= 12.3.3"
|
35
36
|
spec.add_development_dependency "minitest", "~> 5.8"
|
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
set -e
|
3
|
+
|
4
|
+
if [ -z "$(which dotnet)" ]; then
|
5
|
+
echo "A local dotnet installation is required for dotnet/nuget development." >&2
|
6
|
+
exit 127
|
7
|
+
fi
|
8
|
+
|
9
|
+
# setup test fixtures
|
10
|
+
BASE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
11
|
+
cd $BASE_PATH/test/fixtures/nuget
|
12
|
+
|
13
|
+
if [ "$1" == "-f" ]; then
|
14
|
+
dotnet clean
|
15
|
+
fi
|
16
|
+
|
17
|
+
dotnet restore
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: licensed
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.11.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GitHub
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-06-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: licensee
|
@@ -16,7 +16,7 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 9.
|
19
|
+
version: 9.14.0
|
20
20
|
- - "<"
|
21
21
|
- !ruby/object:Gem::Version
|
22
22
|
version: 10.0.0
|
@@ -26,7 +26,7 @@ dependencies:
|
|
26
26
|
requirements:
|
27
27
|
- - ">="
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version: 9.
|
29
|
+
version: 9.14.0
|
30
30
|
- - "<"
|
31
31
|
- !ruby/object:Gem::Version
|
32
32
|
version: 10.0.0
|
@@ -34,14 +34,14 @@ dependencies:
|
|
34
34
|
name: thor
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
36
36
|
requirements:
|
37
|
-
- - "
|
37
|
+
- - ">="
|
38
38
|
- !ruby/object:Gem::Version
|
39
39
|
version: '0.19'
|
40
40
|
type: :runtime
|
41
41
|
prerelease: false
|
42
42
|
version_requirements: !ruby/object:Gem::Requirement
|
43
43
|
requirements:
|
44
|
-
- - "
|
44
|
+
- - ">="
|
45
45
|
- !ruby/object:Gem::Version
|
46
46
|
version: '0.19'
|
47
47
|
- !ruby/object:Gem::Dependency
|
@@ -114,6 +114,20 @@ dependencies:
|
|
114
114
|
- - ">="
|
115
115
|
- !ruby/object:Gem::Version
|
116
116
|
version: 0.18.0
|
117
|
+
- !ruby/object:Gem::Dependency
|
118
|
+
name: reverse_markdown
|
119
|
+
requirement: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - "~>"
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '1.0'
|
124
|
+
type: :runtime
|
125
|
+
prerelease: false
|
126
|
+
version_requirements: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - "~>"
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '1.0'
|
117
131
|
- !ruby/object:Gem::Dependency
|
118
132
|
name: rake
|
119
133
|
requirement: !ruby/object:Gem::Requirement
|
@@ -257,6 +271,7 @@ files:
|
|
257
271
|
- docs/sources/manifests.md
|
258
272
|
- docs/sources/mix.md
|
259
273
|
- docs/sources/npm.md
|
274
|
+
- docs/sources/nuget.md
|
260
275
|
- docs/sources/pip.md
|
261
276
|
- docs/sources/pipenv.md
|
262
277
|
- docs/sources/stack.md
|
@@ -269,6 +284,7 @@ files:
|
|
269
284
|
- lib/licensed/commands/command.rb
|
270
285
|
- lib/licensed/commands/environment.rb
|
271
286
|
- lib/licensed/commands/list.rb
|
287
|
+
- lib/licensed/commands/notices.rb
|
272
288
|
- lib/licensed/commands/status.rb
|
273
289
|
- lib/licensed/configuration.rb
|
274
290
|
- lib/licensed/dependency.rb
|
@@ -280,6 +296,7 @@ files:
|
|
280
296
|
- lib/licensed/reporters/cache_reporter.rb
|
281
297
|
- lib/licensed/reporters/json_reporter.rb
|
282
298
|
- lib/licensed/reporters/list_reporter.rb
|
299
|
+
- lib/licensed/reporters/notices_reporter.rb
|
283
300
|
- lib/licensed/reporters/reporter.rb
|
284
301
|
- lib/licensed/reporters/status_reporter.rb
|
285
302
|
- lib/licensed/reporters/yaml_reporter.rb
|
@@ -297,6 +314,7 @@ files:
|
|
297
314
|
- lib/licensed/sources/manifest.rb
|
298
315
|
- lib/licensed/sources/mix.rb
|
299
316
|
- lib/licensed/sources/npm.rb
|
317
|
+
- lib/licensed/sources/nuget.rb
|
300
318
|
- lib/licensed/sources/pip.rb
|
301
319
|
- lib/licensed/sources/pipenv.rb
|
302
320
|
- lib/licensed/sources/source.rb
|
@@ -320,6 +338,7 @@ files:
|
|
320
338
|
- script/source-setup/go
|
321
339
|
- script/source-setup/mix
|
322
340
|
- script/source-setup/npm
|
341
|
+
- script/source-setup/nuget
|
323
342
|
- script/source-setup/pip
|
324
343
|
- script/source-setup/pipenv
|
325
344
|
- script/source-setup/yarn
|