licensed 2.15.2 → 3.2.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.
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
@@ -2,17 +2,7 @@
2
2
 
3
3
  The bundler source will detect dependencies `Gemfile` and `Gemfile.lock` files are found at an apps `source_path`. The source uses the `Bundler` API to enumerate dependencies from `Gemfile` and `Gemfile.lock`.
4
4
 
5
- ### Enumerating bundler dependencies when using the licensed executable
6
-
7
- **Note** this content only applies to running licensed from an executable. It does not apply when using licensed as a gem.
8
-
9
- _It is required that the ruby runtime is available when running the licensed executable._
10
-
11
- The licensed executable contains and runs a version of ruby. When using the Bundler APIs, a mismatch between the version of ruby built into the licensed executable and the version of licensed used during `bundle install` can occur. This mismatch can lead to licensed raising errors due to not finding dependencies.
12
-
13
- For example, if `bundle install` was run with ruby 2.5.0 then the bundler specification path would be `<bundle path>/ruby/2.5.0/specifications`. However, if the licensed executable contains ruby 2.4.0, then licensed will be looking for specifications at `<bundle path>/ruby/2.4.0/specifications`. That path may not exist, or it may contain invalid or stale content.
14
-
15
- To prevent confusion, licensed uses the local ruby runtime to determine the ruby version for local gems during `bundle install`. If bundler is also available, then the ruby command will be run from a `bundle exec` context.
5
+ **Note** The bundler source cannot be used when running the [packaged licensed executable](../packaging.md)
16
6
 
17
7
  ### Excluding gem groups
18
8
 
@@ -0,0 +1,4 @@
1
+ # Swift
2
+
3
+ The Swift source uses `swift package` subcommands
4
+ to enumerate dependencies and properties.
data/lib/licensed.rb CHANGED
@@ -6,6 +6,7 @@ require "licensed/dependency"
6
6
  require "licensed/git"
7
7
  require "licensed/sources"
8
8
  require "licensed/configuration"
9
+ require "licensed/report"
9
10
  require "licensed/reporters"
10
11
  require "licensed/commands"
11
12
  require "licensed/ui/shell"
data/lib/licensed/cli.rb CHANGED
@@ -20,12 +20,12 @@ module Licensed
20
20
  end
21
21
 
22
22
  desc "status", "Check status of dependencies' cached licenses"
23
- method_option :format, enum: ["yaml", "json"],
24
- desc: "Output format"
25
23
  method_option :config, aliases: "-c", type: :string,
26
24
  desc: "Path to licensed configuration file"
27
25
  method_option :sources, aliases: "-s", type: :array,
28
26
  desc: "Individual source(s) to evaluate. Must also be enabled via configuration."
27
+ method_option :format, aliases: "-f", enum: ["yaml", "json"],
28
+ desc: "Output format"
29
29
  def status
30
30
  run Licensed::Commands::Status.new(config: config), sources: options[:sources], reporter: options[:format]
31
31
  end
@@ -74,11 +74,14 @@ module Licensed
74
74
  method_option :from, aliases: "-f", type: :string, required: true,
75
75
  desc: "Licensed version to migrate from - #{Licensed.previous_major_versions.map { |major| "v#{major}" }.join(", ")}"
76
76
  def migrate
77
+ shell = Thor::Base.shell.new
77
78
  case options["from"]
78
79
  when "v1"
79
80
  Licensed::Migrations::V2.migrate(options["config"])
81
+ when "v2"
82
+ shell.say "No configuration or cached file migration needed."
83
+ shell.say "Please see the documentation at https://github.com/github/licensed/tree/master/docs/migrations/v3.md for details."
80
84
  else
81
- shell = Thor::Base.shell.new
82
85
  shell.say "Unrecognized option from=#{options["from"]}", :red
83
86
  CLI.command_help(shell, "migrate")
84
87
  exit 1
@@ -11,6 +11,8 @@ module Licensed
11
11
  Licensed::Reporters::CacheReporter.new
12
12
  end
13
13
 
14
+ protected
15
+
14
16
  # Run the command.
15
17
  # Removes any cached records that don't match a current application
16
18
  # dependency.
@@ -18,20 +20,15 @@ module Licensed
18
20
  # options - Options to run the command with
19
21
  #
20
22
  # Returns whether the command was a success
