Skip to content

Commit 3d9d2fc

Browse files
committed
Bundler dependency manager handler.
1 parent 5913f5b commit 3d9d2fc

File tree

740 files changed

+92759
-4
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

740 files changed

+92759
-4
lines changed

.rspec

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
--color
22
--require spec_helper
3+
--exclude-pattern spec/fixtures/**/*

.rubocop.yml

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
AllCops:
2+
Excludes:
3+
- spec/fixtures/**/*

lib/license_scout/collector.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ def collect_licenses_from(dependency_manager)
102102

103103
def all_dependency_managers
104104
DependencyManager.implementations.map do |implementation|
105-
implementation.new(overrides)
105+
implementation.new(project_dir, overrides)
106106
end
107107
end
108108
end

lib/license_scout/dependency.rb

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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+
module LicenseScout
19+
Dependency = Struct.new(:name, :version, :license, :license_files)
20+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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+
# When using LicenseScout with Omnibus, LicenseScout is run from the bundled
19+
# omnibus process which has a different ruby executable and rubygems directory
20+
# than the project we want to collect licenses for. Bundler will end up loading
21+
# the gemspecs for the gems we are inspecting, so we need to run our query for
22+
# version and license information from a separate process that executes inside
23+
# the target ruby+bundler environment. This script is the thing that runs that
24+
# query; it's intended to be run like
25+
# `/opt/chef/embedded/bin/ruby /path/to/script`. It returns the data
26+
# LicenseScout needs as JSON on stdout.
27+
28+
# We need to load the target project's bundler config, so we have to do a full
29+
# bundler setup:
30+
require "bundler/setup"
31+
32+
# We're only using things that are in the stdlib.
33+
require "json"
34+
35+
definition = ::Bundler::Definition.build("./Gemfile", "./Gemfile.lock", nil)
36+
dependencies = []
37+
38+
definition.specs_for(definition.groups).each do |gem_spec|
39+
dependencies << {
40+
name: gem_spec.name,
41+
version: gem_spec.version,
42+
license: gem_spec.license,
43+
path: gem_spec.full_gem_path,
44+
}
45+
end
46+
47+
puts JSON.generate(dependencies)

lib/license_scout/dependency_manager/base.rb

+5-1
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,17 @@
1515
# limitations under the License.
1616
#
1717

18+
require "license_scout/dependency"
19+
1820
module LicenseScout
1921
module DependencyManager
2022
class Base
2123

24+
attr_reader :project_dir
2225
attr_reader :overrides
2326

24-
def initialize(overrides)
27+
def initialize(project_dir, overrides)
28+
@project_dir = project_dir
2529
@overrides = overrides
2630
end
2731

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
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 "license_scout/dependency_manager/base"
19+
require "license_scout/exceptions"
20+
21+
require "bundler"
22+
require "mixlib/shellout"
23+
require "ffi_yajl"
24+
25+
module LicenseScout
26+
module DependencyManager
27+
class Bundler < Base
28+
29+
POSSIBLE_LICENSE_FILES = %w{
30+
LICENSE
31+
LICENSE.txt
32+
LICENSE.md
33+
LICENSE.rdoc
34+
License
35+
License.text
36+
License.md
37+
License.rdoc
38+
Licence.rdoc
39+
MIT-LICENSE
40+
MIT-LICENSE.txt
41+
LICENSE.MIT
42+
LGPL-2.1
43+
}
44+
45+
def name
46+
"ruby_bundler"
47+
end
48+
49+
def detected?
50+
# We only check for the existence of Gemfile in order to declare a
51+
# project a Bundler project. If the Gemfile.lock does not exist
52+
# we will raise a specific error to indicate that "bundle install"
53+
# needs to be run before proceeding.
54+
File.exists?(gemfile_path)
55+
end
56+
57+
def dependency_data
58+
bundler_script = File.join(File.dirname(__FILE__), "_bundler_script.rb")
59+
60+
Dir.chdir(project_dir) do
61+
62+
json_dep_data = ::Bundler.with_clean_env do
63+
s = Mixlib::ShellOut.new("ruby #{bundler_script}")
64+
s.run_command
65+
s.error!
66+
s.stdout
67+
end
68+
FFI_Yajl::Parser.parse(json_dep_data)
69+
end
70+
end
71+
72+
def dependencies
73+
if !File.exists?(lockfile_path)
74+
raise LicenseScout::Exceptions::DependencyManagerNotRun.new(project_dir, name)
75+
end
76+
77+
dependencies = []
78+
dependency_data.each do |gem_data|
79+
dependency_name = gem_data["name"]
80+
dependency_version = gem_data["version"]
81+
dependency_license = nil
82+
dependency_license_files = []
83+
84+
if overrides.have_override_for?(name, dependency_name, dependency_version)
85+
dependency_license = overrides.license_for(name, dependency_name, dependency_version)
86+
dependency_license_files = check_override_files(gem_data["path"], overrides.license_files_for(name, dependency_name, dependency_version))
87+
elsif dependency_name == "bundler"
88+
# Bundler is weird. It inserts itself as a dependency, but is a
89+
# special case, so rubygems cannot correctly report the license.
90+
# Additionally, rubygems reports the gem path as a path inside
91+
# bundler's lib/ dir, so we have to munge it.
92+
dependency_license = "MIT"
93+
94+
munged_path = File.expand_path("../../..", gem_data["path"])
95+
dependency_license_files = auto_detect_license_files(munged_path)
96+
else
97+
dependency_license = gem_data["license"]
98+
dependency_license_files = auto_detect_license_files(gem_data["path"])
99+
end
100+
101+
dependencies << Dependency.new(
102+
dependency_name,
103+
dependency_version,
104+
dependency_license,
105+
dependency_license_files
106+
)
107+
end
108+
109+
dependencies
110+
end
111+
112+
private
113+
114+
def auto_detect_license_files(gem_path)
115+
unless File.exist?(gem_path)
116+
raise Exceptions::InaccessibleDependency "Autodetected gem path '#{gem_path}' does not exist"
117+
end
118+
119+
Dir.glob("#{gem_path}/*").select do |f|
120+
POSSIBLE_LICENSE_FILES.include?(File.basename(f))
121+
end
122+
end
123+
124+
def check_override_files(gem_path, override_license_files)
125+
license_files = []
126+
127+
override_license_files.each do |filepath|
128+
potential_path = File.join(gem_path, filepath)
129+
130+
unless File.exists?(potential_path)
131+
raise Exceptions::InvalidOverride, "Provided license file path '#{filepath}' can not be found under detected gem path '#{gem_path}'."
132+
end
133+
134+
license_files << potential_path
135+
end
136+
137+
license_files
138+
end
139+
140+
def gemfile_path
141+
File.join(project_dir, "Gemfile")
142+
end
143+
144+
def lockfile_path
145+
File.join(project_dir, "Gemfile.lock")
146+
end
147+
end
148+
end
149+
end

