module Binbundle # Main class class GemBins def local_gems Gem::Specification.sort_by { |g| [g.name.downcase, g.version] }.group_by(&:name) end def initialize(options = {}) @include_version = options[:include_version] || false @user_install = options[:user_install] @sudo = options[:sudo] @dry_run = options[:dry_run] @file = File.expand_path(options[:file]) @local_gems = local_gems.delete_if { |_, specs| specs.delete_if { |spec| spec.executables.empty? }.empty? } end def install unless File.exist?(@file) puts "File #{@file} not found" Process.exit 1 end res = Prompt.yn("Install gems from #{File.basename(@file)}", default_response: true) Process.exit 0 unless res puts "Installing gems from #{@file}" contents = IO.read(@file) gem_list = GemList.new(contents, include_version: @include_version) lines = if @sudo gem_list.sudo elsif @user_install gem_list.user_install else gem_list.normal_install end if @dry_run puts lines.join("\n") Process.exit 0 end `sudo echo -n ''` if @sudo @errors = [] lines.each do |cmd| spinner = TTY::Spinner.new("[:spinner] #{cmd} ...", hide_cursor: true, format: :dots_2) spinner.auto_spin output = `/bin/bash -c '#{cmd}' 2>&1` result = $?.success? if result spinner.success spinner.stop else spinner.error spinner.stop @errors << output end end unless @errors.empty? puts "ERRORS:" puts @errors.join("\n") end end def gem_command(gem, attrs) ver = @include_version ? " -v '#{attrs[:version]}'" : '' ui = @user_install ? '--user-install ' : '' sudo = @sudo ? 'sudo ' : '' "# Executables: #{attrs[:bins].join(', ')}\n#{sudo}gem install #{ui}#{gem}#{ver}" end def generate gems_with_bins = {} @local_gems.each do |g, specs| versions = specs.map { |spec| spec.version.to_s } bins = specs.map(&:executables) gems_with_bins[g] = { version: versions.max, bins: bins.sort.uniq } end output = gems_with_bins.map { |gem, attrs| gem_command(gem, attrs) }.join("\n\n") if @dry_run puts output else write_file(output) end end def write_file(output) if File.exist?(@file) res = Prompt.yn("#{@file} already exists, overwrite", default_response: false) Process.exit 1 unless res end File.open(@file, 'w') do |f| f.puts '#!/bin/bash' f.puts f.puts output end puts "Wrote list to #{@file}" res = Prompt.yn('Make file executable', default_response: true) return unless res FileUtils.chmod 0o777, @file puts 'Made file executable' end end end