licensed 2.15.2 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +55 -11
  3. data/CHANGELOG.md +56 -1
  4. data/README.md +38 -81
  5. data/docs/adding_a_new_source.md +11 -8
  6. data/docs/commands/README.md +59 -0
  7. data/docs/commands/cache.md +35 -0
  8. data/docs/commands/env.md +10 -0
  9. data/docs/commands/list.md +23 -0
  10. data/docs/commands/migrate.md +10 -0
  11. data/docs/commands/notices.md +12 -0
  12. data/docs/commands/status.md +73 -0
  13. data/docs/commands/version.md +3 -0
  14. data/docs/configuration.md +9 -161
  15. data/docs/configuration/README.md +11 -0
  16. data/docs/configuration/allowed_licenses.md +17 -0
  17. data/docs/configuration/application_name.md +63 -0
  18. data/docs/configuration/application_source.md +64 -0
  19. data/docs/configuration/configuration_root.md +27 -0
  20. data/docs/configuration/configuring_multiple_apps.md +58 -0
  21. data/docs/configuration/dependency_source_enumerators.md +28 -0
  22. data/docs/configuration/ignoring_dependencies.md +19 -0
  23. data/docs/configuration/metadata_cache.md +106 -0
  24. data/docs/configuration/reviewing_dependencies.md +18 -0
  25. data/docs/{migrating_to_newer_versions.md → migrations/v2.md} +1 -1
  26. data/docs/migrations/v3.md +109 -0
  27. data/docs/sources/bundler.md +1 -11
  28. data/docs/sources/swift.md +4 -0
  29. data/lib/licensed.rb +1 -0
  30. data/lib/licensed/cli.rb +6 -3
  31. data/lib/licensed/commands/cache.rb +19 -20
  32. data/lib/licensed/commands/command.rb +104 -72
  33. data/lib/licensed/commands/environment.rb +12 -11
  34. data/lib/licensed/commands/list.rb +0 -19
  35. data/lib/licensed/commands/notices.rb +0 -19
  36. data/lib/licensed/commands/status.rb +13 -15
  37. data/lib/licensed/configuration.rb +105 -12
  38. data/lib/licensed/report.rb +44 -0
  39. data/lib/licensed/reporters/cache_reporter.rb +48 -64
  40. data/lib/licensed/reporters/json_reporter.rb +19 -21
  41. data/lib/licensed/reporters/list_reporter.rb +45 -58
  42. data/lib/licensed/reporters/notices_reporter.rb +33 -46
  43. data/lib/licensed/reporters/reporter.rb +37 -104
  44. data/lib/licensed/reporters/status_reporter.rb +58 -56
  45. data/lib/licensed/reporters/yaml_reporter.rb +19 -21
  46. data/lib/licensed/sources.rb +1 -0
  47. data/lib/licensed/sources/bundler.rb +36 -217
  48. data/lib/licensed/sources/bundler/missing_specification.rb +54 -0
  49. data/lib/licensed/sources/go.rb +1 -1
  50. data/lib/licensed/sources/gradle.rb +2 -2
  51. data/lib/licensed/sources/npm.rb +4 -3
  52. data/lib/licensed/sources/nuget.rb +57 -27
  53. data/lib/licensed/sources/swift.rb +69 -0
  54. data/lib/licensed/version.rb +1 -1
  55. data/script/source-setup/go +1 -1
  56. data/script/source-setup/swift +22 -0
  57. metadata +27 -4
  58. data/docs/commands.md +0 -95
@@ -3,84 +3,71 @@
3
3
  module Licensed
4
4
  module Reporters
5
5
  class ListReporter < Reporter
6
- # Reports on an application configuration in a list command run
6
+ # Reports the start of application configuration in a list command run
7
7
  #
8
8
  # app - An application configuration
9
- #
10
- # Returns the result of the yielded method
11
- # Note - must be called from inside the `report_run` scope
12
- def report_app(app)
13
- super do |report|
14
- shell.info "Listing dependencies for #{app["name"]}"
15
- yield report
16
- end
9
+ # report - A report object containing information about the app evaluation
10
+ def begin_report_app(app, report)
11
+ shell.info "Listing dependencies for #{app["name"]}"
17
12
  end
18
13
 
19
- # Reports on a dependency source enumerator in a list command run.
20
- # Shows the type and count of dependencies found by the source.
14
+ # Reports the start of a source evaluation
21
15
  #