21
- def run(**options)
22
- begin
23
- result = super
23
+ def run_command(report)
24
+ super do |result|
24
25
  clear_stale_cached_records if result
25
-
26
- result
27
- ensure
28
- cache_paths.clear
29
- files.clear
30
26
  end
27
+ ensure
28
+ cache_paths.clear
29
+ files.clear
31
30
  end
32
31
 
33
- protected
34
-
35
32
  # Run the command for all enumerated dependencies found in a dependency source,
36
33
  # recording results in a report.
37
34
  # Enumerating dependencies in the source is skipped if a :sources option
@@ -41,17 +38,12 @@ module Licensed
41
38
  # source - A dependency source enumerator
42
39
  #
43
40
  # Returns whether the command succeeded for the dependency source enumerator
44
- def run_source(app, source)
45
- super do |report|
46
- if Array(options[:sources]).any? && !options[:sources].include?(source.class.type)
47
- report.warnings << "skipped source"
48
- next :skip
49
- end
41
+ def run_source(app, source, report)
42
+ # add the full cache path to the list of cache paths
43
+ # that should be cleaned up after the command run
44
+ cache_paths << app.cache_path.join(source.class.type)
50
45
 
51
- # add the full cache path to the list of cache paths
52
- # that should be cleaned up after the command run
53
- cache_paths << app.cache_path.join(source.class.type)
54
- end
46
+ super
55
47
  end
56
48
 
57
49
  # Cache dependency record data.
@@ -70,6 +62,12 @@ module Licensed
70
62
 
71
63
  filename = app.cache_path.join(source.class.type, "#{dependency.name}.#{DependencyRecord::EXTENSION}")
72
64
  cached_record = Licensed::DependencyRecord.read(filename)
65
+
66
+ report["cached"] = false
67
+ report["license"] = cached_record["license"] if cached_record
68
+ report["filename"] = filename.to_s
69
+ report["version"] = dependency.version
70
+
73
71
  if options[:force] || save_dependency_record?(dependency, cached_record)
74
72
  if dependency.record.matches?(cached_record)
75
73
  # use the cached license value if the license text wasn't updated
@@ -82,6 +80,7 @@ module Licensed
82
80
 
83
81
  dependency.record.save(filename)
84
82
  report["cached"] = true
83
+ report["license"] = dependency.record["license"]
85
84
  end
86
85
 
87
86
  if !dependency.exist?
@@ -18,23 +18,12 @@ module Licensed
18
18
  def run(**options)
19
19
  @options = options
20
20
  @reporter = create_reporter(options)
21
- begin
22
- result = reporter.report_run(self) do |report|
23
- # allow additional report data to be given by commands
24
- if block_given?
25
- next true if (yield report) == :skip
26
- end
27
-
28
- config.apps.sort_by { |app| app["name"] }
29
- .map { |app| run_app(app) }
30
- .all?
31
- end
32
- ensure
33
- @options = nil
34
- @reporter = nil
35
- end
36
21
 
37
- result
22
+ command_report = Licensed::Report.new(name: nil, target: self)
23
+ run_command(command_report)
24
+ ensure
25
+ @options = nil
26
+ @reporter = nil
38
27
  end
39
28
 
40
29
  # Creates a reporter to use during a command run
@@ -64,36 +53,65 @@ module Licensed
64
53
 
65
54
  protected
66
55
 
56
+ # Run the command for all application configurations
57
+ #
58
+ # report - A Licensed::Report object for this command
59
+ #
60
+ # Returns whether the command succeeded
61
+ def run_command(report)
62
+ reporter.begin_report_command(self, report)
63
+ apps = config.apps.sort_by { |app| app["name"] }
64
+ results = apps.map do |app|
65
+ app_report = Licensed::Report.new(name: app["name"], target: app)
66
+ report.reports << app_report
67
+ run_app(app, app_report)
68
+ end
69
+
70
+ result = results.all?
71
+
72
+ yield(result) if block_given?
73
+
74
+ result
75
+ ensure
76
+ reporter.end_report_command(self, report)
77
+ end
78
+
67
79
  # Run the command for all enabled sources for an application configuration,
68
80
  # recording results in a report.
69
81
  #
70
82
  # app - An application configuration
83
+ # report - A report object for this application
71
84
  #
