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.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +55 -11
- data/CHANGELOG.md +56 -1
- data/README.md +38 -81
- data/docs/adding_a_new_source.md +11 -8
- data/docs/commands/README.md +59 -0
- data/docs/commands/cache.md +35 -0
- data/docs/commands/env.md +10 -0
- data/docs/commands/list.md +23 -0
- data/docs/commands/migrate.md +10 -0
- data/docs/commands/notices.md +12 -0
- data/docs/commands/status.md +73 -0
- data/docs/commands/version.md +3 -0
- data/docs/configuration.md +9 -161
- data/docs/configuration/README.md +11 -0
- data/docs/configuration/allowed_licenses.md +17 -0
- data/docs/configuration/application_name.md +63 -0
- data/docs/configuration/application_source.md +64 -0
- data/docs/configuration/configuration_root.md +27 -0
- data/docs/configuration/configuring_multiple_apps.md +58 -0
- data/docs/configuration/dependency_source_enumerators.md +28 -0
- data/docs/configuration/ignoring_dependencies.md +19 -0
- data/docs/configuration/metadata_cache.md +106 -0
- data/docs/configuration/reviewing_dependencies.md +18 -0
- data/docs/{migrating_to_newer_versions.md → migrations/v2.md} +1 -1
- data/docs/migrations/v3.md +109 -0
- data/docs/sources/bundler.md +1 -11
- data/docs/sources/swift.md +4 -0
- data/lib/licensed.rb +1 -0
- data/lib/licensed/cli.rb +6 -3
- data/lib/licensed/commands/cache.rb +19 -20
- data/lib/licensed/commands/command.rb +104 -72
- data/lib/licensed/commands/environment.rb +12 -11
- data/lib/licensed/commands/list.rb +0 -19
- data/lib/licensed/commands/notices.rb +0 -19
- data/lib/licensed/commands/status.rb +13 -15
- data/lib/licensed/configuration.rb +105 -12
- data/lib/licensed/report.rb +44 -0
- data/lib/licensed/reporters/cache_reporter.rb +48 -64
- data/lib/licensed/reporters/json_reporter.rb +19 -21
- data/lib/licensed/reporters/list_reporter.rb +45 -58
- data/lib/licensed/reporters/notices_reporter.rb +33 -46
- data/lib/licensed/reporters/reporter.rb +37 -104
- data/lib/licensed/reporters/status_reporter.rb +58 -56
- data/lib/licensed/reporters/yaml_reporter.rb +19 -21
- data/lib/licensed/sources.rb +1 -0
- data/lib/licensed/sources/bundler.rb +36 -217
- data/lib/licensed/sources/bundler/missing_specification.rb +54 -0
- data/lib/licensed/sources/go.rb +1 -1
- data/lib/licensed/sources/gradle.rb +2 -2
- data/lib/licensed/sources/npm.rb +4 -3
- data/lib/licensed/sources/nuget.rb +57 -27
- data/lib/licensed/sources/swift.rb +69 -0
- data/lib/licensed/version.rb +1 -1
- data/script/source-setup/go +1 -1
- data/script/source-setup/swift +22 -0
- metadata +27 -4
- data/docs/commands.md +0 -95
data/docs/sources/bundler.md
CHANGED
@@ -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
|
-
|
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
|
|
data/lib/licensed.rb
CHANGED
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
|
22
|
-
|
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
|
-
|
46
|
-
|
47
|
-
|
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
|
-
|
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
|
-
|
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.
|
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
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
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.
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
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.
|
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
|
-
|
144
|
-
|
145
|
-
|
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
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
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
|