Class: Licensed::Sources::Bundler
- Defined in:
- lib/licensed/sources/bundler.rb
Defined Under Namespace
Classes: BundlerSpecification, Dependency, MissingSpecification
Constant Summary collapse
- GEMFILES =
{ "Gemfile" => "Gemfile.lock", "gems.rb" => "gems.locked" }
- DEFAULT_WITHOUT_GROUPS =
i{development test}
Instance Attribute Summary
Attributes inherited from Source
Instance Method Summary collapse
-
#bundle_exec_gem_spec(name, requirement) ⇒ Object
Load a gem specification from the YAML returned from ‘gem specification` This is a last resort when licensed can’t obtain a specification from other means.
-
#bundler_exe ⇒ Object
Returns the configured bundler executable to use, or “bundle” by default.
-
#bundler_setting_array(key) ⇒ Object
Returns a bundler setting as an array.
-
#definition ⇒ Object
Build the bundler definition.
- #enabled? ⇒ Boolean
- #enumerate_dependencies ⇒ Object
-
#exclude_development_dependencies? ⇒ Boolean
Returns whether development dependencies should be excluded.
-
#exclude_groups ⇒ Object
Returns any groups to exclude specified from both licensed configuration and bundler configuration.
-
#gem_spec(dependency) ⇒ Object
Returns a Gem::Specification for the provided gem argument.
-
#gemfile_path ⇒ Object
Returns the path to the Bundler Gemfile.
-
#groups ⇒ Object
Returns the bundle definition groups, removing “without” groups, and including “with” groups.
-
#include?(dependency, source) ⇒ Boolean
Returns whether a dependency should be included in the final.
-
#lockfile_path ⇒ Object
Returns the path to the Bundler Gemfile.lock.
-
#matching_spec(dependency) ⇒ Object
Loads a dependency specification using rubygems’ built-in ‘Dependency#matching_specs` and `Dependency#to_spec`, from the original gem environment.
-
#recursive_specs(specs, results = Set.new) ⇒ Object
Recursively finds the dependencies for Gem specifications.
-
#ruby_command_args(*args) ⇒ Object
Determines if the configured bundler executable is available and returns shell command args with or without ‘bundle exec` depending on availability.
-
#specs ⇒ Object
Returns an array of Gem::Specifications for all gem dependencies.
-
#specs_for_dependencies(dependencies, source) ⇒ Object
Returns the specs for dependencies that pass the checks in ‘include?`.
Methods inherited from Source
#dependencies, #ignored?, inherited, #initialize, type
Constructor Details
This class inherits a constructor from Licensed::Sources::Source
Instance Method Details
#bundle_exec_gem_spec(name, requirement) ⇒ Object
Load a gem specification from the YAML returned from ‘gem specification` This is a last resort when licensed can’t obtain a specification from other means
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 |
# File 'lib/licensed/sources/bundler.rb', line 203 def bundle_exec_gem_spec(name, requirement) # `gem` must be available to run `gem specification` return unless Licensed::Shell.tool_available?("gem") # use `gem specification` with a clean ENV and clean Gem.dir paths # to get gem specification at the right directory begin ::Bundler.with_original_env do ::Bundler.rubygems.clear_paths yaml = Licensed::Shell.execute(*ruby_command_args("gem", "specification", name, "-v", requirement.to_s)) spec = Gem::Specification.from_yaml(yaml) # this is horrible, but it will cache the gem_dir using the clean env # so that it can be used outside of this block when running from # the ruby packer executable environment spec.gem_dir if ruby_packer? spec end rescue Licensed::Shell::Error # return nil ensure ::Bundler.configure end end |
#bundler_exe ⇒ Object
Returns the configured bundler executable to use, or “bundle” by default.
287 288 289 290 291 292 293 294 |
# File 'lib/licensed/sources/bundler.rb', line 287 def bundler_exe @bundler_exe ||= begin exe = config.dig("bundler", "bundler_exe") return "bundle" unless exe return exe if Licensed::Shell.tool_available?(exe) config.root.join(exe) end end |
#bundler_setting_array(key) ⇒ Object
Returns a bundler setting as an array. Depending on the version of bundler, array values are either returned as a raw string (“a:b:c”) or as an array ([:a, :b, :c])
256 257 258 259 260 |
# File 'lib/licensed/sources/bundler.rb', line 256 def bundler_setting_array(key) setting = ::Bundler.settings[key] setting = setting.split(":").map(&:to_sym) if setting.is_a?(String) Array(setting) end |
#definition ⇒ Object
Build the bundler definition
243 244 245 |
# File 'lib/licensed/sources/bundler.rb', line 243 def definition @definition ||= ::Bundler::Definition.build(gemfile_path, lockfile_path, nil) end |
#enabled? ⇒ Boolean
80 81 82 83 84 85 |
# File 'lib/licensed/sources/bundler.rb', line 80 def enabled? # running a ruby-packer-built licensed exe when ruby isn't available # could lead to errors if the host ruby doesn't exist return false if ruby_packer? && !Licensed::Shell.tool_available?("ruby") defined?(::Bundler) && lockfile_path && lockfile_path.exist? end |
#enumerate_dependencies ⇒ Object
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/licensed/sources/bundler.rb', line 87 def enumerate_dependencies with_local_configuration do specs.map do |spec| error = spec.error if spec.respond_to?(:error) Dependency.new( name: spec.name, version: spec.version.to_s, path: spec.gem_dir, loaded_from: spec.loaded_from, errors: Array(error), metadata: { "type" => Bundler.type, "summary" => spec.summary, "homepage" => spec.homepage } ) end end end |
#exclude_development_dependencies? ⇒ Boolean
Returns whether development dependencies should be excluded
192 193 194 195 196 197 198 199 |
# File 'lib/licensed/sources/bundler.rb', line 192 def exclude_development_dependencies? @include_development ||= begin # check whether the development dependency group is explicitly removed # or added via bundler and licensed configurations groups = [:development] - Array(::Bundler.settings[:without]) + Array(::Bundler.settings[:with]) - exclude_groups !groups.include?(:development) end end |
#exclude_groups ⇒ Object
Returns any groups to exclude specified from both licensed configuration and bundler configuration. Defaults to [:development, :test] + ::Bundler.settings
265 266 267 268 269 270 271 |
# File 'lib/licensed/sources/bundler.rb', line 265 def exclude_groups @exclude_groups ||= begin exclude = Array(config.dig("bundler", "without")) exclude = DEFAULT_WITHOUT_GROUPS if exclude.empty? exclude.uniq.map(&:to_sym) end end |
#gem_spec(dependency) ⇒ Object
Returns a Gem::Specification for the provided gem argument.
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
# File 'lib/licensed/sources/bundler.rb', line 147 def gem_spec(dependency) return unless dependency # find a specifiction from the resolved ::Bundler::Definition specs spec = definition.resolve.find { |s| s.satisfies?(dependency) } # a nil spec should be rare, generally only seen from bundler return matching_spec(dependency) || bundle_exec_gem_spec(dependency.name, dependency.requirement) if spec.nil? # try to find a non-lazy specification that matches `spec` # spec.source.specs gives access to specifications with more # information than spec itself, including platform-specific gems. # these objects should have all the information needed to detect license metadata source_spec = spec.source.specs.find { |s| s.name == spec.name && s.version == spec.version } return source_spec if source_spec # look for a specification at the bundler specs path spec_path = ::Bundler.specs_path.join("#{spec.full_name}.gemspec") return Gem::Specification.load(spec_path.to_s) if File.exist?(spec_path.to_s) # if the specification file doesn't exist, get the specification using # the bundler and gem CLI bundle_exec_gem_spec(dependency.name, dependency.requirement) end |
#gemfile_path ⇒ Object
Returns the path to the Bundler Gemfile
274 275 276 277 278 |
# File 'lib/licensed/sources/bundler.rb', line 274 def gemfile_path @gemfile_path ||= GEMFILES.keys .map { |g| config.pwd.join g } .find { |f| f.exist? } end |
#groups ⇒ Object
Returns the bundle definition groups, removing “without” groups, and including “with” groups
249 250 251 |
# File 'lib/licensed/sources/bundler.rb', line 249 def groups @groups ||= definition.groups - bundler_setting_array(:without) + bundler_setting_array(:with) - exclude_groups end |
#include?(dependency, source) ⇒ Boolean
Returns whether a dependency should be included in the final
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/licensed/sources/bundler.rb', line 173 def include?(dependency, source) # ::Bundler::Dependency has an extra `should_include?` return false unless dependency.should_include? if dependency.respond_to?(:should_include?) # Don't return gems added from `add_development_dependency` in a gemspec # if the :development group is excluded gemspec_source = source.is_a?(::Bundler::Source::Gemspec) return false if dependency.type == :development && (!gemspec_source || exclude_development_dependencies?) # Gem::Dependency don't have groups - in our usage these objects always # come as child-dependencies and are never directly from a Gemfile. # We assume that all Gem::Dependencies are ok at this point return true if dependency.groups.nil? # check if the dependency is in any groups we're interested in (dependency.groups & groups).any? end |
#lockfile_path ⇒ Object
Returns the path to the Bundler Gemfile.lock
281 282 283 284 |
# File 'lib/licensed/sources/bundler.rb', line 281 def lockfile_path return unless gemfile_path @lockfile_path ||= gemfile_path.dirname.join(GEMFILES[gemfile_path.basename.to_s]) end |
#matching_spec(dependency) ⇒ Object
Loads a dependency specification using rubygems’ built-in ‘Dependency#matching_specs` and `Dependency#to_spec`, from the original gem environment
230 231 232 233 234 235 236 237 238 239 240 |
# File 'lib/licensed/sources/bundler.rb', line 230 def matching_spec(dependency) begin ::Bundler.with_original_env do ::Bundler.rubygems.clear_paths return unless dependency.matching_specs(true).any? BundlerSpecification.new(dependency.to_spec) end ensure ::Bundler.configure end end |
#recursive_specs(specs, results = Set.new) ⇒ Object
Recursively finds the dependencies for Gem specifications. Returns a ‘Set` containing the package names for all dependencies
122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/licensed/sources/bundler.rb', line 122 def recursive_specs(specs, results = Set.new) return [] if specs.nil? || specs.empty? new_specs = Set.new(specs) - results.to_a return [] if new_specs.empty? results.merge new_specs dependency_specs = new_specs.flat_map { |s| specs_for_dependencies(s.dependencies, s.source) } return results if dependency_specs.empty? results.merge recursive_specs(dependency_specs, results) end |
#ruby_command_args(*args) ⇒ Object
Determines if the configured bundler executable is available and returns shell command args with or without ‘bundle exec` depending on availability.
298 299 300 301 |
# File 'lib/licensed/sources/bundler.rb', line 298 def ruby_command_args(*args) return Array(args) unless Licensed::Shell.tool_available?(bundler_exe) [bundler_exe, "exec", *args] end |
#specs ⇒ Object
Returns an array of Gem::Specifications for all gem dependencies
108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/licensed/sources/bundler.rb', line 108 def specs # get the specifications for all dependencies in a Gemfile root_dependencies = definition.dependencies.select { |d| include?(d, nil) } root_specs = specs_for_dependencies(root_dependencies, nil).compact # recursively find the remaining specifications all_specs = recursive_specs(root_specs) # delete any specifications loaded from a gemspec all_specs.delete_if { |s| s.source.is_a?(::Bundler::Source::Gemspec) } end |
#specs_for_dependencies(dependencies, source) ⇒ Object
Returns the specs for dependencies that pass the checks in ‘include?`. Returns a `MissingSpecification` if a gem specification isn’t found.
139 140 141 142 143 144 |
# File 'lib/licensed/sources/bundler.rb', line 139 def specs_for_dependencies(dependencies, source) included_dependencies = dependencies.select { |d| include?(d, source) } included_dependencies.map do |dep| gem_spec(dep) || MissingSpecification.new(name: dep.name, requirement: dep.requirement) end end |