licensed 3.1.0 → 3.2.3

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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +19 -0
  3. data/.github/workflows/release.yml +4 -4
  4. data/.github/workflows/test.yml +169 -48
  5. data/.ruby-version +1 -1
  6. data/CHANGELOG.md +51 -1
  7. data/README.md +25 -80
  8. data/docker/Dockerfile.build-linux +1 -1
  9. data/docs/adding_a_new_source.md +11 -8
  10. data/docs/commands/README.md +59 -0
  11. data/docs/commands/cache.md +35 -0
  12. data/docs/commands/env.md +10 -0
  13. data/docs/commands/list.md +23 -0
  14. data/docs/commands/migrate.md +10 -0
  15. data/docs/commands/notices.md +12 -0
  16. data/docs/commands/status.md +74 -0
  17. data/docs/commands/version.md +3 -0
  18. data/docs/configuration/README.md +11 -0
  19. data/docs/configuration/allowed_licenses.md +17 -0
  20. data/docs/configuration/application_name.md +63 -0
  21. data/docs/configuration/application_source.md +64 -0
  22. data/docs/configuration/configuration_root.md +27 -0
  23. data/docs/configuration/configuring_multiple_apps.md +58 -0
  24. data/docs/configuration/dependency_source_enumerators.md +28 -0
  25. data/docs/configuration/ignoring_dependencies.md +19 -0
  26. data/docs/configuration/metadata_cache.md +106 -0
  27. data/docs/configuration/reviewing_dependencies.md +18 -0
  28. data/docs/configuration.md +9 -173
  29. data/lib/licensed/cli.rb +2 -2
  30. data/lib/licensed/commands/cache.rb +21 -20
  31. data/lib/licensed/commands/command.rb +108 -73
  32. data/lib/licensed/commands/environment.rb +12 -11
  33. data/lib/licensed/commands/list.rb +0 -19
  34. data/lib/licensed/commands/notices.rb +0 -19
  35. data/lib/licensed/commands/status.rb +13 -15
  36. data/lib/licensed/configuration.rb +77 -7
  37. data/lib/licensed/report.rb +44 -0
  38. data/lib/licensed/reporters/cache_reporter.rb +48 -64
  39. data/lib/licensed/reporters/json_reporter.rb +19 -21
  40. data/lib/licensed/reporters/list_reporter.rb +45 -58
  41. data/lib/licensed/reporters/notices_reporter.rb +33 -46
  42. data/lib/licensed/reporters/reporter.rb +37 -104
  43. data/lib/licensed/reporters/status_reporter.rb +58 -56
  44. data/lib/licensed/reporters/yaml_reporter.rb +19 -21
  45. data/lib/licensed/sources/bundler/definition.rb +36 -0
  46. data/lib/licensed/sources/bundler/missing_specification.rb +10 -7
  47. data/lib/licensed/sources/bundler.rb +34 -70
  48. data/lib/licensed/sources/dep.rb +2 -2
  49. data/lib/licensed/sources/go.rb +3 -3
  50. data/lib/licensed/sources/gradle.rb +2 -2
  51. data/lib/licensed/sources/helpers/content_versioning.rb +2 -1
  52. data/lib/licensed/sources/npm.rb +4 -3
  53. data/lib/licensed/sources/nuget.rb +1 -2
  54. data/lib/licensed/version.rb +1 -1
  55. data/lib/licensed.rb +1 -0
  56. data/licensed.gemspec +4 -4
  57. data/script/source-setup/go +1 -1
  58. metadata +45 -13
  59. data/docs/commands.md +0 -95
@@ -6,66 +6,47 @@ module Licensed
6
6
  TEXT_SEPARATOR = "\n\n#{("-" * 5)}\n\n".freeze
7
7
  LICENSE_SEPARATOR = "\n#{("*" * 5)}\n".freeze
8
8
 
9
- # Reports on an application configuration in a notices command run
9
+ # Reports the start of an application evaluation in a notices command run
10
10
  #
11
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
12
+ # report - A report object containing information about the app evaluation
13
+ def begin_report_app(app, report)
14
+ shell.info "Writing notices for #{app["name"]} to #{app_notices_path(app)}"
15
+ end
31
16
 
