license_scout 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +6 -0
- data/bin/config_to_json +0 -0
- data/bin/license_scout +4 -2
- data/lib/license_scout/collector.rb +2 -24
- data/lib/license_scout/dependency_manager/berkshelf.rb +102 -0
- data/lib/license_scout/dependency_manager/bundler/_bundler_script.rb +2 -3
- data/lib/license_scout/dependency_manager/bundler.rb +15 -9
- data/lib/license_scout/dependency_manager/cpan.rb +321 -0
- data/lib/license_scout/dependency_manager/json/README.md +392 -0
- data/lib/license_scout/dependency_manager/rebar.rb +101 -2
- data/lib/license_scout/dependency_manager.rb +3 -1
- data/lib/license_scout/exceptions.rb +1 -11
- data/lib/license_scout/net_fetcher.rb +1 -1
- data/lib/license_scout/options.rb +2 -1
- data/lib/license_scout/overrides.rb +164 -0
- data/lib/license_scout/reporter.rb +90 -0
- data/lib/license_scout/version.rb +1 -1
- data/license_scout.gemspec +1 -0
- metadata +21 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 20de647a9029e60eee95ccfdd6af26ca23f68943
|
4
|
+
data.tar.gz: 4d39bfea2c9974e8af4cd400eccfca1bbec4f4bd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f554b77a3215ce07597d48a64daa48844dc23463372ca5ec5bfda0726418836c25b7b53eb4e123942b5ffdf858a503e14e59d7aa196b6fa27fce0513ed985e8d
|
7
|
+
data.tar.gz: 990fd0c28d0f003e72f0cc1840198b0453c8a9f8c52c45f3e7aff9bae145e645b2a46a83901159a97de45d9e8f056bef92fe41ae3da42014350cdeffbdd6f30f
|
data/README.md
CHANGED
@@ -7,9 +7,15 @@ Currently supported project types are:
|
|
7
7
|
|
8
8
|
* Ruby - bundler
|
9
9
|
* Erlang - rebar
|
10
|
+
* CPAN - perl
|
11
|
+
* Berkshelf - chef
|
10
12
|
|
11
13
|
## Usage
|
12
14
|
|
15
|
+
## Thanks
|
16
|
+
|
17
|
+
Thanks to https://github.com/basho for `config_to_json` binary which helps with parsing Erlang config files. From: https://github.com/basho/erlang_template_helper
|
18
|
+
|
13
19
|
## Contributing
|
14
20
|
|
15
21
|
This project is maintained by the contribution guidelines identified for
|
data/bin/config_to_json
ADDED
Binary file
|
data/bin/license_scout
CHANGED
@@ -22,10 +22,12 @@ require "license_scout/collector"
|
|
22
22
|
require "license_scout/overrides"
|
23
23
|
require "license_scout/options"
|
24
24
|
|
25
|
-
project_dir = File.expand_path(Dir.pwd)
|
25
|
+
project_dir = ARGV[0] || File.expand_path(Dir.pwd)
|
26
26
|
project_name = File.basename(project_dir)
|
27
27
|
|
28
|
-
|
28
|
+
# Create the output files under a specific directory in order not to pollute the
|
29
|
+
# project_dir too much.
|
30
|
+
output_dir = File.join(project_dir, "license-cache")
|
29
31
|
|
30
32
|
overrides = LicenseScout::Overrides.new
|
31
33
|
|
@@ -17,6 +17,7 @@
|
|
17
17
|
|
18
18
|
require "license_scout/exceptions"
|
19
19
|
require "license_scout/dependency_manager"
|
20
|
+
require "license_scout/reporter"
|
20
21
|
|
21
22
|
require "ffi_yajl"
|
22
23
|
|
@@ -59,30 +60,7 @@ module LicenseScout
|
|
59
60
|
end
|
60
61
|
|
61
62
|
def issue_report
|
62
|
-
report
|
63
|
-
license_report = FFI_Yajl::Parser.parse(File.read(license_manifest_path))
|
64
|
-
|
65
|
-
license_report["dependency_managers"].each do |dependency_manager, dependencies|
|
66
|
-
dependencies.each do |dependency|
|
67
|
-
if dependency["name"].nil? || dependency["name"].empty?
|
68
|
-
report << "There is a dependency with a missing name in '#{dependency_manager}'."
|
69
|
-
end
|
70
|
-
|
71
|
-
if dependency["version"].nil? || dependency["version"].empty?
|
72
|
-
report << "Dependency '#{dependency["name"]}' under '#{dependency_manager}' is missing version information."
|
73
|
-
end
|
74
|
-
|
75
|
-
if dependency["license"].nil? || dependency["license"].empty?
|
76
|
-
report << "Dependency '#{dependency["name"]}' version '#{dependency["version"]}' under '#{dependency_manager}' is missing license information."
|
77
|
-
end
|
78
|
-
|
79
|
-
if dependency["license_files"].empty?
|
80
|
-
report << "Dependency '#{dependency["name"]}' version '#{dependency["version"]}' under '#{dependency_manager}' is missing license files information."
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
report
|
63
|
+
Reporter.new(output_dir).report
|
86
64
|
end
|
87
65
|
|
88
66
|
private
|
@@ -0,0 +1,102 @@
|
|
1
|
+
#
|
2
|
+
# Copyright:: Copyright 2016, Chef Software Inc.
|
3
|
+
# License:: Apache License, Version 2.0
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
#
|
17
|
+
|
18
|
+
require "berkshelf"
|
19
|
+
|
20
|
+
require "license_scout/dependency_manager/base"
|
21
|
+
|
22
|
+
module LicenseScout
|
23
|
+
module DependencyManager
|
24
|
+
class Berkshelf < Base
|
25
|
+
|
26
|
+
def name
|
27
|
+
"chef_berkshelf"
|
28
|
+
end
|
29
|
+
|
30
|
+
def detected?
|
31
|
+
File.exists?(berksfile_path) && File.exists?(lockfile_path)
|
32
|
+
end
|
33
|
+
|
34
|
+
def dependencies
|
35
|
+
dependencies = []
|
36
|
+
cookbook_dependencies = nil
|
37
|
+
|
38
|
+
Dir.chdir(project_dir) do
|
39
|
+
berksfile = ::Berkshelf::Berksfile.from_file("./Berksfile")
|
40
|
+
|
41
|
+
# Berkshelf should not give an error when there are cookbooks in the
|
42
|
+
# lockfile that are no longer in the berksfile. It handles this case in
|
43
|
+
# the Installer class which we are not using here. So we handle this
|
44
|
+
# case in the same way Installer does.
|
45
|
+
berksfile.lockfile.reduce!
|
46
|
+
|
47
|
+
cookbook_dependencies = berksfile.list
|
48
|
+
end
|
49
|
+
|
50
|
+
cookbook_dependencies.each do |dep|
|
51
|
+
dependency_name = dep.name
|
52
|
+
dependency_version = dep.cached_cookbook.version
|
53
|
+
|
54
|
+
dependency_license_files = auto_detect_license_files(dep.cached_cookbook.path.to_s)
|
55
|
+
|
56
|
+
# Check license override and license_files override separately since
|
57
|
+
# only one might be set in the overrides.
|
58
|
+
dependency_license = options.overrides.license_for(name, dependency_name, dependency_version) || dep.cached_cookbook.license
|
59
|
+
|
60
|
+
override_license_files = options.overrides.license_files_for(name, dependency_name, dependency_version)
|
61
|
+
cookbook_path = dep.cached_cookbook.path.to_s
|
62
|
+
|
63
|
+
if override_license_files.empty?
|
64
|
+
dependency_license_files = auto_detect_license_files(cookbook_path)
|
65
|
+
else
|
66
|
+
dependency_license_files = override_license_files.resolve_locations(cookbook_path)
|
67
|
+
end
|
68
|
+
|
69
|
+
dependencies << Dependency.new(
|
70
|
+
dependency_name,
|
71
|
+
dependency_version,
|
72
|
+
dependency_license,
|
73
|
+
dependency_license_files
|
74
|
+
)
|
75
|
+
end
|
76
|
+
|
77
|
+
dependencies
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def berksfile_path
|
83
|
+
File.join(project_dir, "Berksfile")
|
84
|
+
end
|
85
|
+
|
86
|
+
def lockfile_path
|
87
|
+
File.join(project_dir, "Berksfile.lock")
|
88
|
+
end
|
89
|
+
|
90
|
+
def auto_detect_license_files(cookbook_path)
|
91
|
+
unless File.exist?(cookbook_path)
|
92
|
+
raise LicenseScout::Exceptions::InaccessibleDependency.new "Autodetected cookbook path '#{cookbook_path}' does not exist"
|
93
|
+
end
|
94
|
+
|
95
|
+
Dir.glob("#{cookbook_path}/*").select do |f|
|
96
|
+
POSSIBLE_LICENSE_FILES.include?(File.basename(f))
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -32,13 +32,12 @@ require "bundler/setup"
|
|
32
32
|
# We're only using things that are in the stdlib.
|
33
33
|
require "json"
|
34
34
|
|
35
|
-
definition = ::Bundler::Definition.build("./Gemfile", "./Gemfile.lock", nil)
|
36
35
|
dependencies = []
|
37
36
|
|
38
|
-
|
37
|
+
Bundler.load.specs.each do |gem_spec|
|
39
38
|
dependencies << {
|
40
39
|
name: gem_spec.name,
|
41
|
-
version: gem_spec.version,
|
40
|
+
version: gem_spec.version.to_s,
|
42
41
|
license: gem_spec.license,
|
43
42
|
path: gem_spec.full_gem_path,
|
44
43
|
}
|
@@ -33,11 +33,13 @@ module LicenseScout
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def detected?
|
36
|
-
# We
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
|
36
|
+
# We check the existence of both Gemfile and Gemfile.lock. We need both
|
37
|
+
# of them to be able to get a concrete set of dependencies which we can
|
38
|
+
# search. We used to raise an error when Gemfile.lock did not exist but
|
39
|
+
# that created issues with projects like oc_bifrost which is a rebar
|
40
|
+
# project but have a Gemfile at its root to be able to run some rake
|
41
|
+
# commands.
|
42
|
+
File.exists?(gemfile_path) && File.exists?(lockfile_path)
|
41
43
|
end
|
42
44
|
|
43
45
|
def dependency_data
|
@@ -56,10 +58,6 @@ module LicenseScout
|
|
56
58
|
end
|
57
59
|
|
58
60
|
def dependencies
|
59
|
-
if !File.exists?(lockfile_path)
|
60
|
-
raise LicenseScout::Exceptions::DependencyManagerNotRun.new(project_dir, name)
|
61
|
-
end
|
62
|
-
|
63
61
|
dependencies = []
|
64
62
|
dependency_data.each do |gem_data|
|
65
63
|
dependency_name = gem_data["name"]
|
@@ -74,6 +72,14 @@ module LicenseScout
|
|
74
72
|
# bundler's lib/ dir, so we have to munge it.
|
75
73
|
dependency_license = "MIT"
|
76
74
|
dependency_license_files = [File.join(File.dirname(__FILE__), "bundler/LICENSE.md")]
|
75
|
+
elsif dependency_name == "json"
|
76
|
+
# json is different weird. When project is using the json that is prepackaged with
|
77
|
+
# Ruby, its included not as a full fledged gem but an *.rb file at:
|
78
|
+
# /opt/opscode/embedded/lib/ruby/2.2.0/json.rb
|
79
|
+
# Because of this its license is reported as nil and its license files can not be
|
80
|
+
# found. That is why we need to provide them manually here.
|
81
|
+
dependency_license = "Ruby"
|
82
|
+
dependency_license_files = [File.join(File.dirname(__FILE__), "json/README.md")]
|
77
83
|
else
|
78
84
|
# Check license override and license_files override separately since
|
79
85
|
# only one might be set in the overrides.
|
@@ -0,0 +1,321 @@
|
|
1
|
+
#
|
2
|
+
# Copyright:: Copyright 2016, Chef Software Inc.
|
3
|
+
# License:: Apache License, Version 2.0
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
#
|
17
|
+
|
18
|
+
require "rexml/document"
|
19
|
+
|
20
|
+
require "ffi_yajl"
|
21
|
+
require "psych"
|
22
|
+
require "mixlib/shellout"
|
23
|
+
|
24
|
+
require "license_scout/dependency_manager/base"
|
25
|
+
require "license_scout/net_fetcher"
|
26
|
+
require "license_scout/exceptions"
|
27
|
+
require "license_scout/dependency"
|
28
|
+
|
29
|
+
module LicenseScout
|
30
|
+
module DependencyManager
|
31
|
+
class CPAN < Base
|
32
|
+
|
33
|
+
class CPANDependency
|
34
|
+
|
35
|
+
LICENSE_TYPE_MAP = {
|
36
|
+
"perl_5" => "Perl-5",
|
37
|
+
"perl" => "Perl-5",
|
38
|
+
"apache_2_0" => "Apache-2.0",
|
39
|
+
"artistic_2" => "Artistic-2.0",
|
40
|
+
"gpl_3" => "GPL-3.0",
|
41
|
+
}.freeze
|
42
|
+
|
43
|
+
attr_reader :module_name
|
44
|
+
attr_reader :dist
|
45
|
+
attr_reader :version
|
46
|
+
attr_reader :cpanfile
|
47
|
+
|
48
|
+
attr_reader :license_files
|
49
|
+
attr_reader :license
|
50
|
+
|
51
|
+
attr_reader :cache_root
|
52
|
+
|
53
|
+
attr_reader :overrides
|
54
|
+
|
55
|
+
def initialize(module_name:, dist:, version:, cpanfile:, cache_root:, overrides:)
|
56
|
+
@module_name = module_name
|
57
|
+
@dist = dist
|
58
|
+
@version = version
|
59
|
+
@cpanfile = cpanfile
|
60
|
+
@cache_root = cache_root
|
61
|
+
@overrides = overrides
|
62
|
+
|
63
|
+
@deps_list = nil
|
64
|
+
|
65
|
+
@license = nil
|
66
|
+
@license_files = []
|
67
|
+
end
|
68
|
+
|
69
|
+
def desc
|
70
|
+
"#{module_name} in #{dist} (#{version}) [#{license}]"
|
71
|
+
end
|
72
|
+
|
73
|
+
def to_dep
|
74
|
+
Dependency.new(
|
75
|
+
# we use dist for the name because there can be multiple modules in
|
76
|
+
# a dist, but the dist is the unit of packaging and licensing
|
77
|
+
dist,
|
78
|
+
version,
|
79
|
+
license,
|
80
|
+
license_files
|
81
|
+
)
|
82
|
+
end
|
83
|
+
|
84
|
+
def collect_licenses
|
85
|
+
ensure_cached
|
86
|
+
Dir.mktmpdir do |tmpdir|
|
87
|
+
FileUtils.cp(distribution_fullpath, tmpdir)
|
88
|
+
Dir.chdir(tmpdir) do
|
89
|
+
untar!
|
90
|
+
distribution_unpack_fullpath = File.join(tmpdir, distribution_unpack_relpath)
|
91
|
+
collect_licenses_in(distribution_unpack_fullpath)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def ensure_cached
|
97
|
+
cache_path = File.join(dist_cache_root, cpanfile)
|
98
|
+
|
99
|
+
# CPAN download URL is like:
|
100
|
+
# http://www.cpan.org/authors/id/R/RJ/RJBS/Sub-Install-0.928.tar.gz
|
101
|
+
# cpanfile is like:
|
102
|
+
# R/RJ/RJBS/Sub-Install-0.928.tar.gz
|
103
|
+
unless File.exist?(cache_path)
|
104
|
+
|
105
|
+
url = "http://www.cpan.org/authors/id/#{cpanfile}"
|
106
|
+
tmp_path = NetFetcher.cache(url)
|
107
|
+
|
108
|
+
FileUtils.mkdir_p(File.dirname(cache_path))
|
109
|
+
FileUtils.cp(tmp_path, cache_path)
|
110
|
+
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def distribution_filename
|
115
|
+
File.basename(cpanfile)
|
116
|
+
end
|
117
|
+
|
118
|
+
def distribution_unpack_relpath
|
119
|
+
# Most packages have tar.gz extension but some have .tgz like
|
120
|
+
# IO-Pager-0.36.tgz
|
121
|
+
[".tar.gz", ".tgz"].each do |ext|
|
122
|
+
if distribution_filename.end_with?(ext)
|
123
|
+
return File.basename(distribution_filename, ext)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def distribution_fullpath
|
129
|
+
File.join(dist_cache_root, cpanfile)
|
130
|
+
end
|
131
|
+
|
132
|
+
# Untar the distribution.
|
133
|
+
#
|
134
|
+
# NOTE: On some platforms, you only get a usable version of tar as
|
135
|
+
# `gtar`, and on windows, symlinks break a lot of stuff. We (Chef
|
136
|
+
# Software) currently only use perl in server products, which we only
|
137
|
+
# build for a handful of Linux distros, so this is sufficient.
|
138
|
+
def untar!
|
139
|
+
s = Mixlib::ShellOut.new("tar zxf #{distribution_filename}")
|
140
|
+
s.run_command
|
141
|
+
s.error!
|
142
|
+
s.stdout
|
143
|
+
end
|
144
|
+
|
145
|
+
def collect_licenses_in(unpack_path)
|
146
|
+
collect_license_info_in(unpack_path)
|
147
|
+
collect_license_files_info_in(unpack_path)
|
148
|
+
end
|
149
|
+
|
150
|
+
def collect_license_info_in(unpack_path)
|
151
|
+
# Notice that we use "dist" as the dependency name
|
152
|
+
# See #to_dep for details.
|
153
|
+
@license = overrides.license_for("perl_cpan", dist, version) || begin
|
154
|
+
metadata = if File.exist?(meta_json_in(unpack_path))
|
155
|
+
slurp_meta_json_in(unpack_path)
|
156
|
+
elsif File.exist?(meta_yaml_in(unpack_path))
|
157
|
+
slurp_meta_yaml_in(unpack_path)
|
158
|
+
end
|
159
|
+
|
160
|
+
if metadata && metadata.key?("license")
|
161
|
+
given_type = Array(metadata["license"]).reject { |l| l == "unknown" }.first
|
162
|
+
normalize_license_type(given_type)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def collect_license_files_info_in(unpack_path)
|
168
|
+
override_license_files = overrides.license_files_for("perl_cpan", dist, version)
|
169
|
+
|
170
|
+
license_files = if override_license_files.empty?
|
171
|
+
find_license_files_in(unpack_path)
|
172
|
+
else
|
173
|
+
override_license_files.resolve_locations(unpack_path)
|
174
|
+
end
|
175
|
+
|
176
|
+
license_files.each do |f|
|
177
|
+
@license_files << cache_license_file(f)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
# Copy license file to the cache. We unpack the CPAN dists in a tempdir
|
182
|
+
# and throw it away after we've inspected the contents, so we need to
|
183
|
+
# put the license file somewhere it can be copied from later.
|
184
|
+
def cache_license_file(unpacked_file)
|
185
|
+
basename = File.basename(unpacked_file)
|
186
|
+
license_cache_path = File.join(license_cache_root, "#{dist}-#{basename}")
|
187
|
+
FileUtils.mkdir_p(license_cache_root)
|
188
|
+
FileUtils.cp(unpacked_file, license_cache_path)
|
189
|
+
# In some cases, the license files get unpacked with 0444
|
190
|
+
# permissions which could make a re-run fail on the `cp` step.
|
191
|
+
FileUtils.chmod(0644, license_cache_path)
|
192
|
+
license_cache_path
|
193
|
+
end
|
194
|
+
|
195
|
+
def slurp_meta_yaml_in(unpack_path)
|
196
|
+
Psych.safe_load(File.read(meta_yaml_in(unpack_path)))
|
197
|
+
end
|
198
|
+
|
199
|
+
def slurp_meta_json_in(unpack_path)
|
200
|
+
FFI_Yajl::Parser.parse(File.read(meta_json_in(unpack_path)))
|
201
|
+
end
|
202
|
+
|
203
|
+
def license_cache_root
|
204
|
+
File.join(cache_root, "cpan-licenses")
|
205
|
+
end
|
206
|
+
|
207
|
+
def dist_cache_root
|
208
|
+
File.join(cache_root, "cpan-dists")
|
209
|
+
end
|
210
|
+
|
211
|
+
def normalize_license_type(given_type)
|
212
|
+
LICENSE_TYPE_MAP[given_type] || given_type
|
213
|
+
end
|
214
|
+
|
215
|
+
def meta_json_in(unpack_path)
|
216
|
+
File.join(unpack_path, "META.json")
|
217
|
+
end
|
218
|
+
|
219
|
+
def mymeta_json_in(unpack_path)
|
220
|
+
File.join(unpack_path, "MYMETA.json")
|
221
|
+
end
|
222
|
+
|
223
|
+
def meta_yaml_in(unpack_path)
|
224
|
+
File.join(unpack_path, "META.yml")
|
225
|
+
end
|
226
|
+
|
227
|
+
def find_license_files_in(unpack_path)
|
228
|
+
Dir["#{unpack_path}/*"].select do |f|
|
229
|
+
CPAN::POSSIBLE_LICENSE_FILES.include?(File.basename(f))
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
end
|
234
|
+
|
235
|
+
def initialize(*args, &block)
|
236
|
+
super
|
237
|
+
@dependencies = nil
|
238
|
+
end
|
239
|
+
|
240
|
+
def name
|
241
|
+
"perl_cpan"
|
242
|
+
end
|
243
|
+
|
244
|
+
def dependencies
|
245
|
+
return @dependencies if @dependencies
|
246
|
+
@dependencies = deps_list.map do |d|
|
247
|
+
d.collect_licenses
|
248
|
+
d.to_dep
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
def deps_list
|
253
|
+
return @deps_list if @deps_list
|
254
|
+
|
255
|
+
xml_doc = REXML::Document.new(dependency_graph_xml)
|
256
|
+
|
257
|
+
root = xml_doc.root
|
258
|
+
|
259
|
+
deps = root.get_elements("//dependency")
|
260
|
+
|
261
|
+
@deps_list = []
|
262
|
+
|
263
|
+
deps.each do |dep|
|
264
|
+
dep_module_name = dep.get_text("module").to_s
|
265
|
+
next if dep_module_name == module_name
|
266
|
+
@deps_list << CPANDependency.new(
|
267
|
+
module_name: dep_module_name,
|
268
|
+
dist: dep.get_text("dist").to_s,
|
269
|
+
version: dep.get_text("distversion").to_s,
|
270
|
+
cpanfile: dep.get_text("cpanfile").to_s,
|
271
|
+
cache_root: options.cpan_cache,
|
272
|
+
overrides: options.overrides
|
273
|
+
)
|
274
|
+
end
|
275
|
+
|
276
|
+
@deps_list
|
277
|
+
end
|
278
|
+
|
279
|
+
def dependency_graph_xml
|
280
|
+
@dependency_graph_xml ||=
|
281
|
+
begin
|
282
|
+
dependency_graph_xml_file = NetFetcher.cache(dependency_graph_url)
|
283
|
+
raw_xml = File.read(dependency_graph_xml_file)
|
284
|
+
FileUtils.rm_f(dependency_graph_xml_file)
|
285
|
+
raw_xml
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
# NOTE: there's no SSL version available. Take care handling any
|
290
|
+
# data/code referenced in responses from this site.
|
291
|
+
def dependency_graph_url
|
292
|
+
"http://deps.cpantesters.org/?xml=1;module=#{module_name};perl=5.24.0;os=any%20OS;pureperl=0"
|
293
|
+
end
|
294
|
+
|
295
|
+
# Infers the module name from the directory name. For Chef Server, the
|
296
|
+
# two perl packages we use are:
|
297
|
+
# * "App-Sqitch-VERSION" => "App::Sqitch"
|
298
|
+
# * "DBD-Pg-VERSION" => "DBD::Pg"
|
299
|
+
#
|
300
|
+
# NOTE: Distributions may contain multiple modules that would each have
|
301
|
+
# their own dependency graphs and it's possible to get a perl project
|
302
|
+
# that doesn't obey this convention (e.g., if you git clone it). But this
|
303
|
+
# meets our immediate needs.
|
304
|
+
def module_name
|
305
|
+
File.basename(project_dir).split("-")[0...-1].join("::")
|
306
|
+
end
|
307
|
+
|
308
|
+
# NOTE: it's possible that projects won't have a META.yml, but the two
|
309
|
+
# that we care about for Chef Server do have one. As of 2015, 84% of perl
|
310
|
+
# distribution packages have one: http://neilb.org/2015/10/18/spotters-guide.html
|
311
|
+
def detected?
|
312
|
+
File.exist?(meta_yml_path)
|
313
|
+
end
|
314
|
+
|
315
|
+
def meta_yml_path
|
316
|
+
File.join(project_dir, "META.yml")
|
317
|
+
end
|
318
|
+
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|