-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathbart
More file actions
executable file
·239 lines (200 loc) · 7.37 KB
/
bart
File metadata and controls
executable file
·239 lines (200 loc) · 7.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
#!/usr/bin/env ruby
# THIS DOES NOT WORK RIGHT, AND POSSIBLY MAY NEVER WORK RIGHT. RETHINK.
# tl;dr two problems:
#
# 1. the system calls appear to mangling spaces somehow, which might be debuggable
#
# 2. this program wraps the bash session in a ruby interpreter. The ruby runtime
# "only" takes up about 12MB of RAM but if I'm going to fire and forget a
# long-running service like this I really don't want anything wrapped around
# it except bash. TODO: Wade through
# https://devver.wordpress.com/2009/06/30/a-dozen-or-so-ways-to-start-sub-processes-in-ruby-part-1/
# and see if there isn't a spawn-child-and-die mechanism. I seem to recall
# investigating this very thing a decade or more ago, and coming up
# frustrated, but I can't remember what exactly my findings were: was it
# impossible or did I just discover that orphaning a child process was the
# wrong approach?
# bart [options] - "bin start" - start up the current application. Basically a
# poor-man's Foreman gem, but requiring no in-repo setup (e.g. Procfile).
# bart is my attempt at a solution to the problem of "I have 20 apps I have to
# run in 14 different folders". Most apps just need "bundle exec rails s" (each
# on a different port) while others need rails, sidekiq, and tiki.
# You tell bart to start up a specific app by name; if omitted, assumes
# "default". Bart uses your current folder to determine which command to run. If
# it can't find one, it looks in a folder called "global" for the command to
# run. So, to illustrate: Since sidekiq mostly doesn't need a specific port,
# that can be the "global default" as well as the "global queue" command, then
# each folder can have their rails app be the "default". For an app that needs
# rails, sidekiq and tiki we can open three terminals (or tmux windows) and run
# "bart", "bart queue" and "bart tiki". The bare bart will check for a local
# default command, which for this app turns out to be the rails startup command.
# For an app that has no rails app, but does have a sidekiq queue, we can just
# run "bart" and it will check for a local default, find none, then check the
# global default and see "bundle exec sidekiq" and we're off to the races
# asynchronously.
# SO!
# bart
# bart <name>
# bart -l[ist] [-g] - list available local or global commands
# bart -s[ave] [-g] <name> <cmd>
# bart -e[dit] [-g] [<name>]
# bart -d[elete] [-g] <name>
# bart -s[how] [-g] [<name>] - show command/default
require 'optimist'
require 'yaml'
class Bart
attr_reader :commands
def initialize
@commands = load_commands
end
def run(command_name=nil, global: false)
section_name = global ? "global": pwd
command_name ||= "default"
command = find_command(section_name, command_name)
if command.nil? && section_name != "global"
command = find_command("global", command_name)
end
if command
puts command
system command
else
if section_name != "global"
section_name += " or global"
end
puts red("No command '#{command_name}' found in section #{section_name}")
end
end
def list(global: false)
section_name = global ? "global": pwd
show_list(section_name, commands[section_name])
end
def save(command_name, command=nil, global: false)
if command.nil?
command = command_name
command_name = "default"
end
section_name = global ? "global": pwd
puts "Saving '#{command_name}' as '#{command}' in #{section_name}"
section = find_or_create_section(section_name)
section[command_name] = command
save_commands
end
def edit
edit_command = ENV["EDITOR"] + " " + command_file
puts edit_command
system edit_command
end
def delete(command_name, global: false)
section_name = global ? "global": pwd
section = find_section!(section_name)
section.delete(command_name)
save_commands
end
def pretend(command_name=nil, global: false)
section_name = global ? "global": pwd
command_name ||= "default"
if section_name != "global"
lookup_command section_name, command_name
end
section_name = "global"
lookup_command section_name, command_name
end
private
def red(string)
"\033[31m#{string}\033[0m"
end
def green(string)
"\033[32m#{string}\033[0m"
end
def lookup_command(section_name, command_name)
command = find_command(section_name, command_name)
if command
puts green("command '#{command_name}' found in #{section_name}: ")
puts command
else
puts red("No command '#{command_name}' found in section #{section_name}")
section_name = "global"
end
end
def show_list(location, section)
puts "Commands available in #{location}:"
name_padding = section.keys.map(&:size).max + 2
section.each_pair do |name, command|
puts (name + ":").ljust(name_padding) + command
end
end
def pwd
Dir.pwd
end
def find_section!(section_name)
commands.fetch(section_name) do
raise "No commands available for #{section_name}"
end
end
def find_command!(section_name, command_name)
find_section!(section_name).fetch(command_name) do
raise "No command '#{command_name}' found in section #{section_name}"
end
end
def find_or_create_section(section_name)
commands[section_name] ||= {}
end
def find_command(section_name, command_name)
return nil unless commands.has_key?(section_name)
return nil unless commands[section_name].has_key?(command_name)
commands[section_name][command_name]
end
def command_file
File.expand_path("~/.bart.yml")
end
def load_commands
YAML.load_file(command_file)
end
def save_commands
File.open(command_file, "w") do |file|
file.puts commands.to_yaml
end
end
end
if __FILE__==$0
opts = Optimist.options do
banner <<~EOS
bart [<name>] - run [default] command
EOS
# bart [-g] [command] [<name>]
# bart -l[ist] [-g] - list available local or global commands
# bart -s[ave] [-g] <name> <cmd>
# bart -d[elete] [-g] <name>
# bart -e[dit]
# bart -p[retend] [-g] [<name>] - pretend run/show command/default
opt :list, "List commands for section", type: :boolean, default: false
opt :save, "Save commands for section", type: :boolean, default: false
opt :delete, "List commands for section", type: :boolean, default: false
opt :edit, "Edit commands file list", type: :boolean, default: false
opt :pretend, "Show which command would be run for section", type: :boolean, default: false
opt :global, "Force global section instead of local", type: :boolean, default: false
end
num_commands = [opts[:list], opts[:save], opts[:delete], opts[:edit], opts[:pretend]].map {|v| v ? 1 : 0 }.sum
Optimist::die "Too many commands given, choose one of -l, -s, -d, -e or -p" if num_commands > 1
Optimist::die "Delete requires command name" if opts[:delete] && (ARGV.size != 1)
Optimist::die "Save requires command or name and command" if opts[:save] && (!(1..2).cover?(ARGV.size))
# puts '-' * 80
# puts opts.inspect
# puts '--'
# puts ARGV.inspect
# puts '-' * 80
bart = Bart.new
if opts[:list]
bart.list global: opts[:global]
elsif opts[:save]
bart.save *ARGV, global: opts[:global]
elsif opts[:delete]
bart.delete *ARGV, global: opts[:global]
elsif opts[:edit]
bart.edit
elsif opts[:pretend]
bart.pretend *ARGV, global: opts[:global]
else
bart.run *ARGV, global: opts[:global]
end
end