22
16
  # source - A dependency source enumerator
23
- #
24
- # Returns the result of the yielded method
25
- # Note - must be called from inside the `report_run` scope
26
- def report_source(source)
27
- super do |report|
28
- shell.info " #{source.class.type}"
29
- result = yield report
17
+ # report - A report object containing information about the source evaluation
18
+ def begin_report_source(source, report)
19
+ shell.info " #{source.class.type}"
20
+ end
30
21
 
31
- warning_reports = report.all_reports.select { |r| r.warnings.any? }.to_a
32
- if warning_reports.any?
33
- shell.newline
34
- shell.warn " * Warnings:"
35
- warning_reports.each do |r|
36
- display_metadata = r.map { |k, v| "#{k}: #{v}" }.join(", ")
22
+ # Report the type and count of dependencies found by the source,
23
+ # along with any warnings and errors
24
+ #
25
+ # source - A dependency source enumerator
26
+ # report - A report object containing information about the source evaluation
27
+ def end_report_source(source, report)
28
+ warning_reports = report.all_reports.select { |r| r.warnings.any? }.to_a
29
+ if warning_reports.any?
30
+ shell.newline
31
+ shell.warn " * Warnings:"
32
+ warning_reports.each do |r|
33
+ display_metadata = r.map { |k, v| "#{k}: #{v}" }.join(", ")
37
34
 
38
- shell.warn " * #{r.name}"
39
- shell.warn " #{display_metadata}" unless display_metadata.empty?
40
- r.warnings.each do |warning|
41
- shell.warn " - #{warning}"
42
- end
43
- shell.newline
35
+ shell.warn " * #{r.name}"
36
+ shell.warn " #{display_metadata}" unless display_metadata.empty?
37
+ r.warnings.each do |warning|
38
+ shell.warn " - #{warning}"
44
39
  end
40
+ shell.newline
45
41
  end
42
+ end
46
43
 
47
- errored_reports = report.all_reports.select { |r| r.errors.any? }.to_a
48
- if errored_reports.any?
49
- shell.newline
50
- shell.error " * Errors:"
51
- errored_reports.each do |r|
52
- display_metadata = r.map { |k, v| "#{k}: #{v}" }.join(", ")
44
+ errored_reports = report.all_reports.select { |r| r.errors.any? }.to_a
45
+ if errored_reports.any?
46
+ shell.newline
47
+ shell.error " * Errors:"
48
+ errored_reports.each do |r|
49
+ display_metadata = r.map { |k, v| "#{k}: #{v}" }.join(", ")
53
50
 
54
- shell.error " * #{r.name}"
55
- shell.error " #{display_metadata}" unless display_metadata.empty?
56
- r.errors.each do |error|
57
- shell.error " - #{error}"
58
- end
59
- shell.newline
51
+ shell.error " * #{r.name}"
52
+ shell.error " #{display_metadata}" unless display_metadata.empty?
53
+ r.errors.each do |error|
54
+ shell.error " - #{error}"
60
55
  end
61
- else
62
- shell.confirm " * #{report.reports.size} #{source.class.type} dependencies"
56
+ shell.newline
63
57
  end
64
-
65
- result
58
+ else
59
+ shell.confirm " * #{report.reports.size} #{source.class.type} dependencies"
66
60
  end
67
61
  end
68
62
 
69
63
  # Reports on a dependency in a list command run.
70
64
  #
71
65
  # dependency - An application dependency
72
- #
73
- # Returns the result of the yielded method
74
- # Note - must be called from inside the `report_run` scope
75
- def report_dependency(dependency)
76
- super do |report|
77
- result = yield report
78
- info = "#{dependency.name} (#{dependency.version})"
79
- info = "#{info}: #{report["license"]}" if report["license"]
80
- shell.info " #{info}"
81
-
82
- result
83
- end
66
+ # report - A report object containing information about the dependency evaluation
67
+ def end_report_dependency(dependency, report)
68
+ info = "#{dependency.name} (#{dependency.version})"
69
+ info = "#{info}: #{report["license"]}" if report["license"]
70
+ shell.info " #{info}"
84
71
  end
85
72
  end
86
73
  end
@@ -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.select { |r| r.target.is_a?(Licensed::Dependency) }.size
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