Skip to content

Instantly share code, notes, and snippets.

@nicolas-brousse
Last active October 25, 2018 18:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nicolas-brousse/4ae026966fee51831c01cf725d726514 to your computer and use it in GitHub Desktop.
Save nicolas-brousse/4ae026966fee51831c01cf725d726514 to your computer and use it in GitHub Desktop.
Get dependencies list of a Ruby project by reading its Gemfile and Gemfile.lock
#!/usr/bin/env ruby
# frozen_string_literal: true
# USAGE:
# $ ./list_dependencies.rb
# $ ./list_dependencies.rb /path/to/project
#
# @see: https://stackoverflow.com/questions/44681231/how-to-list-ruby-production-only-dependencies-using-gemfile-lock-and-lockfilepar
require "bundler"
project_path = File.expand_path(ARGV[0] || ".", __dir__)
gemfile_path = File.expand_path("./Gemfile", project_path)
gemfile_lock_path = File.expand_path("./Gemfile.lock", project_path)
class DependecyTree
def initialize(gemfile_path, gemfile_lock_path)
@definition = Bundler::Definition.build(gemfile_path, gemfile_lock_path, nil)
end
def all_dependencies
return @all_dependencies if @all_dependencies
collect_dependencies
end
def lock_file
@definition.locked_gems
end
def groupify(dep)
dep.groups.map do |g|
a = [{ group: g, name: dep.name, dep: dep }]
begin
a << runtime_dependencies(g, dep.to_spec)
rescue Gem::LoadError => _e
a[-1] = { group: g, name: dep.name, dep: dep, error: "NOT INSTALLED" }
end
end
end
def collect_dependencies
@all_dependencies = @definition.dependencies.map do |dep|
groupify(dep)
end.flatten
group_missing(@all_dependencies)
@all_dependencies.uniq!
end
def runtime_dependencies(group, spec)
spec.dependencies.select { |dep| dep.type == :runtime }.map do |dep|
a = { group: group, name: dep.name, dep: dep }
dep.to_spec.dependencies.empty? ? a : [a] << runtime_dependencies(group, dep.to_spec)
end
end
def group_missing(dependencies)
all_locks.cycle(2) do |a|
deep_dep = dependencies.find_all { |h| a.include?(h[:name]) }.uniq
a.each do |k|
deep_dep.each do |h|
dependencies << { group: h[:group], name: k, error: "NOT INSTALLED" }
end
end
end
end
def all_locks
lock_file.specs.map do |spec|
spec.to_lock.split("\n").map do |s|
s.slice(/[\w\-]+/)
end
end
end
end
# Print
$stdout.puts "Ruby\t#{RUBY_VERSION}\tRuby is a dynamic, interpreted, reflective, object-oriented, general-purpose programming language.\tGPLv2 or 2-clause BSD license\thttps://www.ruby-lang.org"
$stdout.puts ""
$stdout.puts "Name\tVersion\tDescription\tLicense(s)\tWebsite"
DependecyTree.new(gemfile_path, gemfile_lock_path).all_dependencies.uniq { |s| s[:name] }.sort{ |a, b| a[:name] <=> b[:name] }.each do |desc|
gemspec = desc[:dep].to_spec
$stdout.puts "#{gemspec.name}\t#{gemspec.version}\t#{gemspec.summary}\t#{gemspec.licenses.join(", ")}\t#{gemspec.homepage}"
rescue Gem::MissingSpecError => _e
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment