licensed 2.10.0 → 2.11.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8b47685dba77cc47406dfa9aa2dd805c5f4a1746ea547a5b34c39a6d2ec47dcf
4
- data.tar.gz: 0eef1add309449ee2f00a33831d780cf6250990bb74be08077cf786ab28996a2
3
+ metadata.gz: 9cb9d80097e4329d3aa276faf53b72cac7fed2c2f4c09edc500bc627fb8d942b
4
+ data.tar.gz: f2ba113cfac5f807980f44a7d60a40dd6c946c85a351bf8161854b72d715d625
5
5
  SHA512:
6
- metadata.gz: e01ca0150f1690ade72d0c95743d435af9796269d6b3a51496d39b5e98c186dc0cf78dd824f565c7070dc75e8a85e5af0aad21c8382b92b1578bd09edbe89dd3
7
- data.tar.gz: 0d00ed4c4b0596f96cb2ec77972fe36352bc17a84ee7043b5c3d002db9d9b2f15c9ddf87a2b67ebf36b99eb8dc8b02b4a917e68f9d83d2d42457bb26ee3a5314
6
+ metadata.gz: dcdc95e2d512dbf8f9e84f76409f4d94b93ae3bdf95d17fa4b668f04e996cdcda340c5e7f09dd499c509b122e92cfc90d58838f33df24d690fd4ae1b759550b0
7
+ data.tar.gz: bba8ba26db299965d6fc7e2d783060d827e5f45cd7f1a287414f63c39298b39dfb6739beebe46d152e6619830705a8abf822a33ce01d94335fc195596a65e6cb
@@ -93,7 +93,7 @@ jobs:
93
93
  steps:
94
94
  - uses: actions/checkout@v2
95
95
  - name: Setup php
96
- uses: nanasess/setup-php@v1.0.2
96
+ uses: nanasess/setup-php@v3.0.4
97
97
  with:
98
98
  php-version: ${{ matrix.php }}
99
99
  - name: Set up Ruby
data/.gitignore CHANGED
@@ -45,6 +45,8 @@ test/fixtures/mix/mix.lock
45
45
  test/fixtures/yarn/*
46
46
  !test/fixtures/yarn/package.json
47
47
 
48
+ test/fixtures/nuget/obj/*
49
+
48
50
  vendor/licenses
49
51
  .licenses
50
52
  *.gem
@@ -6,6 +6,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## 2.11.0
10
+ 2020-06-02
11
+
12
+ ### Added
13
+ - `notices` command to create a `NOTICE` file for each configured app (https://github.com/github/licensed/pull/277)
14
+
15
+ ### Fixed
16
+ - NuGet source no longer crashes on a non-existent dependency path (https://github.com/github/licensed/pull/280)
17
+ - Go source no longer crashes on a non-existent dependency package path (https://github.com/github/licensed/pull/274)
18
+
9
19
  ## 2.10.0
10
20
  2020-05-15
11
21
 
@@ -13,7 +23,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
13
23
  - NPM source ignores missing peer dependencies (https://github.com/github/licensed/pull/267)
14
24
 
15
25
  ### Added
16
- - Nuget source (:tada: @zarenner https://github.com/github/licensed/pull/261)
26
+ - NuGet source (:tada: @zarenner https://github.com/github/licensed/pull/261)
17
27
  - Multiple apps can share a single cache location (https://github.com/github/licensed/pull/263)
18
28
 
19
29
  ## 2.9.2
@@ -302,4 +312,4 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
302
312
 
303
313
  Initial release :tada:
304
314
 
305
- [Unreleased]: https://github.com/github/licensed/compare/2.10.0...HEAD
315
+ [Unreleased]: https://github.com/github/licensed/compare/2.11.0...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. For example:
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
 
@@ -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.
@@ -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]"
@@ -6,5 +6,6 @@ module Licensed
6
6
  require "licensed/commands/status"
7
7
  require "licensed/commands/list"
8
8
  require "licensed/commands/environment"
9
+ require "licensed/commands/notices"
9
10
  end
10
11
  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
@@ -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, files| { "sources" => license_content_sources(files), "text" => 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
@@ -7,5 +7,6 @@ module Licensed
7
7
  require "licensed/reporters/list_reporter"
8
8
  require "licensed/reporters/json_reporter"
9
9
  require "licensed/reporters/yaml_reporter"
10
+ require "licensed/reporters/notices_reporter"
10
11
  end
11
12
  end
@@ -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 { |report| report.warnings.any? }.to_a
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 |report|
35
- display_metadata = report.map { |k, v| "#{k}: #{v}" }.join(", ")
34
+ warning_reports.each do |r|
35
+ display_metadata = r.map { |k, v| "#{k}: #{v}" }.join(", ")
36
36
 
37
- shell.warn " * #{report.name}"
37
+ shell.warn " * #{r.name}"
38
38
  shell.warn " #{display_metadata}" unless display_metadata.empty?
39
- report.warnings.each do |warning|
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 { |report| report.errors.any? }.to_a
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 |report|
51
- display_metadata = report.map { |k, v| "#{k}: #{v}" }.join(", ")
50
+ errored_reports.each do |r|
51
+ display_metadata = r.map { |k, v| "#{k}: #{v}" }.join(", ")
52
52
 
53
- shell.error " * #{report.name}"
53
+ shell.error " * #{r.name}"
54
54
  shell.error " #{display_metadata}" unless display_metadata.empty?
55
- report.errors.each do |error|
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 { |report| report.errors.any? }.to_a
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 |report|
36
- display_metadata = report.map { |k, v| "#{k}: #{v}" }.join(", ")
35
+ errored_reports.each do |r|
36
+ display_metadata = r.map { |k, v| "#{k}: #{v}" }.join(", ")
37
37
 
38
- shell.error " * #{report.name}"
38
+ shell.error " * #{r.name}"
39
39
  shell.error " #{display_metadata}" unless display_metadata.empty?
40
- report.errors.each do |error|
40
+ r.errors.each do |error|
41
41
  shell.error " - #{error}"
42
42
  end
43
43
  shell.newline
@@ -0,0 +1,68 @@
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
+ texts.concat(cached_record.notices)
59
+
60
+ <<~NOTICE
61
+ #{cached_record["name"]}@#{cached_record["version"]}
62
+
63
+ #{texts.map(&:strip).reject(&:empty?).compact.join(TEXT_SEPARATOR)}
64
+ NOTICE
65
+ end
66
+ end
67
+ end
68
+ end
@@ -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 { |report| report.errors.any? }.to_a
18
+ errored_reports = all_reports.select { |r| r.errors.any? }.to_a
19
19
 
20
- dependency_count = all_reports.select { |report| report.target.is_a?(Licensed::Dependency) }.size
21
- error_count = errored_reports.sum { |report| report.errors.size }
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 |report|
27
- display_metadata = report.map { |k, v| "#{k}: #{v}" }.join(", ")
26
+ errored_reports.each do |r|
27
+ display_metadata = r.map { |k, v| "#{k}: #{v}" }.join(", ")
28
28
 
29
- shell.error "* #{report.name}"
29
+ shell.error "* #{r.name}"
30
30
  shell.error " #{display_metadata}" unless display_metadata.empty?
31
- report.errors.each do |error|
31
+ r.errors.each do |error|
32
32
  shell.error " - #{error}"
33
33
  end
34
34
  shell.newline
@@ -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, info|
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
- info[key] = value
118
+ hsh[key] = value
119
119
  end
120
120
  end
121
121
 
@@ -120,7 +120,7 @@ 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
@@ -17,10 +17,13 @@ module Licensed
17
17
  PROJECT_URL_REGEX = /<projectUrl>\s*(.*)\s*<\/projectUrl>/ix.freeze
18
18
  PROJECT_DESC_REGEX = /<description>\s*(.*)\s*<\/description>/ix.freeze
19
19
 
20
- def initialize(name:, version:, path:, search_root: nil, metadata: {}, errors: [])
21
- super(name: name, version: version, path: path, search_root: search_root, metadata: metadata, errors: errors)
22
- @metadata["homepage"] = project_url if project_url
23
- @metadata["summary"] = description if description
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
24
27
  end
25
28
 
26
29
  def nuspec_path
@@ -29,14 +32,17 @@ module Licensed
29
32
  end
30
33
 
31
34
  def nuspec_contents
32
- return unless nuspec_path
33
- @nuspec_contents ||= File.read(nuspec_path)
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
34
40
  end
35
41
 
36
42
  def project_url
37
43
  return @project_url if defined?(@project_url)
38
- return unless nuspec_contents
39
44
  @project_url = begin
45
+ return unless nuspec_contents
40
46
  match = nuspec_contents.match PROJECT_URL_REGEX
41
47
  match[1] if match && match[1]
42
48
  end
@@ -44,8 +50,8 @@ module Licensed
44
50
 
45
51
  def description
46
52
  return @description if defined?(@description)
47
- return unless nuspec_contents
48
53
  @description = begin
54
+ return unless nuspec_contents
49
55
  match = nuspec_contents.match PROJECT_DESC_REGEX
50
56
  match[1] if match && match[1]
51
57
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module Licensed
3
- VERSION = "2.10.0".freeze
3
+ VERSION = "2.11.0".freeze
4
4
 
5
5
  def self.previous_major_versions
6
6
  major_version = Gem::Version.new(Licensed::VERSION).segments.first
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.10.0
4
+ version: 2.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitHub
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-05-15 00:00:00.000000000 Z
11
+ date: 2020-06-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: licensee
@@ -284,6 +284,7 @@ files:
284
284
  - lib/licensed/commands/command.rb
285
285
  - lib/licensed/commands/environment.rb
286
286
  - lib/licensed/commands/list.rb
287
+ - lib/licensed/commands/notices.rb
287
288
  - lib/licensed/commands/status.rb
288
289
  - lib/licensed/configuration.rb
289
290
  - lib/licensed/dependency.rb
@@ -295,6 +296,7 @@ files:
295
296
  - lib/licensed/reporters/cache_reporter.rb
296
297
  - lib/licensed/reporters/json_reporter.rb
297
298
  - lib/licensed/reporters/list_reporter.rb
299
+ - lib/licensed/reporters/notices_reporter.rb
298
300
  - lib/licensed/reporters/reporter.rb
299
301
  - lib/licensed/reporters/status_reporter.rb
300
302
  - lib/licensed/reporters/yaml_reporter.rb