72
85
  # Returns whether the command succeeded for the application.
73
- def run_app(app)
74
- reporter.report_app(app) do |report|
75
- # ensure the app source path exists before evaluation
76
- if !Dir.exist?(app.source_path)
77
- report.errors << "No such directory #{app.source_path}"
78
- next false
79
- end
86
+ def run_app(app, report)
87
+ reporter.begin_report_app(app, report)
80
88
 
81
- Dir.chdir app.source_path do
82
- begin
83
- # allow additional report data to be given by commands
84
- if block_given?
85
- next true if (yield report) == :skip
86
- end
87
-
88
- app.sources.select(&:enabled?)
89
- .sort_by { |source| source.class.type }
90
- .map { |source| run_source(app, source) }.all?
91
- rescue Licensed::Shell::Error => err
92
- report.errors << err.message
93
- false
94
- end
89
+ # ensure the app source path exists before evaluation
90
+ if !Dir.exist?(app.source_path)
91
+ report.errors << "No such directory #{app.source_path}"
92
+ return false
93
+ end
94
+
95
+ Dir.chdir app.source_path do
96
+ sources = app.sources.select(&:enabled?)
97
+ .sort_by { |source| source.class.type }
98
+ results = sources.map do |source|
99
+ source_report = Licensed::Report.new(name: [report.name, source.class.type].join("."), target: source)
100
+ report.reports << source_report
101
+ run_source(app, source, source_report)
95
102
  end
103
+
104
+ result = results.all?
105
+
106
+ yield(result) if block_given?
107
+
108
+ result
96
109
  end
110
+ rescue Licensed::Shell::Error => err
111
+ report.errors << err.message
112
+ false
113
+ ensure
114
+ reporter.end_report_app(app, report)
97
115
  end
98
116
 
99
117
  # Run the command for all enumerated dependencies found in a dependency source,
@@ -101,27 +119,37 @@ module Licensed
101
119
  #
102
120
  # app - The application configuration for the source
103
121
  # source - A dependency source enumerator
122
+ # report - A report object for this source
104
123
  #
105
124
  # Returns whether the command succeeded for the dependency source enumerator
106
- def run_source(app, source)
107
- reporter.report_source(source) do |report|
108
- begin
109
- # allow additional report data to be given by commands
110
- if block_given?
111
- next true if (yield report) == :skip
112
- end
113
-
114
- source.dependencies.sort_by { |dependency| dependency.name }
115
- .map { |dependency| run_dependency(app, source, dependency) }
116
- .all?
117
- rescue Licensed::Shell::Error => err
118
- report.errors << err.message
119
- false
120
- rescue Licensed::Sources::Source::Error => err
121
- report.errors << err.message
122
- false
123
- end
125
+ def run_source(app, source, report)
126
+ reporter.begin_report_source(source, report)
127
+
128
+ if !sources_overrides.empty? && !sources_overrides.include?(source.class.type)
129
+ report.warnings << "skipped source"
130
+ return true
124
131
  end
132
+
133
+ dependencies = source.dependencies.sort_by { |dependency| dependency.name }
134
+ results = dependencies.map do |dependency|
135
+ dependency_report = Licensed::Report.new(name: [report.name, dependency.name].join("."), target: dependency)
136
+ report.reports << dependency_report
137
+ run_dependency(app, source, dependency, dependency_report)
138
+ end
139
+
140
+ result = results.all?
141
+
142
+ yield(result) if block_given?
143
+
144
+ result
145
+ rescue Licensed::Shell::Error => err
146
+ report.errors << err.message
147
+ false
148
+ rescue Licensed::Sources::Source::Error => err
149
+ report.errors << err.message
150
+ false
151
+ ensure
152
+ reporter.end_report_source(source, report)
125
153
  end
126
154
 
127
155
  # Run the command for a dependency, evaluating the dependency and
@@ -131,27 +159,27 @@ module Licensed
131
159
  # app - The application configuration for the dependency
132
160
  # source - The dependency source enumerator for the dependency
133
161
  # dependency - An application dependency
162
+ # report - A report object for this dependency
134
163
  #
135
164
  # Returns whether the command succeeded for the dependency
136
- def run_dependency(app, source, dependency)
137
- reporter.report_dependency(dependency) do |report|
138
- if dependency.errors?
139
- report.errors.concat(dependency.errors)
140
- return false
141
- end
165
+ def run_dependency(app, source, dependency, report)
166
+ reporter.begin_report_dependency(dependency, report)
142
167
 