32
- result
17
+ # Writes the licensing information gathered during the application evaluation
18
+ # to a notices file
19
+ #
20
+ # app - An application configuration
21
+ # report - A report object containing information about the app evaluation
22
+ def end_report_app(app, report)
23
+ File.open(app_notices_path(app), "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)
33
30
  end
34
31
  end
35
32
 
36
-
37
- # Reports on a dependency source enumerator in a notices command run.
38
- # Shows warnings encountered during the run.
33
+ # Reports any warnings encountered during the run.
39
34
  #
40
35
  # source - A dependency source enumerator
41
- #
42
- # Returns the result of the yielded method
43
- # Note - must be called from inside the `report_run` scope
44
- def report_source(source)
45
- super do |report|
46
- result = yield report
47
-
48
- report.warnings.each do |warning|
49
- shell.warn "* #{report.name}: #{warning}"
50
- end
51
-
52
- result
36
+ # report - A report object containing information about the source evaluation
37
+ def end_report_source(source, report)
38
+ report.warnings.each do |warning|
39
+ shell.warn "* #{report.name}: #{warning}"
53
40
  end
54
41
  end
55
42
 
56
43
  # Reports on a dependency in a notices command run.
57
44
  #
58
45
  # dependency - An application dependency
59
- #
60
- # Returns the result of the yielded method
61
- # Note - must be called from inside the `report_run` scope
62
- def report_dependency(dependency)
63
- super do |report|
64
- result = yield report
65
- report.warnings.each do |warning|
66
- shell.warn "* #{report.name}: #{warning}"
67
- end
68
- result
46
+ # report - A report object containing information about the dependency evaluation
47
+ def end_report_dependency(dependency, report)
48
+ report.warnings.each do |warning|
49
+ shell.warn "* #{report.name}: #{warning}"
69
50
  end
70
51
  end
71
52
 
@@ -94,6 +75,12 @@ module Licensed
94
75
  #{texts.map(&:strip).reject(&:empty?).compact.join(TEXT_SEPARATOR)}
95
76
  NOTICE
96
77
  end
78
+
79
+ # Returns the path to an applications notices file
80
+ def app_notices_path(app)
81
+ filename = app["shared_cache"] ? "NOTICE.#{app["name"]}" : "NOTICE"
82
+ app.cache_path.join(filename)
83
+ end
97
84
  end
98
85
  end
99
86
  end
@@ -2,133 +2,66 @@
2
2
  module Licensed
3
3
  module Reporters
4
4
  class Reporter
5
- class Report < Hash
6
- attr_reader :name
7
- attr_reader :target
8
- def initialize(name:, target:)
9
- super()
10
- @name = name
11
- @target = target
12
- end
13
-
14
- def reports
15
- @reports ||= []
16
- end
17
-
18
- def errors
19
- @errors ||= []
20
- end
21
-
22
- def warnings
23
- @warnings ||= []
24
- end
25
-
26
- def all_reports
27
- result = []
28
- result << self
29
- result.push(*reports.flat_map(&:all_reports))
30
- end
31
-
32
- # Returns the data from the report as a hash
33
- def to_h
34
- # add name, errors and warnings if they have real data
35
- output = {}
36
- output["name"] = name unless name.to_s.empty?
37
- output["errors"] = errors.dup if errors.any?
38
- output["warnings"] = warnings.dup if warnings.any?
39
-
40
- # merge the hash data from the report. command-specified data always
41
- # overwrites local data
42
- output.merge(super)
43
- end
44
- end
45
-
46
5
  class ReportingError < StandardError; end;
47
6
 
48
7
  def initialize(shell = Licensed::UI::Shell.new)
49
8
  @shell = shell
50
- @run_report = nil
51
- @app_report = nil
52
- @source_report = nil
53
9
  end
54
10
 
55
- # Generate a report for a licensed command execution
56
- # Yields a report object which can be used to view or add
57
- # data generated for this run
11
+ # Report the beginning of a command evaluation
58
12
  #
59
- # Returns the result of the yielded method
60
- def report_run(command)
61
- result = nil
62
- @run_report = Report.new(name: nil, target: command)
63
- begin
64
- result = yield @run_report
65
- ensure
66
- @run_report = nil
67
- end
13
+ # command - The command being run
14
+ # report - A report object containing information about the command run
15
+ def begin_report_command(command, report)
16
+ end
68
17
 
69
- result
18
+ # Report the end of a command evaluation
19
+ #
20
+ # command - The command being run
21
+ # report - A report object containing information about the command run
22
+ def end_report_command(command, report)
70
23
  end
71
24
 
72
- # Generate a report for a licensed app configuration
73
- # Yields a report object which can be used to view or add
74
- # data generated for this app
25
+ # Report the beginning of an app evaluation
75
26
  #
76
27
  # app - An application configuration
77
- #
78
- # Returns the result of the yielded method
79
- # Note - must be called from inside the `report_run` scope
80
- def report_app(app)
81
- raise ReportingError.new("Cannot call report_app with active app context") unless @app_report.nil?
82
- raise ReportingError.new("Call report_run before report_app") if @run_report.nil?
83
- result = nil
84
- @app_report = Report.new(name: app["name"], target: app)
85
- begin
86
- result = yield @app_report
87
- ensure
88
- @run_report.reports << @app_report
89
- @app_report = nil
90
- end
28
+ # report - A report object containing information about the app evaluation
29
+ def begin_report_app(app, report)
30
+ end
91
31
 
92
- result
32
+ # Report the end of an app evaluation
33
+ #
34
+ # app - An application configuration
35
+ # report - A report object containing information about the app evaluation
36
+ def end_report_app(app, report)
93
37
  end
94
38
 
95
- # Generate a report for a licensed dependency source enumerator
96
- # Yields a report object which can be used to view or add
97
- # data generated for this dependency source
39
+ # Report the beginning of a source evaluation
98
40
  #
99
41
  # source - A dependency source enumerator
100
- #
101
- # Returns the result of the yielded method
102
- # Note - must be called from inside the `report_app` scope
103
- def report_source(source)
104
- raise ReportingError.new("Cannot call report_source with active source context") unless @source_report.nil?
105
- raise ReportingError.new("Call report_app before report_source") if @app_report.nil?
106
- result = nil
107
- @source_report = Report.new(name: [@app_report.name, source.class.type].join("."), target: source)
108
- begin
109
- result = yield @source_report
110
- ensure
111
- @app_report.reports << @source_report
112
- @source_report = nil
113
- end
42
+ # report - A report object containing information about the source evaluation
43
+ def begin_report_source(source, report)
44
+ end
114
45
 
115
- result
46
+ # Report the end of a source evaluation
47
+ #
48
+ # source - A dependency source enumerator
49
+ # report - A report object containing information about the source evaluation
50
+ def end_report_source(source, report)
116
51
  end
117
52
 
118
- # Generate a report for a licensed dependency
119
- # Yields a report object which can be used to view or add
120
- # data generated for this dependency
53
+ # Report the beginning of a dependency evaluation
121
54
  #
122
55
  # dependency - An application dependency
123
- #
124
- # Returns the result of the yielded method
125
- # Note - must be called from inside the `report_source` scope
126
- def report_dependency(dependency)
127
- raise ReportingError.new("Call report_source before report_dependency") if @source_report.nil?
56
+ # source - A report object containing information about the dependency evaluation
57
+ def begin_report_dependency(dependency, report)
58
+ end
128
59
 
129
- dependency_report = Report.new(name: [@source_report.name, dependency.name].join("."), target: dependency)
130
- @source_report.reports << dependency_report
131
- yield dependency_report
60
+ # Report the end of a dependency evaluation
61
+ #
62
+ # dependency - An application dependency
63
+ # source - A report object containing information about the dependency evaluation
64
+ def end_report_dependency(dependency, report)
132
65
  end
133
66
 
134
67
  protected
@@ -3,80 +3,82 @@
3
3
  module Licensed
4
4
  module Reporters
5
5
  class StatusReporter < Reporter
6
- # Generate a report for a licensed status command run
7
- # Shows the errors found when checking status, as well as
8
- # overall number of dependencies checked
6
+ # Reports any errors encountered at the command level
9
7
  #
10
- # Returns the result of the yielded method
11
- def report_app(app)
12
- super do |report|
13
- shell.info "Checking cached dependency records for #{app["name"]}"
8
+ # command - The command being run
9
+ # report - A report object containing information about the command run
10
+ def end_report_command(command, report)
11
+ if report.errors.any?
12
+ shell.newline
13
+ report.errors.each { |e| shell.error e }
14
+ end
15
+ end
14
16
 
15
- result = yield report
17
+ # Reports the start of checking records for an app
18
+ #
19
+ # app - An application configuration
20
+ # report - A report containing information about the app evaluation
21
+ def begin_report_app(app, report)
22
+ shell.info "Checking cached dependency records for #{app["name"]}"
23
+ end
16
24
 
17
- all_reports = report.all_reports
25
+ # Reports any errors found when checking status, as well as
26
+ # overall number of dependencies checked
27
+ #
28
+ # app - An application configuration
29
+ # report - A report containing information about the app evaluation
30
+ def end_report_app(app, report)
31
+ all_reports = report.all_reports
18
32
 
19
- warning_reports = all_reports.select { |r| r.warnings.any? }.to_a
20
- if warning_reports.any?
21
- shell.newline
22
- shell.warn "Warnings:"
23
- warning_reports.each do |r|
24
- display_metadata = r.map { |k, v| "#{k}: #{v}" }.join(", ")
33
+ warning_reports = all_reports.select { |r| r.warnings.any? }.to_a
34
+ if warning_reports.any?
35
+ shell.newline
36
+ shell.warn "Warnings:"
37
+ warning_reports.each do |r|
38
+ display_metadata = r.map { |k, v| "#{k}: #{v}" }.join(", ")
25
39
 
26
- shell.warn "* #{r.name}"
27
- shell.warn " #{display_metadata}" unless display_metadata.empty?
28
- r.warnings.each do |warning|
29
- shell.warn " - #{warning}"
30
- end
31
- shell.newline
40
+ shell.warn "* #{r.name}"
41
+ shell.warn " #{display_metadata}" unless display_metadata.empty?
42
+ r.warnings.each do |warning|
43
+ shell.warn " - #{warning}"
32
44
  end
45
+ shell.newline
33
46
  end
47
+ end
34
48
 
35
- errored_reports = all_reports.select { |r| r.errors.any? }.to_a
49
+ errored_reports = all_reports.select { |r| r.errors.any? }.to_a
36
50
 
37
- dependency_count = all_reports.select { |r| r.target.is_a?(Licensed::Dependency) }.size
38
- error_count = errored_reports.sum { |r| r.errors.size }
51
+ dependency_count = all_reports.count { |r| r.target.is_a?(Licensed::Dependency) }
52
+ error_count = errored_reports.sum { |r| r.errors.size }
39
53
 
40
- if error_count > 0
41
- shell.newline
42
- shell.error "Errors:"
43
- errored_reports.each do |r|
44
- display_metadata = r.map { |k, v| "#{k}: #{v}" }.join(", ")
54
+ if error_count > 0
55
+ shell.newline
56
+ shell.error "Errors:"
57
+ errored_reports.each do |r|
58
+ display_metadata = r.map { |k, v| "#{k}: #{v}" }.join(", ")
45
59
 
46
- shell.error "* #{r.name}"
47
- shell.error " #{display_metadata}" unless display_metadata.empty?
48
- r.errors.each do |error|
49
- shell.error " - #{error}"
50
- end
51
- shell.newline
60
+ shell.error "* #{r.name}"
61
+ shell.error " #{display_metadata}" unless display_metadata.empty?
62
+ r.errors.each do |error|
63
+ shell.error " - #{error}"
52
64
  end
65
+ shell.newline
53
66
  end
54
-
55
- shell.newline
56
- shell.info "#{dependency_count} dependencies checked, #{error_count} errors found."
57
-
58
- result
59
67
  end
68
+
69
+ shell.newline
70
+ shell.info "#{dependency_count} dependencies checked, #{error_count} errors found."
60
71
  end
61
72
 
62
- # Reports on a dependency in a status command run.
63
- # Shows whether the dependency's status is valid in dot format
73
+ # Reports whether the dependency's status is valid in dot format
64
74
  #
65
75
  # dependency - An application dependency
66
- #
67
- # Returns the result of the yielded method
68
- # Note - must be called from inside the `report_run` scope
69
- def report_dependency(dependency)
70
- super do |report|
71
- result = yield report
72
-
73
- if report.errors.empty?
74
- shell.confirm(".", false)
75
- else
76
- shell.error("F", false)
77
- end
78
-
79
- result
76
+ # report - A report containing information about the dependency evaluation
77
+ def end_report_dependency(dependency, report)
78
+ if report.errors.empty?
79
+ shell.confirm(".", false)
80
+ else
81
+ shell.error("F", false)
80
82
  end
81
83
  end
82
84
  end
@@ -2,31 +2,29 @@
2
2
  module Licensed
3
3
  module Reporters
4
4
  class YamlReporter < Reporter
5
- def report_run(command)
6
- super do |report|
7
- result = yield report
8
-
9
- report["apps"] = report.reports.map(&:to_h) if report.reports.any?
10
- shell.info sanitize(report.to_h).to_yaml
11
-
12
- result
13
- end
5
+ # Report all information from the command run to the shell as a YAML object
6
+ #
7
+ # command - The command being run
8
+ # report - A report object containing information about the command run
9
+ def end_report_command(command, report)
10
+ report["apps"] = report.reports.map(&:to_h) if report.reports.any?
11
+ shell.info sanitize(report.to_h).to_yaml
14
12
  end
15
13
 
16
- def report_app(app)
17
- super do |report|
18
- result = yield report
19
- report["sources"] = report.reports.map(&:to_h) if report.reports.any?
20
- result
21
- end
14
+ # Add source report information to the app report hash
15
+ #
16
+ # app - An application configuration
17
+ # report - A report object containing information about the app evaluation
18
+ def end_report_app(app, report)
19
+ report["sources"] = report.reports.map(&:to_h) if report.reports.any?
22
20
  end
23
21
 
24
- def report_source(source)
25
- super do |report|
26
- result = yield report
27
- report["dependencies"] = report.reports.map(&:to_h) if report.reports.any?
28
- result
29
- end
22
+ # Add dependency report information to the source report hash
23
+ #
24
+ # source - A dependency source enumerator
25
+ # report - A report object containing information about the source evaluation
26
+ def end_report_source(source, report)
27
+ report["dependencies"] = report.reports.map(&:to_h) if report.reports.any?
30
28
  end
31
29
 
32
30
  def sanitize(object)