lib/license_scout/exceptions.rb

+14
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,19 @@ def to_s
3939
end
4040
end
4141

42+
class DependencyManagerNotRun < Error
43+
def initialize(project_dir, dependency_manager_name)
44+
@project_dir = project_dir
45+
@dependency_manager_name = dependency_manager_name
46+
end
47+
48+
def to_s
49+
"Dependency manager '#{dependency_manager_name}' is not yet run for project at '#{@project_dir}'."
50+
end
51+
end
52+
53+
class InaccessibleDependency < Error; end
54+
class InvalidOverride < Error; end
55+
4256
end
4357
end

license_scout.gemspec

+3
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,11 @@ Gem::Specification.new do |spec|
3636
spec.require_paths = %w{lib}
3737

3838
spec.add_dependency "ffi-yajl", "~> 2.2"
39+
spec.add_dependency "mixlib-shellout", "~> 2.2"
3940

4041
spec.add_development_dependency "bundler", "~> 1.12"
4142
spec.add_development_dependency "rake", "~> 10.0"
4243
spec.add_development_dependency "rspec"
44+
spec.add_development_dependency "pry"
45+
spec.add_development_dependency "rb-readline"
4346
end

spec/fixtures/bundler/.bundle/config

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
---
2+
BUNDLE_PATH: vendor/bundle
3+
BUNDLE_DISABLE_SHARED_GEMS: true

spec/fixtures/bundler/Gemfile

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Gemfile used to test bundler dependency manager.
2+
3+
source "https://rubygems.org"
4+
5+
gem "test-kitchen"

spec/fixtures/bundler/Gemfile.lock

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
GEM
2+
remote: https://rubygems.org/
3+
specs:
4+
artifactory (2.3.3)
5+
mixlib-install (1.1.0)
6+
artifactory
7+
mixlib-shellout
8+
mixlib-versioning
9+
mixlib-shellout (2.2.6)
10+
mixlib-versioning (1.1.0)
11+
net-scp (1.2.1)
12+
net-ssh (>= 2.6.5)
13+
net-ssh (3.2.0)
14+
safe_yaml (1.0.4)
15+
test-kitchen (1.10.2)
16+
mixlib-install (~> 1.0, >= 1.0.4)
17+
mixlib-shellout (>= 1.2, < 3.0)
18+
net-scp (~> 1.1)
19+
net-ssh (>= 2.9, < 4.0)
20+
safe_yaml (~> 1.0)
21+
thor (~> 0.18)
22+
thor (0.19.1)
23+
24+
PLATFORMS
25+
ruby
26+
27+
DEPENDENCIES
28+
test-kitchen
29+
30+
BUNDLED WITH
31+
1.12.5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/usr/bin/env ruby
2+
#
3+
# This file was generated by RubyGems.
4+
#
5+
# The application 'test-kitchen' is installed as part of a gem, and
6+
# this file is here to facilitate running it.
7+
#
8+
9+
require 'rubygems'
10+
11+
version = ">= 0.a"
12+
13+
if ARGV.first
14+
str = ARGV.first
15+
str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding
16+
if str =~ /\A_(.*)_\z/ and Gem::Version.correct?($1) then
17+
version = $1
18+
ARGV.shift
19+
end
20+
end
21+
22+
load Gem.activate_bin_path('test-kitchen', 'kitchen', version)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/usr/bin/env ruby
2+
#
3+
# This file was generated by RubyGems.
4+
#
5+
# The application 'safe_yaml' is installed as part of a gem, and
6+
# this file is here to facilitate running it.
7+
#
8+
9+
require 'rubygems'
10+
11+
version = ">= 0.a"
12+
13+
if ARGV.first
14+
str = ARGV.first
15+
str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding
16+
if str =~ /\A_(.*)_\z/ and Gem::Version.correct?($1) then
17+
version = $1
18+
ARGV.shift
19+
end
20+
end
21+
22+
load Gem.activate_bin_path('safe_yaml', 'safe_yaml', version)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/usr/bin/env ruby
2+
#
3+
# This file was generated by RubyGems.
4+
#
5+
# The application 'thor' is installed as part of a gem, and
6+
# this file is here to facilitate running it.
7+
#
8+
9+
require 'rubygems'
10+
11+
version = ">= 0.a"
12+
13+
if ARGV.first
14+
str = ARGV.first
15+
str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding
16+
if str =~ /\A_(.*)_\z/ and Gem::Version.correct?($1) then
17+
version = $1
18+
ARGV.shift
19+
end
20+
end
21+
22+
load Gem.activate_bin_path('thor', 'thor', version)
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)