Use gem install bunchcli
to install, then run bunch -h
for a list of commands.
Last active
November 28, 2021 07:37
-
-
Save ttscoff/07820820270759b5ce98b06521877a54 to your computer and use it in GitHub Desktop.
A CLI for Bunch.app (https://brettterpstra.com/projects/bunch/)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 |
This is outside the scope of what Bunch.app itself would be able to do,
but you could fairly easily write a CLI (or extend this one) to take
arguments, render a template (like `dev` or `jira`) with them, and then
pass the result directly to Bunch's [/raw url
handler](https://brettterpstra.com/projects/bunch/#raw) encoded in the
`txt=` parameter.
…-Brett
On 18 Jun 2020, at 2:40, Jonathan de Jong wrote:
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
- ***@***.***}
- [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
- ***@***.***}
- [${dm}]
```
--
You are receiving this because you authored the thread.
Reply to this email directly or view it on GitHub:
https://gist.github.com/07820820270759b5ce98b06521877a54#gistcomment-3345887
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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:
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:
I want to discuss something with a coworker in a project.
bunch slack --dm=erik
in bunch: