licensed 2.9.0 → 2.11.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
data/CHANGELOG.md
CHANGED
@@ -6,17 +6,55 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
+
## 2.11.1
|
10
|
+
2020-06-09
|
11
|
+
|
12
|
+
### Fixed
|
13
|
+
- `notices` command properly reads cached dependency notices contents (https://github.com/github/licensed/pull/283)
|
14
|
+
|
15
|
+
## 2.11.0
|
16
|
+
2020-06-02
|
17
|
+
|
18
|
+
### Added
|
19
|
+
- `notices` command to create a `NOTICE` file for each configured app (https://github.com/github/licensed/pull/277)
|
20
|
+
|
21
|
+
### Fixed
|
22
|
+
- NuGet source no longer crashes on a non-existent dependency path (https://github.com/github/licensed/pull/280)
|
23
|
+
- Go source no longer crashes on a non-existent dependency package path (https://github.com/github/licensed/pull/274)
|
24
|
+
|
25
|
+
## 2.10.0
|
26
|
+
2020-05-15
|
27
|
+
|
28
|
+
### Changed
|
29
|
+
- NPM source ignores missing peer dependencies (https://github.com/github/licensed/pull/267)
|
30
|
+
|
31
|
+
### Added
|
32
|
+
- NuGet source (:tada: @zarenner https://github.com/github/licensed/pull/261)
|
33
|
+
- Multiple apps can share a single cache location (https://github.com/github/licensed/pull/263)
|
34
|
+
|
35
|
+
## 2.9.2
|
36
|
+
2020-04-28
|
37
|
+
|
38
|
+
### Changed
|
39
|
+
- `licensee` minimum version bumped to 9.13.2 (https://github.com/github/licensed/pull/256)
|
40
|
+
|
41
|
+
## 2.9.1
|
42
|
+
2020-03-24
|
43
|
+
|
44
|
+
### Changed
|
45
|
+
- relaxed gem version restrictions on Thor (:tada: @eileencodes https://github.com/github/licensed/pull/254)
|
46
|
+
|
9
47
|
## 2.9.0
|
10
48
|
2020-03-19
|
11
49
|
|
12
|
-
|
50
|
+
### Added
|
13
51
|
- Source paths use glob pattern matching (https://github.com/github/licensed/pull/245)
|
14
52
|
|
15
|
-
|
53
|
+
### Fixed
|
16
54
|
- Mix source supports updates to mix.lock format (:tada: @bruce https://github.com/github/licensed/pull/242)
|
17
55
|
- Go source supports `go list` format changes in go 1.14 (https://github.com/github/licensed/pull/247)
|
18
56
|
|
19
|
-
|
57
|
+
### Changed
|
20
58
|
- `licensed cache` will flag dependencies for re-review when license text changes (https://github.com/github/licensed/pull/248)
|
21
59
|
- `licensed status` will raise errors on dependencies that need re-review (https://github.com/github/licensed/pull/248)
|
22
60
|
- `licensee` minimum version bumped to 9.13.1 (https://github.com/github/licensed/pull/251)
|
@@ -280,4 +318,4 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
280
318
|
|
281
319
|
Initial release :tada:
|
282
320
|
|
283
|
-
[Unreleased]: https://github.com/github/licensed/compare/2.
|
321
|
+
[Unreleased]: https://github.com/github/licensed/compare/2.11.1...HEAD
|
data/README.md
CHANGED
@@ -24,13 +24,13 @@ See the [migration documentation](./docs/migrating_to_newer_versions.md) for mor
|
|
24
24
|
### Dependencies
|
25
25
|
|
26
26
|
Licensed uses the `libgit2` bindings for Ruby provided by `rugged`. `rugged` requires `cmake` and `pkg-config` which you may need to install before you can install Licensed.
|
27
|
-
|
27
|
+
|
28
28
|
> Ubuntu
|
29
|
-
|
29
|
+
|
30
30
|
sudo apt-get install cmake pkg-config
|
31
|
-
|
31
|
+
|
32
32
|
> OS X
|
33
|
-
|
33
|
+
|
34
34
|
brew install cmake pkg-config
|
35
35
|
|
36
36
|
### With a Gemfile
|
@@ -64,8 +64,10 @@ For system wide usage, install licensed to a location on `$PATH`, e.g. `/usr/loc
|
|
64
64
|
|
65
65
|
- `licensed list`: Output enumerated dependencies only.
|
66
66
|
- `licensed cache`: Cache licenses and metadata.
|
67
|
-
- `licensed status`: Check status of dependencies' cached licenses.
|
67
|
+
- `licensed status`: Check status of dependencies' cached licenses.
|
68
|
+
- `licensed notices`: Write a `NOTICE` file for each application configuration.
|
68
69
|
- `licensed version`: Show current installed version of Licensed. Aliases: `-v|--version`
|
70
|
+
- `licensed env`: Output environment information from the licensed configuration.
|
69
71
|
|
70
72
|
See the [commands documentation](./docs/commands.md) for additional documentation, or run `licensed -h` to see all of the current available commands.
|
71
73
|
|
@@ -102,14 +104,16 @@ Dependencies will be automatically detected for all of the following sources by
|
|
102
104
|
1. [Bundler](./docs/sources/bundler.md)
|
103
105
|
1. [Cabal](./docs/sources/cabal.md)
|
104
106
|
1. [Composer](./docs/sources/composer.md)
|
107
|
+
1. [Git Submodules (git_submodule)](./docs/sources/git_submodule.md)
|
105
108
|
1. [Go](./docs/sources/go.md)
|
106
109
|
1. [Go Dep (dep)](./docs/sources/dep.md)
|
110
|
+
1. [Gradle](./docs/sources/gradle.md)
|
107
111
|
1. [Manifest lists (manifests)](./docs/sources/manifests.md)
|
112
|
+
1. [Mix](./docs/sources/mix.md)
|
108
113
|
1. [NPM](./docs/sources/npm.md)
|
114
|
+
1. [NuGet](./docs/sources/nuget.md)
|
109
115
|
1. [Pip](./docs/sources/pip.md)
|
110
116
|
1. [Pipenv](./docs/sources/pipenv.md)
|
111
|
-
1. [Git Submodules (git_submodule)](./docs/sources/git_submodule.md)
|
112
|
-
1. [Mix](./docs/sources/mix.md)
|
113
117
|
1. [Yarn](./docs/sources/yarn.md)
|
114
118
|
|
115
119
|
You can disable any of them in the configuration file:
|
data/docs/commands.md
CHANGED
@@ -31,6 +31,12 @@ A dependency will fail the status checks if:
|
|
31
31
|
5. The cached record is flagged for re-review.
|
32
32
|
- This occurs when the record's license text has changed since the record was reviewed.
|
33
33
|
|
34
|
+
## `notices`
|
35
|
+
|
36
|
+
Outputs license and notice text for all dependencies in each app into a `NOTICE` file in the app's `cache_path`. If an app uses a shared cache path, the file name will contain the app name as well, e.g. `NOTICE.my_app`.
|
37
|
+
|
38
|
+
The `NOTICE` file contents are retrieved from cached records, with the assumption that cached records have already been reviewed in a compliance workflow.
|
39
|
+
|
34
40
|
## `env`
|
35
41
|
|
36
42
|
Prints the runtime environment used by licensed after loading a configuration file. By default the output is in YAML format, but can be output in JSON using the `--json` flag.
|
data/docs/configuration.md
CHANGED
@@ -209,6 +209,26 @@ apps:
|
|
209
209
|
|
210
210
|
In this example, the root configuration will contain a default cache path of `.licenses`. `app1` will inherit this value and append it's name, resulting in a cache path of `.licenses/app1`.
|
211
211
|
|
212
|
+
### Sharing caches between apps
|
213
|
+
|
214
|
+
Dependency caches can be shared between apps by setting the same cache path on each app.
|
215
|
+
|
216
|
+
```yaml
|
217
|
+
apps:
|
218
|
+
- source_path: "path/to/app1"
|
219
|
+
cache_path: ".licenses/apps"
|
220
|
+
- source_path: "path/to/app2"
|
221
|
+
cache_path: ".licenses/apps"
|
222
|
+
```
|
223
|
+
|
224
|
+
When using a source path with a glob pattern, the apps created from the glob pattern can share a dependency by setting an explicit cache path and setting `shared_cache` to true.
|
225
|
+
|
226
|
+
```yaml
|
227
|
+
source_path: "path/to/apps/*"
|
228
|
+
cache_path: ".licenses/apps"
|
229
|
+
shared_cache: true
|
230
|
+
```
|
231
|
+
|
212
232
|
## Source specific configuration
|
213
233
|
|
214
234
|
See the [source documentation](./sources) for details on any source specific configuration.
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# NuGet
|
2
|
+
|
3
|
+
The NuGet source will detect ProjectReference-style restored packages by inspecting `project.assets.json` files for dependencies. It requires that `dotnet restore` has already ran on the project.
|
4
|
+
|
5
|
+
The source currently expects that `source_path` is set to the `obj` directory containing the `project.assets.json`.
|
6
|
+
For example, if your project lives at `foo/foo.proj`, you likely want to set `source_path` to `foo/obj`.
|
7
|
+
If in MSBuild you have customized your `obj` paths (e.g. to live outside your source tree), you may need to set `source_path` to something different such as `../obj/foo`.
|
8
|
+
|
9
|
+
### Search strategy
|
10
|
+
This source looks for licenses:
|
11
|
+
1. Specified by SPDX expression via `<license type="expression">` in a package's `.nuspec` (via licensee)
|
12
|
+
2. In license files such as `LICENSE.txt`, even if not specified in the `.nuspec` (via licensee)
|
13
|
+
3. Specified by filepath via `<license type="file">` in a package's `.nuspec`, even if not a standard license filename.
|
14
|
+
4. By downloading and inspecting the contents of `<licenseUrl>` in a package's `.nuspec`, if not found otherwise.
|
data/lib/licensed/cli.rb
CHANGED
@@ -28,6 +28,13 @@ module Licensed
|
|
28
28
|
run Licensed::Commands::List.new(config: config)
|
29
29
|
end
|
30
30
|
|
31
|
+
desc "notices", "Generate a NOTICE file from cached records"
|
32
|
+
method_option :config, aliases: "-c", type: :string,
|
33
|
+
desc: "Path to licensed configuration file"
|
34
|
+
def notices
|
35
|
+
run Licensed::Commands::Notices.new(config: config)
|
36
|
+
end
|
37
|
+
|
31
38
|
map "-v" => :version
|
32
39
|
map "--version" => :version
|
33
40
|
desc "version", "Show Installed Version of Licensed, [-v, --version]"
|
data/lib/licensed/commands.rb
CHANGED
@@ -11,20 +11,39 @@ module Licensed
|
|
11
11
|
Licensed::Reporters::CacheReporter.new
|
12
12
|
end
|
13
13
|
|
14
|
+
# Run the command.
|
15
|
+
# Removes any cached records that don't match a current application
|
16
|
+
# dependency.
|
17
|
+
#
|
18
|
+
# options - Options to run the command with
|
19
|
+
#
|
20
|
+
# Returns whether the command was a success
|
21
|
+
def run(**options)
|
22
|
+
begin
|
23
|
+
result = super
|
24
|
+
clear_stale_cached_records if result
|
25
|
+
|
26
|
+
result
|
27
|
+
ensure
|
28
|
+
cache_paths.clear
|
29
|
+
files.clear
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
14
33
|
protected
|
15
34
|
|
16
|
-
# Run the command for all
|
35
|
+
# Run the command for all enabled sources for an application configuration,
|
17
36
|
# recording results in a report.
|
18
|
-
# Removes any cached records that don't match a current application
|
19
|
-
# dependency.
|
20
37
|
#
|
21
|
-
# app -
|
22
|
-
# source - A dependency source enumerator
|
38
|
+
# app - An application configuration
|
23
39
|
#
|
24
|
-
# Returns whether the command succeeded for the
|
25
|
-
def
|
40
|
+
# Returns whether the command succeeded for the application.
|
41
|
+
def run_app(app)
|
26
42
|
result = super
|
27
|
-
|
43
|
+
|
44
|
+
# add the full cache path to the list of cache paths evaluted during this run
|
45
|
+
cache_paths << app.cache_path
|
46
|
+
|
28
47
|
result
|
29
48
|
end
|
30
49
|
|
@@ -62,6 +81,9 @@ module Licensed
|
|
62
81
|
report.warnings << "expected dependency path #{dependency.path} does not exist"
|
63
82
|
end
|
64
83
|
|
84
|
+
# add the absolute dependency file path to the list of files seen during this licensed run
|
85
|
+
files << filename.to_s
|
86
|
+
|
65
87
|
true
|
66
88
|
end
|
67
89
|
|
@@ -86,18 +108,26 @@ module Licensed
|
|
86
108
|
|
87
109
|
# Clean up cached files that dont match current dependencies
|
88
110
|
#
|
89
|
-
# app - An application configuration
|
90
|
-
# source - A dependency source enumerator
|
91
|
-
#
|
92
111
|
# Returns nothing
|
93
|
-
def clear_stale_cached_records
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
112
|
+
def clear_stale_cached_records
|
113
|
+
cache_paths.each do |cache_path|
|
114
|
+
Dir.glob(cache_path.join("**/*.#{DependencyRecord::EXTENSION}")).each do |file|
|
115
|
+
next if files.include?(file)
|
116
|
+
|
117
|
+
FileUtils.rm(file)
|
118
|
+
end
|
99
119
|
end
|
100
120
|
end
|
121
|
+
|
122
|
+
# Set of unique cache paths that are evaluted during the run
|
123
|
+
def cache_paths
|
124
|
+
@cache_paths ||= Set.new
|
125
|
+
end
|
126
|
+
|
127
|
+
# Set of unique absolute file paths of cached records evaluted during the run
|
128
|
+
def files
|
129
|
+
@files ||= Set.new
|
130
|
+
end
|
101
131
|
end
|
102
132
|
end
|
103
133
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Licensed
|
3
|
+
module Commands
|
4
|
+
class Notices < Command
|
5
|
+
# Create a reporter to use during a command run
|
6
|
+
#
|
7
|
+
# options - The options the command was run with
|
8
|
+
#
|
9
|
+
# Raises a Licensed::Reporters::CacheReporter
|
10
|
+
def create_reporter(options)
|
11
|
+
Licensed::Reporters::NoticesReporter.new
|
12
|
+
end
|
13
|
+
|
14
|
+
protected
|
15
|
+
|
16
|
+
# Load stored dependency record data to add to the notices report.
|
17
|
+
#
|
18
|
+
# app - The application configuration for the dependency
|
19
|
+
# source - The dependency source enumerator for the dependency
|
20
|
+
# dependency - An application dependency
|
21
|
+
# report - A report hash for the command to provide extra data for the report output.
|
22
|
+
#
|
23
|
+
# Returns true.
|
24
|
+
def evaluate_dependency(app, source, dependency, report)
|
25
|
+
filename = app.cache_path.join(source.class.type, "#{dependency.name}.#{DependencyRecord::EXTENSION}")
|
26
|
+
report["cached_record"] = Licensed::DependencyRecord.read(filename)
|
27
|
+
if !report["cached_record"]
|
28
|
+
report["warning"] = "expected cached record not found at #{filename}"
|
29
|
+
end
|
30
|
+
|
31
|
+
true
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -108,7 +108,12 @@ module Licensed
|
|
108
108
|
def detect_cache_path(options, inherited_options)
|
109
109
|
return options["cache_path"] unless options["cache_path"].to_s.empty?
|
110
110
|
|
111
|
-
|
111
|
+
# if cache_path and shared_cache are both set in inherited_options,
|
112
|
+
# don't append the app name to the cache path
|
113
|
+
cache_path = inherited_options["cache_path"]
|
114
|
+
return cache_path if cache_path && inherited_options["shared_cache"] == true
|
115
|
+
|
116
|
+
cache_path ||= DEFAULT_CACHE_PATH
|
112
117
|
File.join(cache_path, self["name"])
|
113
118
|
end
|
114
119
|
|
@@ -167,7 +172,12 @@ module Licensed
|
|
167
172
|
# will handle configurations that don't have these explicitly set
|
168
173
|
dir_name = File.basename(path)
|
169
174
|
config["name"] = "#{config["name"]}-#{dir_name}" if config["name"]
|
170
|
-
|
175
|
+
|
176
|
+
# if a cache_path is set and is not marked as shared, append the app name
|
177
|
+
# to the end of the cache path to make a unique cache path for the app
|
178
|
+
if config["cache_path"] && config["shared_cache"] != true
|
179
|
+
config["cache_path"] = File.join(config["cache_path"], dir_name)
|
180
|
+
end
|
171
181
|
|
172
182
|
config
|
173
183
|
end
|
data/lib/licensed/dependency.rb
CHANGED
@@ -74,7 +74,7 @@ module Licensed
|
|
74
74
|
def license_contents
|
75
75
|
files = matched_files.reject { |f| f == package_file }
|
76
76
|
.group_by(&:content)
|
77
|
-
.map { |content,
|
77
|
+
.map { |content, sources| { "sources" => license_content_sources(sources), "text" => content } }
|
78
78
|
|
79
79
|
files << generated_license_contents if files.empty?
|
80
80
|
files.compact
|
data/lib/licensed/reporters.rb
CHANGED
@@ -27,32 +27,32 @@ module Licensed
|
|
27
27
|
shell.info " #{source.class.type}"
|
28
28
|
result = yield report
|
29
29
|
|
30
|
-
warning_reports = report.all_reports.select { |
|
30
|
+
warning_reports = report.all_reports.select { |r| r.warnings.any? }.to_a
|
31
31
|
if warning_reports.any?
|
32
32
|
shell.newline
|
33
33
|
shell.warn " * Warnings:"
|
34
|
-
warning_reports.each do |
|
35
|
-
display_metadata =
|
34
|
+
warning_reports.each do |r|
|
35
|
+
display_metadata = r.map { |k, v| "#{k}: #{v}" }.join(", ")
|
36
36
|
|
37
|
-
shell.warn " * #{
|
37
|
+
shell.warn " * #{r.name}"
|
38
38
|
shell.warn " #{display_metadata}" unless display_metadata.empty?
|
39
|
-
|
39
|
+
r.warnings.each do |warning|
|
40
40
|
shell.warn " - #{warning}"
|
41
41
|
end
|
42
42
|
shell.newline
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
-
errored_reports = report.all_reports.select { |
|
46
|
+
errored_reports = report.all_reports.select { |r| r.errors.any? }.to_a
|
47
47
|
if errored_reports.any?
|
48
48
|
shell.newline
|
49
49
|
shell.error " * Errors:"
|
50
|
-
errored_reports.each do |
|
51
|
-
display_metadata =
|
50
|
+
errored_reports.each do |r|
|
51
|
+
display_metadata = r.map { |k, v| "#{k}: #{v}" }.join(", ")
|
52
52
|
|
53
|
-
shell.error " * #{
|
53
|
+
shell.error " * #{r.name}"
|
54
54
|
shell.error " #{display_metadata}" unless display_metadata.empty?
|
55
|
-
|
55
|
+
r.errors.each do |error|
|
56
56
|
shell.error " - #{error}"
|
57
57
|
end
|
58
58
|
shell.newline
|
@@ -28,16 +28,16 @@ module Licensed
|
|
28
28
|
shell.info " #{source.class.type}"
|
29
29
|
result = yield report
|
30
30
|
|
31
|
-
errored_reports = report.all_reports.select { |
|
31
|
+
errored_reports = report.all_reports.select { |r| r.errors.any? }.to_a
|
32
32
|
if errored_reports.any?
|
33
33
|
shell.newline
|
34
34
|
shell.error " * Errors:"
|
35
|
-
errored_reports.each do |
|
36
|
-
display_metadata =
|
35
|
+
errored_reports.each do |r|
|
36
|
+
display_metadata = r.map { |k, v| "#{k}: #{v}" }.join(", ")
|
37
37
|
|
38
|
-
shell.error " * #{
|
38
|
+
shell.error " * #{r.name}"
|
39
39
|
shell.error " #{display_metadata}" unless display_metadata.empty?
|
40
|
-
|
40
|
+
r.errors.each do |error|
|
41
41
|
shell.error " - #{error}"
|
42
42
|
end
|
43
43
|
shell.newline
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Licensed
|
4
|
+
module Reporters
|
5
|
+
class NoticesReporter < Reporter
|
6
|
+
TEXT_SEPARATOR = "\n\n#{("-" * 5)}\n\n".freeze
|
7
|
+
LICENSE_SEPARATOR = "\n#{("*" * 5)}\n".freeze
|
8
|
+
|
9
|
+
# Reports on an application configuration in a notices command run
|
10
|
+
#
|
11
|
+
# app - An application configuration
|
12
|
+
#
|
13
|
+
# Returns the result of the yielded method
|
14
|
+
# Note - must be called from inside the `report_run` scope
|
15
|
+
def report_app(app)
|
16
|
+
super do |report|
|
17
|
+
filename = app["shared_cache"] ? "NOTICE.#{app["name"]}" : "NOTICE"
|
18
|
+
path = app.cache_path.join(filename)
|
19
|
+
shell.info "Writing notices for #{app["name"]} to #{path}"
|
20
|
+
|
21
|
+
result = yield report
|
22
|
+
|
23
|
+
File.open(path, "w") do |file|
|
24
|
+
file << "THIRD PARTY NOTICES\n"
|
25
|
+
file << LICENSE_SEPARATOR
|
26
|
+
file << report.all_reports
|
27
|
+
.map { |r| notices(r) }
|
28
|
+
.compact
|
29
|
+
.join(LICENSE_SEPARATOR)
|
30
|
+
end
|
31
|
+
|
32
|
+
result
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Reports on a dependency in a notices command run.
|
37
|
+
#
|
38
|
+
# dependency - An application dependency
|
39
|
+
#
|
40
|
+
# Returns the result of the yielded method
|
41
|
+
# Note - must be called from inside the `report_run` scope
|
42
|
+
def report_dependency(dependency)
|
43
|
+
super do |report|
|
44
|
+
result = yield report
|
45
|
+
shell.warn "* #{report["warning"]}" if report["warning"]
|
46
|
+
result
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns notices information for a dependency report
|
51
|
+
def notices(report)
|
52
|
+
return unless report.target.is_a?(Licensed::Dependency)
|
53
|
+
|
54
|
+
cached_record = report["cached_record"]
|
55
|
+
return unless cached_record
|
56
|
+
|
57
|
+
texts = cached_record.licenses.map(&:text)
|
58
|
+
cached_record.notices.each do |notice|
|
59
|
+
case notice
|
60
|
+
when Hash
|
61
|
+
texts << notice["text"]
|
62
|
+
when String
|
63
|
+
texts << notice
|
64
|
+
else
|
65
|
+
shell.warn "* unable to parse notices for #{report.target.name}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
<<~NOTICE
|
70
|
+
#{cached_record["name"]}@#{cached_record["version"]}
|
71
|
+
|
72
|
+
#{texts.map(&:strip).reject(&:empty?).compact.join(TEXT_SEPARATOR)}
|
73
|
+
NOTICE
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|