licensed 3.0.0 → 3.2.1
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/dependabot.yml +19 -0
- data/.github/workflows/release.yml +4 -4
- data/.github/workflows/test.yml +180 -47
- data/.ruby-version +1 -1
- data/CHANGELOG.md +60 -1
- data/README.md +25 -79
- data/docker/Dockerfile.build-linux +1 -1
- 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 +74 -0
- data/docs/commands/version.md +3 -0
- 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/configuration.md +9 -161
- data/docs/sources/swift.md +4 -0
- data/lib/licensed/cli.rb +2 -2
- 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/bundler/definition.rb +36 -0
- data/lib/licensed/sources/bundler/missing_specification.rb +1 -1
- data/lib/licensed/sources/bundler.rb +38 -86
- data/lib/licensed/sources/dep.rb +2 -2
- data/lib/licensed/sources/go.rb +3 -3
- data/lib/licensed/sources/gradle.rb +2 -2
- data/lib/licensed/sources/helpers/content_versioning.rb +2 -1
- data/lib/licensed/sources/npm.rb +4 -3
- data/lib/licensed/sources/nuget.rb +56 -27
- data/lib/licensed/sources/swift.rb +69 -0
- data/lib/licensed/sources.rb +1 -0
- data/lib/licensed/version.rb +1 -1
- data/lib/licensed.rb +1 -0
- data/licensed.gemspec +4 -4
- data/script/source-setup/go +1 -1
- data/script/source-setup/swift +22 -0
- metadata +48 -13
- data/docs/commands.md +0 -95
@@ -3,80 +3,82 @@
|
|
3
3
|
module Licensed
|
4
4
|
module Reporters
|
5
5
|
class StatusReporter < Reporter
|
6
|
-
#
|
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
|
-
#
|
11
|
-
|
12
|
-
|
13
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
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
|
-
|
49
|
+
errored_reports = all_reports.select { |r| r.errors.any? }.to_a
|
36
50
|
|
37
|
-
|
38
|
-
|
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
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
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
|
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
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
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
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
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)
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Licensed
|
4
|
+
module Bundler
|
5
|
+
module DefinitionExtensions
|
6
|
+
attr_accessor :force_exclude_groups
|
7
|
+
|
8
|
+
# Override specs to avoid logic that would raise Gem::NotFound
|
9
|
+
# which is handled in this ./missing_specification.rb, and to not add
|
10
|
+
# bundler as a dependency if it's not a user-requested gem.
|
11
|
+
#
|
12
|
+
# Newer versions of Bundler have changed the implementation of specs_for
|
13
|
+
# as well which no longer calls this function. Overriding this function
|
14
|
+
# gives a stable access point for licensed
|
15
|
+
def specs
|
16
|
+
@specs ||= begin
|
17
|
+
specs = resolve.materialize(requested_dependencies)
|
18
|
+
|
19
|
+
all_dependencies = requested_dependencies.concat(specs.flat_map(&:dependencies))
|
20
|
+
if all_dependencies.any? { |d| d.name == "bundler" } && !specs["bundler"].any?
|
21
|
+
bundler = sources.metadata_source.specs.search(Gem::Dependency.new("bundler", ::Bundler::VERSION)).last
|
22
|
+
specs["bundler"] = bundler
|
23
|
+
end
|
24
|
+
|
25
|
+
specs
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Override requested_groups to also exclude any groups that are
|
30
|
+
# in the "bundler.without" section of the licensed configuration file.
|
31
|
+
def requested_groups
|
32
|
+
super - Array(force_exclude_groups)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -48,7 +48,7 @@ module Bundler
|
|
48
48
|
spec = orig_materialize
|
49
49
|
return spec if spec
|
50
50
|
|
51
|
-
Licensed::Bundler::
|
51
|
+
Licensed::Bundler::MissingSpecification.new(name: name, version: version, platform: platform, source: source)
|
52
52
|
end
|
53
53
|
end
|
54
54
|
end
|
@@ -3,6 +3,7 @@ require "delegate"
|
|
3
3
|
begin
|
4
4
|
require "bundler"
|
5
5
|
require "licensed/sources/bundler/missing_specification"
|
6
|
+
require "licensed/sources/bundler/definition"
|
6
7
|
rescue LoadError
|
7
8
|
end
|
8
9
|
|
@@ -12,9 +13,9 @@ module Licensed
|
|
12
13
|
class Dependency < Licensed::Dependency
|
13
14
|
attr_reader :loaded_from
|
14
15
|
|
15
|
-
def initialize(name:, version:, path:, loaded_from:,
|
16
|
+
def initialize(name:, version:, path:, loaded_from:, errors: [], metadata: {})
|
16
17
|
@loaded_from = loaded_from
|
17
|
-
super name: name, version: version, path: path, errors: errors, metadata: metadata
|
18
|
+
super name: name, version: version, path: path, errors: errors, metadata: metadata
|
18
19
|
end
|
19
20
|
|
20
21
|
# Load a package manager file from the base Licensee::Projects::FsProject
|
@@ -29,7 +30,7 @@ module Licensed
|
|
29
30
|
# `loaded_from` if available.
|
30
31
|
def spec_file
|
31
32
|
return @spec_file if defined?(@spec_file)
|
32
|
-
return @spec_file = nil unless loaded_from && File.
|
33
|
+
return @spec_file = nil unless loaded_from && File.file?(loaded_from)
|
33
34
|
@spec_file = begin
|
34
35
|
file = { name: File.basename(loaded_from), dir: File.dirname(loaded_from) }
|
35
36
|
Licensee::ProjectFiles::PackageManagerFile.new(File.read(loaded_from), file)
|
@@ -37,7 +38,6 @@ module Licensed
|
|
37
38
|
end
|
38
39
|
end
|
39
40
|
|
40
|
-
GEMFILES = { "Gemfile" => "Gemfile.lock", "gems.rb" => "gems.locked" }
|
41
41
|
DEFAULT_WITHOUT_GROUPS = %i{development test}
|
42
42
|
RUBY_PACKER_ERROR = "The bundler source cannot be used from the executable built with ruby-packer. Please install licensed using `gem install` or using bundler."
|
43
43
|
|
@@ -45,24 +45,28 @@ module Licensed
|
|
45
45
|
# running a ruby-packer-built licensed exe when ruby isn't available
|
46
46
|
# could lead to errors if the host ruby doesn't exist
|
47
47
|
return false if ruby_packer? && !Licensed::Shell.tool_available?("ruby")
|
48
|
-
|
48
|
+
|
49
|
+
# if Bundler isn't loaded, this enumerator won't work!
|
50
|
+
return false unless defined?(::Bundler)
|
51
|
+
|
52
|
+
with_application_environment { ::Bundler.default_lockfile&.exist? }
|
53
|
+
rescue ::Bundler::GemfileNotFound
|
54
|
+
false
|
49
55
|
end
|
50
56
|
|
51
57
|
def enumerate_dependencies
|
52
58
|
raise Licensed::Sources::Source::Error.new(RUBY_PACKER_ERROR) if ruby_packer?
|
53
59
|
|
54
|
-
|
55
|
-
specs.map do |spec|
|
56
|
-
next if spec.name == "bundler" && !include_bundler?
|
60
|
+
with_application_environment do
|
61
|
+
definition.specs.map do |spec|
|
57
62
|
next if spec.name == config["name"]
|
58
63
|
|
59
64
|
error = spec.error if spec.respond_to?(:error)
|
60
65
|
Dependency.new(
|
61
66
|
name: spec.name,
|
62
67
|
version: spec.version.to_s,
|
63
|
-
path: spec.
|
68
|
+
path: spec.full_gem_path,
|
64
69
|
loaded_from: spec.loaded_from,
|
65
|
-
search_root: spec_root(spec),
|
66
70
|
errors: Array(error),
|
67
71
|
metadata: {
|
68
72
|
"type" => Bundler.type,
|
@@ -74,53 +78,13 @@ module Licensed
|
|
74
78
|
end
|
75
79
|
end
|
76
80
|
|
77
|
-
# Returns an array of Gem::Specifications for all gem dependencies
|
78
|
-
def specs
|
79
|
-
@specs ||= definition.specs_for(groups)
|
80
|
-
end
|
81
|
-
|
82
|
-
# Returns whether to include bundler as a listed dependency of the project
|
83
|
-
def include_bundler?
|
84
|
-
@include_bundler ||= begin
|
85
|
-
# include if bundler is listed as a direct dependency that should be included
|
86
|
-
requested_dependencies = definition.dependencies.select { |d| (d.groups & groups).any? && d.should_include? }
|
87
|
-
return true if requested_dependencies.any? { |d| d.name == "bundler" }
|
88
|
-
# include if bundler is an indirect dependency
|
89
|
-
return true if specs.flat_map(&:dependencies).any? { |d| d.name == "bundler" }
|
90
|
-
false
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
# Returns a search root for a specification, one of:
|
95
|
-
# - the local bundler gem location
|
96
|
-
# - the system rubygems install gem location
|
97
|
-
# - nil
|
98
|
-
def spec_root(spec)
|
99
|
-
return if spec.gem_dir.nil?
|
100
|
-
root = [Gem.default_dir, Gem.dir].find { |dir| spec.gem_dir.start_with?(dir) }
|
101
|
-
return unless root
|
102
|
-
|
103
|
-
"#{root}/gems/#{spec.full_name}"
|
104
|
-
end
|
105
|
-
|
106
|
-
# Build the bundler definition
|
107
81
|
def definition
|
108
|
-
@definition ||=
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
@groups ||= definition.groups - bundler_setting_array(:without) + bundler_setting_array(:with) - exclude_groups
|
115
|
-
end
|
116
|
-
|
117
|
-
# Returns a bundler setting as an array.
|
118
|
-
# Depending on the version of bundler, array values are either returned as
|
119
|
-
# a raw string ("a:b:c") or as an array ([:a, :b, :c])
|
120
|
-
def bundler_setting_array(key)
|
121
|
-
setting = ::Bundler.settings[key]
|
122
|
-
setting = setting.split(":").map(&:to_sym) if setting.is_a?(String)
|
123
|
-
Array(setting)
|
82
|
+
@definition ||= begin
|
83
|
+
definition = ::Bundler::Definition.build(::Bundler.default_gemfile, ::Bundler.default_lockfile, nil)
|
84
|
+
definition.extend Licensed::Bundler::DefinitionExtensions
|
85
|
+
definition.force_exclude_groups = exclude_groups
|
86
|
+
definition
|
87
|
+
end
|
124
88
|
end
|
125
89
|
|
126
90
|
# Returns any groups to exclude specified from both licensed configuration
|
@@ -134,41 +98,29 @@ module Licensed
|
|
134
98
|
end
|
135
99
|
end
|
136
100
|
|
137
|
-
# Returns the path to the Bundler Gemfile
|
138
|
-
def gemfile_path
|
139
|
-
@gemfile_path ||= GEMFILES.keys
|
140
|
-
.map { |g| config.pwd.join g }
|
141
|
-
.find { |f| f.exist? }
|
142
|
-
end
|
143
|
-
|
144
|
-
# Returns the path to the Bundler Gemfile.lock
|
145
|
-
def lockfile_path
|
146
|
-
return unless gemfile_path
|
147
|
-
@lockfile_path ||= gemfile_path.dirname.join(GEMFILES[gemfile_path.basename.to_s])
|
148
|
-
end
|
149
|
-
|
150
|
-
private
|
151
|
-
|
152
101
|
# helper to clear all bundler environment around a yielded block
|
153
|
-
def
|
154
|
-
|
155
|
-
original_bundle_gemfile, ENV["BUNDLE_GEMFILE"] = ENV["BUNDLE_GEMFILE"], gemfile_path.to_s
|
102
|
+
def with_application_environment
|
103
|
+
backup = nil
|
156
104
|
|
157
|
-
|
158
|
-
|
105
|
+
::Bundler.ui.silence do
|
106
|
+
if ::Bundler.root != config.source_path
|
107
|
+
backup = ENV.to_hash
|
108
|
+
ENV.replace(::Bundler.original_env)
|
159
109
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
110
|
+
# reset bundler to load from the current app's source path
|
111
|
+
::Bundler.reset!
|
112
|
+
::Bundler.load
|
113
|
+
end
|
164
114
|
|
165
|
-
|
115
|
+
yield
|
116
|
+
end
|
166
117
|
ensure
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
118
|
+
if backup
|
119
|
+
# restore bundler configuration
|
120
|
+
ENV.replace(backup)
|
121
|
+
::Bundler.reset!
|
122
|
+
::Bundler.load
|
123
|
+
end
|
172
124
|
end
|
173
125
|
|
174
126
|
# Returns whether the current licensed execution is running ruby-packer
|
data/lib/licensed/sources/dep.rb
CHANGED
@@ -40,10 +40,10 @@ module Licensed
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
-
# Returns the
|
43
|
+
# Returns the pkg.go.dev page for a package.
|
44
44
|
def homepage(import_path)
|
45
45
|
return unless import_path
|
46
|
-
"https://
|
46
|
+
"https://pkg.go.dev/#{import_path}"
|
47
47
|
end
|
48
48
|
|
49
49
|
# Returns whether the package is part of the go std list. Replaces
|
data/lib/licensed/sources/go.rb
CHANGED
@@ -98,7 +98,7 @@ module Licensed
|
|
98
98
|
# Returns whether the package is local to the current project
|
99
99
|
def local_package?(package)
|
100
100
|
return false unless package && package["Dir"]
|
101
|
-
return false unless File.fnmatch?("#{config.root
|
101
|
+
return false unless File.fnmatch?("#{config.root}*", package["Dir"], File::FNM_CASEFOLD)
|
102
102
|
vendored_path_parts(package).nil?
|
103
103
|
end
|
104
104
|
|
@@ -132,10 +132,10 @@ module Licensed
|
|
132
132
|
end
|
133
133
|
end
|
134
134
|
|
135
|
-
# Returns the
|
135
|
+
# Returns the pkg.go.dev page for a package.
|
136
136
|
def homepage(import_path)
|
137
137
|
return unless import_path
|
138
|
-
"https://
|
138
|
+
"https://pkg.go.dev/#{import_path}"
|
139
139
|
end
|
140
140
|
|
141
141
|
# Returns the root directory to search for a package license
|