Skip to content

Instantly share code, notes, and snippets.

@ttscoff
Last active November 28, 2021 07:37
Show Gist options
  • Select an option

  • Save ttscoff/07820820270759b5ce98b06521877a54 to your computer and use it in GitHub Desktop.

Select an option

Save ttscoff/07820820270759b5ce98b06521877a54 to your computer and use it in GitHub Desktop.

This has been replaced by a gem

Use gem install bunchcli to install, then run bunch -h for a list of commands.

#!/usr/bin/env ruby
# frozen_string_literal: true
# A CLI for [Bunch.app](https://brettterpstra.com/projects/bunch/) by Brett Terpstra
# With tweaks from jlw (https://gist.github.com/jlw/28ab2591f8d14c7799a9e18580279f08#gistcomment-2924428)
CACHE_TIME = 86400 #seconds, 1 day = 86400
CACHE_FILE = "~/.bunch_cli_cache"
require 'optparse'
require 'yaml'
class Bunch
attr_writer :url_method
def initialize
@bunch_dir = nil
@url_method = nil
@bunches = nil
get_cache
end
def update_cache
@bunch_dir = nil
@url_method = nil
@bunches = nil
target = File.expand_path(CACHE_FILE)
settings = {
'bunchDir' => bunch_dir,
'method' => url_method,
'bunches' => bunches,
'updated' => Time.now.strftime('%s').to_i
}
File.open(target,'w') do |f|
f.puts YAML.dump(settings)
end
return settings
end
def get_cache
target = File.expand_path(CACHE_FILE)
if File.exists?(target)
settings = YAML.load(IO.read(target))
now = Time.now.strftime('%s').to_i
if now - settings['updated'].to_i > CACHE_TIME
settings = update_cache
end
else
settings = update_cache
end
@bunch_dir = settings['bunchDir'] || bunch_dir
@url_method = settings['method'] || url_method
@bunches = settings['bunches'] || generate_bunch_list
end
# items.push({title: 0})
def generate_bunch_list
items = []
Dir.glob(File.join(bunch_dir, '*.bunch')).each do |f|
items.push(
path: f,
title: File.basename(f, '.bunch')
)
end
items
end
def bunch_dir
@bunch_dir ||= begin
dir = `/usr/bin/defaults read #{ENV['HOME']}/Library/Preferences/com.brettterpstra.Bunch.plist configDir`.strip
File.expand_path(dir)
end
end
def url_method
@url_method ||= `/usr/bin/defaults read #{ENV['HOME']}/Library/Preferences/com.brettterpstra.Bunch.plist toggleBunches`.strip == '1' ? 'toggle' : 'open'
end
def bunches
@bunches ||= generate_bunch_list
end
def url(bunch)
if url_method == 'file'
%(x-bunch://raw?file=#{bunch})
else
%(x-bunch://#{url_method}?bunch=#{bunch[:title]})
end
end
def bunch_list
list = []
bunches.each { |bunch| list.push(bunch[:title]) }
list
end
def list_bunches
$stdout.puts bunch_list.join("\n")
end
def find_bunch(str)
found_bunch = false
bunches.each do |bunch|
if bunch[:title].downcase =~ /.*?#{str}.*?/i
found_bunch = bunch
break
end
end
found_bunch
end
def human_action
(url_method.gsub(/e$/, '') + 'ing').capitalize
end
def open(str)
# get front app
front_app = %x{osascript -e 'tell application "System Events" to return name of first application process whose frontmost is true'}.strip
bunch = find_bunch(str)
unless bunch
if File.exists?(str)
@url_method = 'file'
warn "Opening file"
`open '#{url(str)}'`
else
warn 'No matching Bunch found'
Process.exit 1
end
else
warn "#{human_action} #{bunch[:title]}"
`open "#{url(bunch)}"`
end
# attempt to restore front app
%x{osascript -e 'delay 2' -e 'tell application "#{front_app}" to activate'}
end
def show(str)
bunch = find_bunch(str)
output = `cat "#{bunch[:path]}"`.strip
puts output
end
def show_config
puts "Bunches Folder: #{bunch_dir}"
puts "Default URL Method: #{url_method}"
puts "Cached Bunches"
bunches.each {|b|
puts " - #{b[:title]}"
}
end
end
def help
puts "\nUsage: #{File.basename(__FILE__)} [options] BUNCH_NAME|PATH_TO_FILE"
puts "\nBunch names are case insensitive and will execute first match"
end
bunch = Bunch.new
optparse = OptionParser.new do |opts|
opts.banner = 'CLI for Bunch.app'
opts.on('-h', '--help', 'Display this screen') do |_opt|
puts opts
help
Process.exit 0
end
opts.on('-f', '--force-refresh', 'Force refresh cached preferences') do |opt|
bunch.update_cache
end
opts.on('-l', '--list', 'List available Bunches') do |_opt|
bunch.list_bunches
Process.exit 0
end
opts.on('-o', '--open', 'Open Bunch ignoring "Toggle Bunches" preference') do |_opt|
bunch.url_method = 'open'
end
opts.on('-c', '--close', 'Close Bunch ignoring "Toggle Bunches" preference') do |_opt|
bunch.url_method = 'close'
end
opts.on('-t', '--toggle', 'Toggle Bunch ignoring "Toggle Bunches" preference') do |_opt|
bunch.url_method = 'toggle'
end
opts.on('-s', '--show BUNCH', 'Show contents of Bunch') do |opt|
bunch.show(opt)
Process.exit 0
end
opts.on('--show-config', 'Display configuration values') do |opt|
bunch.show_config
Process.exit 0
end
end
optparse.parse!
unless ARGV.length > 0
puts "CLI for Bunches.app"
help
else
ARGV.map { |arg| bunch.open(arg) }
end
@njm2112
Copy link
Copy Markdown

njm2112 commented Mar 24, 2020

what is the proper ownership for the script once accessible through $PATH?

bunch -h is throwing a permission denied error for me.

@ttscoff
Copy link
Copy Markdown
Author

ttscoff commented Mar 24, 2020 via email

@jonathan-dejong
Copy link
Copy Markdown

Pretty excited to see what I can automate with this 👍
Something that came to mind: would it be possible to pass/pipe an argument or more to the bunch through the CLI?
Without any real knowledge I figure it could be something like the ${KEY} syntax?
A couple of example use cases :

I want to startup dev env for a specific project. By passing the projects name I could fire up iTerm and VSC on that specific project, start NPM, close down any other running node etc. etc.

bunch dev --f=<clientfolder>

in bunch:

%iTerm
- {@n}
- [cd path/to/dev/${f}]
- [composer install]
- [npm install && npm start]
- [vsc]

I want to check the JIRA board of a specific client. I could pass the projects JIRA tag (2-5 letter shorthand) and bring up my browser to that address. Like https://jirainstance.com/projects/<shorthand>/issues/

bunch jira --p=<shorthand>

in bunch:

https://jirainstance.com/projects/${p}/issues/

I want to discuss something with a coworker in a project.

bunch slack --dm=erik

in bunch:

%Slack
- {@k}
- [${dm}]

@ttscoff
Copy link
Copy Markdown
Author

ttscoff commented Jun 19, 2020 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment