Skip to content

Instantly share code, notes, and snippets.

@chetan
Created November 24, 2021 18:13
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 chetan/3151545b8d7a078bc531c53d7e3f8c64 to your computer and use it in GitHub Desktop.
Save chetan/3151545b8d7a078bc531c53d7e3f8c64 to your computer and use it in GitHub Desktop.
Quickly search for homebrew formula or casks and display info via fzf
#!/usr/bin/env ruby
# bi
# Quickly fetch and display info for the given homebrew formula or cask
#
# perf:
# brew info nginx -> 0m1.742s
# bi nginx -> 0m0.166s
#
# Works well with fzf (via brew.fzf)
# rubocop:disable Metrics, Style/Documentation, Style/CaseLikeIf
HOMEBREW_PREFIX = '/usr/local'
def cask(name, &block)
classname = name.capitalize
eval <<~EOF
class #{classname} < Formula
def self._caskify(&block)
instance_exec(&block)
end
def self.version(str=nil)
return @version if @version
@version = Cask::DSL::Version.new(str)
end
def self.name(str=nil)
return @name if @name
@name = str
end
def cask?
true
end
end
EOF
clazz = Module.const_get(classname.to_sym)
clazz._caskify(&block)
obj = clazz.new(name)
end
class String
def /(other)
if end_with?('/')
self + other
else
self + '/' + other
end
end
def exist?
File.exist?(self)
end
def read
IO.read(self) if exist?
end
end
require 'cgi'
require 'forwardable'
require 'pathname'
$LOAD_PATH << '/usr/local/Homebrew/Library/Homebrew'
require 'vendor/bundle/ruby/2.6.0/gems/sorbet-runtime-stub-0.2.0/lib/sorbet-runtime-stub'
require 'vendor/bundle/ruby/2.6.0/gems/activesupport-6.1.4.1/lib/active_support/core_ext/object/blank'
# require 'extend/pathname'
require 'version'
require 'utils/shell'
require 'cask/dsl/version'
class Pathname
def stem
File.basename(self, extname)
end
end
module Language
module Python
module Virtualenv
end
end
end
class Array
def second
self[1]
end
end
class Formula
include Utils::Shell
class << self
attr_reader :deprecated, :name
def [](key)
Formula.new(key)
end
def descendants
ObjectSpace.each_object(Class).select { |klass| klass < self }
end
def method_missing(symbol, *args)
var = ('@' + symbol.id2name).to_sym
if block_given?
pass
elsif args.empty?
instance_variable_get(var)
elsif args.length == 1
instance_variable_set(var, args[0])
else
instance_variable_set(var, args)
end
end
def respond_to_missing?(_name, _include_private = false)
true
end
def depends_on(pkg = nil)
unless pkg.nil?
@depends_on ||= []
@depends_on << pkg
end
@depends_on
end
def deprecate!(*args)
@deprecated = true if args.length > 0
end
def test(); end
end
def initialize(name)
@name = name
end
def caveats; end
def deps
return @deps if @deps
deps = self.class.depends_on
return '' if !deps || deps.empty?
@deps = deps.map do |d|
if d.is_a?(String)
d
elsif d.is_a?(Symbol)
nil
else
r = []
d.each do |k, v|
r << k if v != :build
end
r.join(' ')
end
end.reject { |s| s.nil? || s.empty? }.join(' ')
end
def version
return @version if @version
@version = if self.class.version
Version.parse(self.class.version)
else
Version.parse(self.class.url)
end
end
def cask?
false
end
def any_version_installed?
false
end
attr_reader :name
def etc
HOMEBREW_PREFIX / 'etc'
end
def var
HOMEBREW_PREFIX / 'var'
end
def opt_prefix
HOMEBREW_PREFIX / 'opt' / name
end
def opt_bin
opt_prefix / 'bin'
end
def opt_include
opt_prefix / 'include'
end
def opt_lib
opt_prefix / 'lib'
end
def opt_libexec
opt_prefix / 'libexec'
end
def opt_sbin
opt_prefix / 'sbin'
end
def opt_share
opt_prefix / 'share'
end
def opt_pkgshare
opt_prefix / 'share' / name
end
def opt_elisp
opt_prefix / 'share/emacs/site-lisp' / name
end
def opt_frameworks
opt_prefix / 'Frameworks'
end
def shell_profile
Utils::Shell.profile
end
end
def main
tap = nil
formula = ARGV[0]
# Find the correct formula/cask path
if formula.include? '/'
fs = formula.split('/')
tap = fs.first
formula = fs.last
paths = `fd ^#{formula}.rb /usr/local/Homebrew/Library/Taps/#{tap}`.strip.split(/\n/)
else
paths = `fd ^#{formula}.rb /usr/local/Homebrew/Library/Taps`.strip.split(/\n/)
end
if paths.empty?
puts "error: formula '#{formula}' not found"
exit 1
end
if paths.length == 1
path = paths[0]
else
path = paths.find { |s| s.include? 'homebrew/homebrew-core' }
path = paths[0] if path.nil?
end
# Load it
require path
# Create an instance
clazz = Formula.descendants.first
f = clazz.new(formula)
ver = if f.cask?
clazz.version
else
f.version.to_s.gsub(/\.tar$/, '')
end
# Print info
puts "#{formula}: #{ver}"
puts clazz.desc unless clazz.desc.blank?
puts clazz.homepage unless clazz.homepage.blank?
puts "tap: #{tap}" if tap
puts 'cask' if f.cask?
puts 'Deprecated' if clazz.deprecated
puts "deps: #{f.deps}" unless f.deps.blank?
unless f.caveats.nil? || f.caveats.empty?
puts 'caveats:'
puts f.caveats
end
end
main
#!/usr/bin/env bash
# brew.fzf
# Quickly search for homebrew formula or casks and display info via fzf
function brew.fzf {
! type fzf &>/dev/null &&
echo "fzf required. Install with 'brew install fzf'." &&
return 1
local str=""
declare -a FLAGS=(--formula)
while [ ! $# -eq 0 ]; do
case "$1" in
-c | --casks | --cask)
FLAGS+=("--casks")
;;
-h | --help)
_brew.fzf.usage
exit 0
;;
*)
str="$str $1"
;;
esac
shift
done
str=$(echo "$str" | tr -d ' ')
if [ -z "$str" ]; then
_brew.fzf.usage
exit 0
fi
brew search ${FLAGS[@]} $str | grep -v '==' | grep -v '^$' | fzf -m +s --header "searching homebrew for '$str'" --preview 'bi {}'
}
function _brew.fzf.usage {
cat << EOF
usage: $PROGRAM [--casks] <search term>
Search for homebrew formulas and/or casks with the help of fzf
OPTIONS:
-c|--casks Include casks in the search (default: false)
-h|--help Show this message
EOF
}
PROGRAM=$(basename $0)
brew.fzf $@
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment