licensed 3.9.1 → 4.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +53 -1
- data/CONTRIBUTING.md +0 -2
- data/Gemfile.lock +112 -0
- data/README.md +7 -12
- data/Rakefile +0 -26
- data/docs/configuration/README.md +1 -0
- data/docs/configuration/additional_terms.md +41 -0
- data/docs/configuration.md +7 -0
- data/docs/sources/bundler.md +0 -2
- data/docs/sources/cocoapods.md +17 -0
- data/docs/sources/gradle.md +18 -0
- data/docs/sources/pnpm.md +18 -0
- data/lib/licensed/configuration.rb +6 -1
- data/lib/licensed/dependency.rb +27 -0
- data/lib/licensed/sources/bundler/definition.rb +9 -1
- data/lib/licensed/sources/bundler/missing_specification.rb +2 -2
- data/lib/licensed/sources/bundler.rb +0 -12
- data/lib/licensed/sources/cocoapods.rb +68 -0
- data/lib/licensed/sources/go.rb +1 -36
- data/lib/licensed/sources/gradle.rb +127 -94
- data/lib/licensed/sources/pnpm.rb +52 -0
- data/lib/licensed/sources/source.rb +8 -1
- data/lib/licensed/sources.rb +4 -2
- data/lib/licensed/version.rb +1 -1
- data/licensed.gemspec +13 -13
- metadata +46 -73
- data/docs/packaging.md +0 -53
data/lib/licensed/sources/go.rb
CHANGED
@@ -36,27 +36,13 @@ module Licensed
|
|
36
36
|
|
37
37
|
# Returns an array of dependency package import paths
|
38
38
|
def packages
|
39
|
-
dependency_packages =
|
40
|
-
if go_version < Gem::Version.new("1.11.0")
|
41
|
-
root_package_deps
|
42
|
-
else
|
43
|
-
go_list_deps
|
44
|
-
end
|
45
|
-
|
46
39
|
# don't include go std packages
|
47
40
|
# don't include packages under the root project that aren't vendored
|
48
|
-
|
41
|
+
go_list_deps
|
49
42
|
.reject { |pkg| go_std_package?(pkg) }
|
50
43
|
.reject { |pkg| local_package?(pkg) }
|
51
44
|
end
|
52
45
|
|
53
|
-
# Returns non-ignored packages found from the root packages "Deps" property
|
54
|
-
def root_package_deps
|
55
|
-
# check for ignored packages to avoid raising errors calling `go list`
|
56
|
-
# when ignored package is not found
|
57
|
-
Parallel.map(Array(root_package["Deps"])) { |name| package_info(name) }
|
58
|
-
end
|
59
|
-
|
60
46
|
# Returns the list of dependencies as returned by "go list -json -deps"
|
61
47
|
# available in go 1.11
|
62
48
|
def go_list_deps
|
@@ -188,13 +174,6 @@ module Licensed
|
|
188
174
|
package["ImportPath"]
|
189
175
|
end
|
190
176
|
|
191
|
-
# Returns a hash of information about the package with a given import path
|
192
|
-
#
|
193
|
-
# import_path - Go package import path
|
194
|
-
def package_info(import_path)
|
195
|
-
JSON.parse(package_info_command(import_path))
|
196
|
-
end
|
197
|
-
|
198
177
|
# Returns package information as a JSON string
|
199
178
|
#
|
200
179
|
# args - additional arguments to `go list`, e.g. Go package import path
|
@@ -202,11 +181,6 @@ module Licensed
|
|
202
181
|
Licensed::Shell.execute("go", "list", "-e", "-json", *Array(args)).strip
|
203
182
|
end
|
204
183
|
|
205
|
-
# Returns the info for the package under test
|
206
|
-
def root_package
|
207
|
-
@root_package ||= package_info(".")
|
208
|
-
end
|
209
|
-
|
210
184
|
# Returns whether go source is found
|
211
185
|
def go_source?
|
212
186
|
with_configured_gopath { Licensed::Shell.success?("go", "doc") }
|
@@ -230,15 +204,6 @@ module Licensed
|
|
230
204
|
end
|
231
205
|
end
|
232
206
|
|
233
|
-
# Returns the current version of go available, as a Gem::Version
|
234
|
-
def go_version
|
235
|
-
@go_version ||= begin
|
236
|
-
full_version = Licensed::Shell.execute("go", "version").strip
|
237
|
-
version_string = full_version.gsub(%r{.*go(\d+\.\d+(\.\d+)?).*}, "\\1")
|
238
|
-
Gem::Version.new(version_string)
|
239
|
-
end
|
240
|
-
end
|
241
|
-
|
242
207
|
private
|
243
208
|
|
244
209
|
# Execute a block with ENV["GOPATH"] set to the value of #gopath.
|
@@ -9,53 +9,19 @@ require "fileutils"
|
|
9
9
|
module Licensed
|
10
10
|
module Sources
|
11
11
|
class Gradle < Source
|
12
|
-
|
13
12
|
DEFAULT_CONFIGURATIONS = ["runtime", "runtimeClasspath"].freeze
|
14
13
|
GRADLE_LICENSES_PATH = ".gradle-licenses".freeze
|
15
|
-
|
14
|
+
GRADLE_LICENSES_CSV_NAME = "licenses.csv".freeze
|
16
15
|
class Dependency < Licensed::Dependency
|
17
|
-
GRADLE_LICENSES_CSV_NAME = "licenses.csv".freeze
|
18
|
-
|
19
16
|
class << self
|
20
|
-
# Returns a key to uniquely identify a name and version in the obtained CSV content
|
21
|
-
def csv_key(name:, version:)
|
22
|
-
"#{name}-#{version}"
|
23
|
-
end
|
24
|
-
|
25
|
-
# Loads and caches license report CSV data as a hash of :name-:version => :url pairs
|
26
|
-
#
|
27
|
-
# executable - The gradle executable to run to generate the license report
|
28
|
-
# configurations - The gradle configurations to generate license report for
|
29
|
-
#
|
30
|
-
# Returns a hash of dependency identifiers to their license content URL
|
31
|
-
def load_csv(path, executable, configurations)
|
32
|
-
@csv ||= begin
|
33
|
-
gradle_licenses_dir = File.join(path, GRADLE_LICENSES_PATH)
|
34
|
-
Licensed::Sources::Gradle.gradle_command("generateLicenseReport", path: path, executable: executable, configurations: configurations)
|
35
|
-
CSV.foreach(File.join(gradle_licenses_dir, GRADLE_LICENSES_CSV_NAME), headers: true).each_with_object({}) do |row, hsh|
|
36
|
-
name, _, version = row["artifact"].rpartition(":")
|
37
|
-
key = csv_key(name: name, version: version)
|
38
|
-
hsh[key] = row["moduleLicenseUrl"]
|
39
|
-
end
|
40
|
-
ensure
|
41
|
-
FileUtils.rm_rf(gradle_licenses_dir)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
# Returns the cached url for the given dependency
|
46
|
-
def url_for(dependency)
|
47
|
-
@csv[csv_key(name: dependency.name, version: dependency.version)]
|
48
|
-
end
|
49
|
-
|
50
17
|
# Cache and return the results of getting the license content.
|
51
18
|
def retrieve_license(url)
|
52
19
|
(@licenses ||= {})[url] ||= Net::HTTP.get(URI(url))
|
53
20
|
end
|
54
21
|
end
|
55
22
|
|
56
|
-
def initialize(name:, version:, path:,
|
57
|
-
@
|
58
|
-
@executable = executable
|
23
|
+
def initialize(name:, version:, path:, url:, metadata: {})
|
24
|
+
@url = url
|
59
25
|
super(name: name, version: version, path: path, metadata: metadata)
|
60
26
|
end
|
61
27
|
|
@@ -68,32 +34,27 @@ module Licensed
|
|
68
34
|
|
69
35
|
# Returns a Licensee::ProjectFiles::LicenseFile for the dependency
|
70
36
|
def project_files
|
71
|
-
|
72
|
-
url = self.class.url_for(self)
|
37
|
+
return [] if @url.nil?
|
73
38
|
|
74
|
-
|
75
|
-
|
76
|
-
license_data = self.class.retrieve_license(url)
|
77
|
-
|
78
|
-
Array(Licensee::ProjectFiles::LicenseFile.new(license_data, { uri: url }))
|
39
|
+
license_data = self.class.retrieve_license(@url)
|
40
|
+
Array(Licensee::ProjectFiles::LicenseFile.new(license_data, { uri: @url }))
|
79
41
|
end
|
80
42
|
end
|
81
43
|
|
82
44
|
def enabled?
|
83
|
-
!
|
45
|
+
!executable.to_s.empty? && File.exist?(config.pwd.join("build.gradle"))
|
84
46
|
end
|
85
47
|
|
86
48
|
def enumerate_dependencies
|
87
|
-
JSON.parse(
|
88
|
-
name = "#{package[
|
49
|
+
JSON.parse(gradle_runner.run("printDependencies", config.source_path)).map do |package|
|
50
|
+
name = "#{package['group']}:#{package['name']}"
|
89
51
|
Dependency.new(
|
90
52
|
name: name,
|
91
53
|
version: package["version"],
|
92
54
|
path: config.pwd,
|
93
|
-
|
94
|
-
configurations: configurations,
|
55
|
+
url: package_url(name: name, version: package["version"]),
|
95
56
|
metadata: {
|
96
|
-
"type"
|
57
|
+
"type" => Gradle.type,
|
97
58
|
}
|
98
59
|
)
|
99
60
|
end
|
@@ -101,15 +62,20 @@ module Licensed
|
|
101
62
|
|
102
63
|
private
|
103
64
|
|
104
|
-
def
|
105
|
-
return @
|
106
|
-
|
107
|
-
|
65
|
+
def executable
|
66
|
+
return @executable if defined?(@executable)
|
67
|
+
|
68
|
+
@executable = begin
|
108
69
|
return gradlew if File.executable?(gradlew)
|
70
|
+
|
109
71
|
"gradle" if Licensed::Shell.tool_available?("gradle")
|
110
72
|
end
|
111
73
|
end
|
112
74
|
|
75
|
+
def gradle_runner
|
76
|
+
@gradle_runner ||= Runner.new(config.pwd, configurations, executable)
|
77
|
+
end
|
78
|
+
|
113
79
|
# Returns the configurations to include in license generation.
|
114
80
|
# Defaults to ["runtime", "runtimeClasspath"]
|
115
81
|
def configurations
|
@@ -122,61 +88,128 @@ module Licensed
|
|
122
88
|
end
|
123
89
|
end
|
124
90
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
gradle_build_file = " plugins { id 'com.github.jk1.dependency-license-report' version '1.16' }" + gradle_build_file
|
91
|
+
# Returns the path to the Gradle wrapper.
|
92
|
+
def gradlew
|
93
|
+
@gradlew ||= begin
|
94
|
+
gradlew = config.dig("gradle", "gradlew")
|
95
|
+
config.root.join(gradlew || "gradlew").to_s
|
132
96
|
end
|
133
97
|
end
|
134
98
|
|
135
|
-
|
136
|
-
|
99
|
+
# Returns a key to uniquely identify a name and version in the obtained CSV content
|
100
|
+
def csv_key(name:, version:)
|
101
|
+
"#{name}-#{version}"
|
102
|
+
end
|
103
|
+
|
104
|
+
def package_url(name:, version:)
|
105
|
+
# load and memoize the license report CSV
|
106
|
+
@urls ||= load_csv
|
137
107
|
|
138
|
-
|
139
|
-
|
140
|
-
|
108
|
+
# uniquely identify a name and version in the obtained CSV content
|
109
|
+
@urls["#{name}-#{version}"]
|
110
|
+
end
|
141
111
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
112
|
+
def load_csv
|
113
|
+
begin
|
114
|
+
# create the CSV file including dependency license urls using the gradle plugin
|
115
|
+
gradle_licenses_dir = File.join(config.root, GRADLE_LICENSES_PATH)
|
116
|
+
gradle_runner.run("generateLicenseReport", config.source_path)
|
117
|
+
|
118
|
+
# parse the CSV report for dependency license urls
|
119
|
+
CSV.foreach(File.join(gradle_licenses_dir, GRADLE_LICENSES_CSV_NAME), headers: true).each_with_object({}) do |row, hsh|
|
120
|
+
name, _, version = row["artifact"].rpartition(":")
|
121
|
+
key = csv_key(name: name, version: version)
|
122
|
+
hsh[key] = row["moduleLicenseUrl"]
|
148
123
|
end
|
124
|
+
ensure
|
125
|
+
FileUtils.rm_rf(gradle_licenses_dir)
|
149
126
|
end
|
150
127
|
end
|
151
128
|
|
152
|
-
|
153
|
-
|
129
|
+
# Returns the cached url for the given dependency
|
130
|
+
def url_for(dependency)
|
131
|
+
@csv[csv_key(name: dependency.name, version: dependency.version)]
|
132
|
+
end
|
133
|
+
|
134
|
+
# The Gradle::Runner class is a wrapper which provides
|
135
|
+
# an interface to run gradle commands with the init script initialized
|
136
|
+
class Runner
|
137
|
+
def initialize(root_path, configurations, executable)
|
138
|
+
@root_path = root_path
|
139
|
+
@executable = executable
|
140
|
+
@init_script = create_init_script(root_path, configurations)
|
141
|
+
end
|
154
142
|
|
155
|
-
|
156
|
-
|
143
|
+
def run(command, source_path)
|
144
|
+
args = [format_command(command, source_path)]
|
145
|
+
# The configuration cache is an incubating feature that can be activated manually.
|
146
|
+
# The gradle plugin for licenses does not support it so we prevent it to run for gradle version supporting it.
|
147
|
+
args << "--no-configuration-cache" if gradle_version >= "6.6"
|
148
|
+
Licensed::Shell.execute(@executable, "-q", "--init-script", @init_script.path, *args)
|
149
|
+
end
|
157
150
|
|
158
|
-
|
151
|
+
private
|
159
152
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
renderers = [new CsvReportRenderer()]
|
164
|
-
filters = [new LicenseBundleNormalizer()]
|
165
|
-
}
|
153
|
+
def gradle_version
|
154
|
+
@gradle_version ||= Licensed::Shell.execute(@executable, "--version").scan(/Gradle [\d+]\.[\d+]/).last.split(" ").last
|
155
|
+
end
|
166
156
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
157
|
+
def create_init_script(path, configurations)
|
158
|
+
Dir.chdir(path) do
|
159
|
+
f = Tempfile.new(["init", ".gradle"], @root_path)
|
160
|
+
f.write(
|
161
|
+
<<~EOF
|
162
|
+
import com.github.jk1.license.render.CsvReportRenderer
|
163
|
+
import com.github.jk1.license.filter.LicenseBundleNormalizer
|
164
|
+
final configs = #{configurations.inspect}
|
165
|
+
|
166
|
+
initscript {
|
167
|
+
repositories {
|
168
|
+
maven {
|
169
|
+
url "https://plugins.gradle.org/m2/"
|
174
170
|
}
|
171
|
+
}
|
172
|
+
dependencies {
|
173
|
+
classpath "com.github.jk1:gradle-license-report:#{gradle_version >= "7.0" ? "2.0" : "1.17"}"
|
174
|
+
}
|
175
175
|
}
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
176
|
+
|
177
|
+
allprojects {
|
178
|
+
apply plugin: com.github.jk1.license.LicenseReportPlugin
|
179
|
+
licenseReport {
|
180
|
+
outputDir = "$rootDir/.gradle-licenses"
|
181
|
+
configurations = configs
|
182
|
+
renderers = [new CsvReportRenderer()]
|
183
|
+
filters = [new LicenseBundleNormalizer()]
|
184
|
+
}
|
185
|
+
|
186
|
+
task printDependencies {
|
187
|
+
doLast {
|
188
|
+
def dependencies = []
|
189
|
+
configs.each {
|
190
|
+
configurations[it].resolvedConfiguration.resolvedArtifacts.each { artifact ->
|
191
|
+
def id = artifact.moduleVersion.id
|
192
|
+
dependencies << "{ \\"group\\": \\"${id.group}\\", \\"name\\": \\"${id.name}\\", \\"version\\": \\"${id.version}\\" }"
|
193
|
+
}
|
194
|
+
}
|
195
|
+
println "[${dependencies.join(", ")}]"
|
196
|
+
}
|
197
|
+
}
|
198
|
+
}
|
199
|
+
EOF
|
200
|
+
)
|
201
|
+
f.close
|
202
|
+
f
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
# Prefixes the gradle command with the project name for multi-build projects.
|
207
|
+
def format_command(command, source_path)
|
208
|
+
Dir.chdir(source_path) do
|
209
|
+
path = Licensed::Shell.execute(@executable, "properties", "-Dorg.gradle.logging.level=quiet").scan(/path:.*/).last.split(" ").last
|
210
|
+
path == ":" ? command : "#{path}:#{command}"
|
211
|
+
end
|
212
|
+
end
|
180
213
|
end
|
181
214
|
end
|
182
215
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "json"
|
3
|
+
|
4
|
+
module Licensed
|
5
|
+
module Sources
|
6
|
+
class PNPM < Source
|
7
|
+
# Returns true when pnpm is installed and a pnpm-lock.yaml file is found,
|
8
|
+
# otherwise false
|
9
|
+
def enabled?
|
10
|
+
return false unless Licensed::Shell.tool_available?("pnpm")
|
11
|
+
File.exist?(File.join(config.pwd, "pnpm-lock.yaml"))
|
12
|
+
end
|
13
|
+
|
14
|
+
def enumerate_dependencies
|
15
|
+
packages.map do |package|
|
16
|
+
name_with_version = "#{package["name"]}@#{package["version"]}"
|
17
|
+
Dependency.new(
|
18
|
+
name: name_with_version,
|
19
|
+
version: package["version"],
|
20
|
+
path: package["path"],
|
21
|
+
metadata: {
|
22
|
+
"type" => PNPM.type,
|
23
|
+
"name" => package["name"],
|
24
|
+
"summary" => package["description"],
|
25
|
+
"homepage" => package["homepage"]
|
26
|
+
}
|
27
|
+
)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns package metadata returned from `pnpm licensed list`
|
32
|
+
def packages
|
33
|
+
JSON.parse(package_metadata_command).values.flatten
|
34
|
+
rescue JSON::ParserError => e
|
35
|
+
message = "Licensed was unable to parse the output from 'pnpm licenses list'. JSON Error: #{e.message}"
|
36
|
+
raise Licensed::Sources::Source::Error, message
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns the output from running `pnpm licenses list` to get package metadata
|
40
|
+
def package_metadata_command
|
41
|
+
args = %w(--json --long)
|
42
|
+
args << "--prod" unless include_non_production?
|
43
|
+
Licensed::Shell.execute("pnpm", "licenses", "list", *args, allow_failure: true)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns whether to include non production dependencies based on the licensed configuration settings
|
47
|
+
def include_non_production?
|
48
|
+
config.dig("pnpm", "production_only") == false
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -69,7 +69,9 @@ module Licensed
|
|
69
69
|
# Returns all dependencies that should be evaluated.
|
70
70
|
# Excludes ignored dependencies.
|
71
71
|
def dependencies
|
72
|
-
cached_dependencies
|
72
|
+
cached_dependencies
|
73
|
+
.reject { |d| ignored?(d) }
|
74
|
+
.each { |d| add_additional_terms_from_configuration(d) }
|
73
75
|
end
|
74
76
|
|
75
77
|
# Enumerate all source dependencies. Must be implemented by each source class.
|
@@ -88,6 +90,11 @@ module Licensed
|
|
88
90
|
def cached_dependencies
|
89
91
|
@dependencies ||= enumerate_dependencies.compact
|
90
92
|
end
|
93
|
+
|
94
|
+
# Add any additional_terms for this dependency that have been added to the configuration
|
95
|
+
def add_additional_terms_from_configuration(dependency)
|
96
|
+
dependency.additional_terms.concat config.additional_terms_for_dependency("type" => self.class.type, "name" => dependency.name)
|
97
|
+
end
|
91
98
|
end
|
92
99
|
end
|
93
100
|
end
|
data/lib/licensed/sources.rb
CHANGED
@@ -6,18 +6,20 @@ module Licensed
|
|
6
6
|
require "licensed/sources/bundler"
|
7
7
|
require "licensed/sources/cabal"
|
8
8
|
require "licensed/sources/cargo"
|
9
|
+
require "licensed/sources/cocoapods"
|
9
10
|
require "licensed/sources/composer"
|
10
11
|
require "licensed/sources/dep"
|
11
12
|
require "licensed/sources/git_submodule"
|
12
13
|
require "licensed/sources/go"
|
14
|
+
require "licensed/sources/gradle"
|
13
15
|
require "licensed/sources/manifest"
|
16
|
+
require "licensed/sources/mix"
|
14
17
|
require "licensed/sources/npm"
|
15
18
|
require "licensed/sources/nuget"
|
16
19
|
require "licensed/sources/pip"
|
17
20
|
require "licensed/sources/pipenv"
|
21
|
+
require "licensed/sources/pnpm"
|
18
22
|
require "licensed/sources/swift"
|
19
|
-
require "licensed/sources/gradle"
|
20
|
-
require "licensed/sources/mix"
|
21
23
|
require "licensed/sources/yarn"
|
22
24
|
end
|
23
25
|
end
|
data/lib/licensed/version.rb
CHANGED
data/licensed.gemspec
CHANGED
@@ -21,21 +21,21 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
22
|
spec.require_paths = ["lib"]
|
23
23
|
|
24
|
-
spec.required_ruby_version = ">= 2.
|
24
|
+
spec.required_ruby_version = ">= 2.6.0"
|
25
25
|
|
26
|
-
spec.add_dependency "licensee", "
|
27
|
-
spec.add_dependency "thor", "
|
26
|
+
spec.add_dependency "licensee", "~> 9.16"
|
27
|
+
spec.add_dependency "thor", "~> 1.2"
|
28
28
|
spec.add_dependency "pathname-common_prefix", "~> 0.0.1"
|
29
|
-
spec.add_dependency "tomlrb", "
|
30
|
-
spec.add_dependency "
|
31
|
-
spec.add_dependency "
|
32
|
-
spec.add_dependency "
|
33
|
-
spec.add_dependency "
|
34
|
-
spec.add_dependency "
|
29
|
+
spec.add_dependency "tomlrb", "~> 2.0"
|
30
|
+
spec.add_dependency "ruby-xxHash", "~> 0.4.0"
|
31
|
+
spec.add_dependency "parallel", "~> 1.22"
|
32
|
+
spec.add_dependency "reverse_markdown", "~> 2.1"
|
33
|
+
spec.add_dependency "json", "~> 2.6"
|
34
|
+
# spec.add_dependency "cocoapods-core", "~> 1.11"
|
35
35
|
|
36
|
-
spec.add_development_dependency "rake", "
|
37
|
-
spec.add_development_dependency "minitest", "~> 5.
|
36
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
37
|
+
spec.add_development_dependency "minitest", "~> 5.17"
|
38
38
|
spec.add_development_dependency "mocha", "~> 2.0"
|
39
|
-
spec.add_development_dependency "rubocop-github", "~> 0.
|
40
|
-
spec.add_development_dependency "byebug", "~> 11.1
|
39
|
+
spec.add_development_dependency "rubocop-github", "~> 0.20"
|
40
|
+
spec.add_development_dependency "byebug", "~> 11.1"
|
41
41
|
end
|