143
- begin
144
- # allow additional report data to be given by commands
145
- if block_given?
146
- next true if (yield report) == :skip
147
- end
148
-
149
- evaluate_dependency(app, source, dependency, report)
150
- rescue Licensed::DependencyRecord::Error, Licensed::Shell::Error => err
151
- report.errors << err.message
152
- false
153
- end
168
+ if dependency.errors?
169
+ report.errors.concat(dependency.errors)
170
+ return false
154
171
  end
172
+
173
+ result = evaluate_dependency(app, source, dependency, report)
174
+
175
+ yield(result) if block_given?
176
+
177
+ result
178
+ rescue Licensed::DependencyRecord::Error, Licensed::Shell::Error => err
179
+ report.errors << err.message
180
+ false
181
+ ensure
182
+ reporter.end_report_dependency(dependency, report)
155
183
  end
156
184
 
157
185
  # Evaluate a dependency for the command. Must be implemented by a command implementation.
@@ -165,6 +193,10 @@ module Licensed
165
193
  def evaluate_dependency(app, source, dependency, report)
166
194
  raise "`evaluate_dependency` must be implemented by a command"
167
195
  end
196
+
197
+ def sources_overrides
198
+ @sources_overrides = Array(options[:sources])
199
+ end
168
200
  end
169
201
  end
170
202
  end
@@ -29,12 +29,6 @@ module Licensed
29
29
  end
30
30
  end
31
31
 
32
- def run(**options)
33
- super do |report|
34
- report["git_repo"] = Licensed::Git.git_repo?
35
- end
36
- end
37
-
38
32
  # Returns the default reporter to use during the command run
39
33
  #
40
34
  # options - The options the command was run with
@@ -46,11 +40,18 @@ module Licensed
46
40
 
47
41
  protected
48
42
 
49
- def run_app(app)
50
- reporter.report_app(app) do |report|
51
- report.merge! AppEnvironment.new(app).to_h
52
- true
53
- end
43
+ def run_command(report)
44
+ report["git_repo"] = Licensed::Git.git_repo?
45
+ super
46
+ end
47
+
48
+ def run_app(app, report)
49
+ report.merge! AppEnvironment.new(app).to_h
50
+ super
51
+ end
52
+
53
+ def run_source(app, source, report)
54
+ true
54
55
  end
55
56
  end
56
57
  end
@@ -13,25 +13,6 @@ module Licensed
13
13
 
14
14
  protected
15
15
 
16
- # Run the command for all enumerated dependencies found in a dependency source,
17
- # recording results in a report.
18
- # Enumerating dependencies in the source is skipped if a :sources option
19
- # is provided and the evaluated `source.class.type` is not in the :sources values
20
- #
21
- # app - The application configuration for the source
22
- # source - A dependency source enumerator
23
- #
24
- # Returns whether the command succeeded for the dependency source enumerator
25
- def run_source(app, source)
26
- super do |report|
27
- next if Array(options[:sources]).empty?
28
- next if options[:sources].include?(source.class.type)
29
-
30
- report.warnings << "skipped source"
31
- :skip
32
- end
33
- end
34
-
35
16
  # Listing dependencies requires no extra work.
36
17
  #
37
18
  # app - The application configuration for the dependency
@@ -13,25 +13,6 @@ module Licensed
13
13
 
14
14
  protected
15
15
 
16
- # Run the command for all enumerated dependencies found in a dependency source,
17
- # recording results in a report.
18
- # Enumerating dependencies in the source is skipped if a :sources option
19
- # is provided and the evaluated `source.class.type` is not in the :sources values
20
- #
21
- # app - The application configuration for the source
22
- # source - A dependency source enumerator
23
- #
24
- # Returns whether the command succeeded for the dependency source enumerator
25
- def run_source(app, source)
26
- super do |report|
27
- next if Array(options[:sources]).empty?
28
- next if options[:sources].include?(source.class.type)
29
-
30
- report.warnings << "skipped source"
31
- :skip
32
- end
33
- end
34
-
35
16
  # Load stored dependency record data to add to the notices report.
36
17
  #
37
18
  # app - The application configuration for the dependency