homebrew-cask/lib/hbc/utils.rb

308 lines
8.5 KiB
Ruby
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

module Hbc::Utils; end
require 'yaml'
require 'open3'
require 'stringio'
require 'hbc/utils/tty'
UPDATE_CMD = "brew uninstall --force brew-cask; brew untap phinze/cask; brew update; brew cleanup; brew cask cleanup"
ISSUES_URL = "https://github.com/caskroom/homebrew-cask#reporting-bugs"
# todo: temporary
Tty = Hbc::Utils::Tty
# monkeypatch Object - not a great idea
class Object
def utf8_inspect
if not defined?(Encoding)
self.inspect
else
if self.respond_to?(:map)
self.map do |sub_elt|
sub_elt.utf8_inspect
end
else
self.inspect.force_encoding('UTF-8').sub(%r{\A"(.*)"\Z}, '\1')
end
end
end
end
class Buffer < StringIO
def initialize(tty = false)
super()
@tty = tty
end
def tty?
@tty
end
end
# global methods
# originally from Homebrew
def ohai(title, *sput)
title = Tty.truncate(title) if $stdout.tty? && !Hbc.verbose
puts "#{Tty.blue.bold}==>#{Tty.reset.bold} #{title}#{Tty.reset}"
puts sput unless sput.empty?
end
# originally from Homebrew
def opoo(warning)
$stderr.puts "#{Tty.yellow.underline}Warning#{Tty.reset}: #{warning}"
end
# originally from Homebrew
def onoe(error)
$stderr.puts "#{Tty.red.underline}Error#{Tty.reset}: #{error}"
end
def odebug(title, *sput)
if Hbc.respond_to?(:debug) and Hbc.debug
width = Tty.width * 4 - 6
if $stdout.tty? and title.to_s.length > width
title = title.to_s[0, width - 3] + '...'
end
puts "#{Tty.magenta.bold}==>#{Tty.reset.bold} #{title}#{Tty.reset}"
puts sput unless sput.empty?
end
end
def puts_columns(items, star_items=[])
return if items.empty?
puts Hbc::Utils.stringify_columns(items, star_items)
end
module Hbc::Utils
def self.which(cmd, path=ENV['PATH'])
unless File.basename(cmd) == cmd.to_s
# cmd contains a directory element
cmd_pn = Pathname(cmd)
return nil unless cmd_pn.absolute?
return resolve_executable(cmd_pn)
end
path.split(File::PATH_SEPARATOR).each do |elt|
fq_cmd = Pathname(elt).expand_path.join(cmd)
resolved = resolve_executable fq_cmd
return resolved if resolved
end
return nil
end
def self.resolve_executable(cmd)
cmd_pn = Pathname(cmd)
return nil unless cmd_pn.exist?
return nil unless cmd_pn.executable?
begin
cmd_pn = Pathname(cmd_pn.realpath)
rescue RuntimeError => e
return nil
end
return nil unless cmd_pn.file?
return cmd_pn
end
def self.exec_editor(*args)
editor = [
*ENV.values_at('HOMEBREW_EDITOR', 'VISUAL', 'EDITOR'),
*%w{mate edit vim /usr/bin/vim}.map{ |x| which(x) }
].compact.first.to_s
exec(*editor.split.concat(args))
end
# originally from Homebrew puts_columns
def self.stringify_columns items, star_items=[]
return if items.empty?
if star_items && star_items.any?
items = items.map{|item| star_items.include?(item) ? "#{item}*" : item}
end
unless $stdout.tty?
return items.join("\n").concat("\n")
end
# determine the best width to display for different console sizes
console_width = `/bin/stty size 2>/dev/null`.chomp.split(' ').last.to_i
console_width = 80 if console_width <= 0
longest = items.sort_by { |item| item.length }.last
optimal_col_width = (console_width.to_f / (longest.length + 2).to_f).floor
cols = optimal_col_width > 1 ? optimal_col_width : 1
Open3.popen3('/usr/bin/pr', "-#{cols}", '-t', "-w#{console_width}") do |stdin, stdout, stderr|
stdin.puts(items)
stdin.close
stdout.read
end
end
# originally from Homebrew
# children.length == 0 is slow to enumerate the whole directory just
# to see if it is empty
def self.rmdir_if_possible(dir)
dirpath = Pathname(dir)
begin
dirpath.rmdir
true
rescue Errno::ENOTEMPTY
if (ds_store = dirpath.join('.DS_Store')).exist? and
dirpath.children.length == 1
ds_store.unlink
retry
else
false
end
rescue Errno::EACCES, Errno::ENOENT
false
end
end
# originally from Homebrew abv
def self.cabv(dir)
output = ''
count = Hbc::SystemCommand.run!('/usr/bin/find',
:args => [dir, *%w[-type f -not -name .DS_Store -print0]],
:print_stderr => false).stdout.count("\000")
size = Hbc::SystemCommand.run!('/usr/bin/du',
:args => ['-hs', '--', dir],
:print_stderr => false).stdout.split("\t").first.strip
output << "#{count} files, " if count > 1
output << size
end
# paths that "look" descendant (textually) will still
# return false unless both the given paths exist
def self.file_is_descendant(file, dir)
file = Pathname.new(file)
dir = Pathname.new(dir)
return false unless file.exist? and dir.exist?
unless dir.directory?
onoe "Argument must be a directory: '#{dir}'"
return false
end
unless file.absolute? and dir.absolute?
onoe "Both arguments must be absolute: '#{file}', '#{dir}'"
return false
end
while file.parent != file
return true if File.identical?(file, dir)
file = file.parent
end
return false
end
def self.error_message_with_suggestions
<<-EOS.undent
#{ Tty.reset.bold }
Most likely, this means you have an outdated version of homebrew-cask.#{
} Please run:
#{ Tty.green.normal }#{ UPDATE_CMD }
#{ Tty.reset.bold }If this doesnt fix the problem, please report this bug:
#{ Tty.underline }#{ ISSUES_URL }#{ Tty.reset }
EOS
end
def self.method_missing_message(method, token, section=nil)
poo = []
poo << "Unexpected method '#{method}' called"
poo << "during #{section}" if section
poo << "on Cask #{token}."
opoo(poo.join(' ') + "\n" + error_message_with_suggestions)
end
# originally from Homebrew
def self.ignore_interrupts(opt = nil)
std_trap = trap('INT') do
puts 'One sec, just cleaning up' unless opt == :quietly
end
yield
ensure
trap('INT', std_trap)
end
def self.nowstamp_metadata_path(container_path)
@timenow ||= Time.now.gmtime
if container_path.respond_to?(:join)
precision = 3
timestamp = @timenow.strftime('%Y%m%d%H%M%S')
fraction = ("%.#{precision}f" % (@timenow.to_f - @timenow.to_i))[1..-1]
timestamp.concat(fraction)
container_path.join(timestamp)
end
end
def self.size_in_bytes(files)
Array(files).reduce(0) { |t, f| t + (File.size?(f) || 0) }
end
# originally from Homebrew
def self.disk_usage_readable(size_in_bytes)
if size_in_bytes >= 1_073_741_824
size = size_in_bytes.to_f / 1_073_741_824
unit = "G"
elsif size_in_bytes >= 1_048_576
size = size_in_bytes.to_f / 1_048_576
unit = "M"
elsif size_in_bytes >= 1_024
size = size_in_bytes.to_f / 1_024
unit = "K"
else
size = size_in_bytes
unit = "B"
end
# avoid trailing zero after decimal point
if (size * 10).to_i % 10 == 0
"#{size.to_i}#{unit}"
else
"#{"%.1f" % size}#{unit}"
end
end
# originally from Homebrew
def self.number_readable(number)
numstr = number.to_i.to_s
(numstr.size - 3).step(1, -3) { |i| numstr.insert(i, ",") }
numstr
end
# originally from Homebrew
def self.install_gem_setup_path!(gem_name, version = nil, executable = gem_name)
require "rubygems"
ENV["PATH"] = "#{Gem.user_dir}/bin:#{ENV["PATH"]}"
if Gem::Specification.find_all_by_name(gem_name, version).empty?
ohai "Installing or updating '#{gem_name}' gem"
install_args = %W[--no-ri --no-rdoc --user-install #{gem_name}]
install_args << "--version" << version if version
# Do `gem install [...]` without having to spawn a separate process or
# having to find the right `gem` binary for the running Ruby interpreter.
require "rubygems/commands/install_command"
install_cmd = Gem::Commands::InstallCommand.new
install_cmd.handle_options(install_args)
exit_code = 1 # Should not matter as `install_cmd.execute` always throws.
begin
install_cmd.execute
rescue Gem::SystemExitException => e
exit_code = e.exit_code
end
raise Hbc::CaskError.new("Failed to install/update the '#{gem_name}' gem.") if exit_code != 0
end
unless which executable
raise Hbc::CaskError.new(<<-EOS.undent)
The '#{gem_name}' gem is installed but couldn't find '#{executable}' in the PATH:
#{ENV["PATH"]}
EOS
end
end
end