Class: Sesh::TmuxControl
- Inherits:
-
Object
- Object
- Sesh::TmuxControl
- Defined in:
- lib/sesh/tmux_control.rb
Class Method Summary collapse
Instance Method Summary collapse
- #already_running? ⇒ Boolean
- #begin_tmuxinator_session! ⇒ Object
- #connected_client_devices ⇒ Object
- #connected_clients ⇒ Object
- #connection_command ⇒ Object
- #disconnect_client!(identifier) ⇒ Object
- #disconnect_client_by_device!(devname) ⇒ Object
- #disconnect_client_by_index!(index) ⇒ Object
- #disconnect_client_by_ip!(ip) ⇒ Object
- #do_shell_operation!(options = ) ⇒ Object
- #do_shell_operation_here!(cmd, allow_zeus_retry = true) ⇒ Object
- #get_device_from_ip(ip) ⇒ Object
- #get_ip_from_device(devname) ⇒ Object
-
#initialize(project, options = {}) ⇒ TmuxControl
constructor
A new instance of TmuxControl.
- #interrupt_and_send_command_to_pane!(pane, command) ⇒ Object
- #interrupt_and_send_command_to_project!(command) ⇒ Object
- #interrupt_and_send_keys_to_pane!(pane, keys) ⇒ Object
- #interrupt_and_send_keys_to_project!(keys) ⇒ Object
-
#issue_start_command! ⇒ Object
pn = @project.gsub ‘-’, ‘-’ “[t]mux.*[#pn</a>]#1.1..-1” end.
- #issue_stop_command! ⇒ Object
-
#kill_process!(pid) ⇒ Object
if File.exists? @options File.readlines(@options).each{|pid| puts “Killing #{pid}” kill_process! pid } File.delete @options end.
- #kill_running_processes ⇒ Object
- #list_running_processes ⇒ Object
- #move_cursor_to_pane!(pane) ⇒ Object
- #move_cursor_to_pane_and_interrupt!(pane) ⇒ Object
- #obtain_child_pids_from_pid(pid) ⇒ Object
- #obtain_pids_from_session ⇒ Object
- #options ⇒ Object
-
#project ⇒ Object
Getter methods for passthru to SshControl class.
-
#project_name_matcher ⇒ Object
‘ps aux | grep “##project_name_matcher”`.strip.length > 0 end.
- #restart_project! ⇒ Object
- #send_command_to_pane!(pane, command) ⇒ Object
- #send_command_to_project!(command) ⇒ Object
- #send_interrupt! ⇒ Object
- #send_keys_to_pane!(pane, keys) ⇒ Object
- #send_keys_to_project!(keys) ⇒ Object
- #start_project! ⇒ Object
- #stop_project! ⇒ Object
Constructor Details
#initialize(project, options = {}) ⇒ TmuxControl
Returns a new instance of TmuxControl.
9 10 11 12 13 |
# File 'lib/sesh/tmux_control.rb', line 9 def initialize(project, ={}) @project = project || Inferences::infer_project_from_current_directory @options = {}.merge(DEFAULT_OPTIONS[:tmux]).merge @socket_file = @options[:socket_file] || "/tmp/#{@project}.sock" end |
Class Method Details
.get_running_projects ⇒ Object
15 16 17 18 19 20 21 |
# File 'lib/sesh/tmux_control.rb', line 15 def self.get_running_projects output = Sesh.format_and_run_command <<-BASH ps aux | grep tmux | grep "sesh begin" | grep -v "[g]rep" \ | sed -e "s/.*\\/tmp\\/\\(.*\\)\\.sock.*/\\1/" BASH output.lines.map(&:strip) end |
Instance Method Details
#already_running? ⇒ Boolean
22 |
# File 'lib/sesh/tmux_control.rb', line 22 def already_running?; self.class.get_running_projects.include? @project end |
#begin_tmuxinator_session! ⇒ Object
100 101 |
# File 'lib/sesh/tmux_control.rb', line 100 def begin_tmuxinator_session! %x[env TMUX='' mux start #{@project}] end |
#connected_client_devices ⇒ Object
128 129 130 |
# File 'lib/sesh/tmux_control.rb', line 128 def connected_client_devices `tmux -S "#{@socket_file}" list-clients 2>/dev/null | cut -d : -f 1 | cut -d / -f 3`.strip.lines.map(&:strip) end |
#connected_clients ⇒ Object
144 145 |
# File 'lib/sesh/tmux_control.rb', line 144 def connected_clients connected_client_devices.map{|devname| get_ip_from_device(devname) } end |
#connection_command ⇒ Object
48 |
# File 'lib/sesh/tmux_control.rb', line 48 def connection_command; "tmux -S #{@socket_file} a" end |
#disconnect_client!(identifier) ⇒ Object
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/sesh/tmux_control.rb', line 154 def disconnect_client!(identifier) identifier = '127.0.0.1' if identifier.nil? || identifier.length == 0 if identifier.to_i.to_s == identifier.to_s # It's an integer disconnect_client_by_index! identifier.to_i - 1 elsif identifier =~ /[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/ # It's an ip disconnect_client_by_ip! identifier elsif identifier =~ /.*@.*/ || identifier =~ /\.local$/ ping_output = `ping -a -c 1 #{identifier} 2>&1`.strip.lines if $? && ping_output.length > 1 && ping_output[1] =~ / from / resolved_ip = ping_output[1].split(' from ')[1].split(': ')[0] disconnect_client_by_ip! resolved_ip else puts "Ping output: #{ping_output}" end else ssh_identifier = `awk '/Host #{identifier}/ {getline; print $2}' ~/.ssh/config`.strip return disconnect_client!(ssh_identifier) if ssh_identifier.length > 0 fatal("Client") end end |
#disconnect_client_by_device!(devname) ⇒ Object
146 147 |
# File 'lib/sesh/tmux_control.rb', line 146 def disconnect_client_by_device!(devname) `tmux -S "#{@socket_file}" detach-client -t "/dev/#{devname}"`.strip.length == 0 end |
#disconnect_client_by_index!(index) ⇒ Object
152 153 |
# File 'lib/sesh/tmux_control.rb', line 152 def disconnect_client_by_index!(index) disconnect_client_by_device! connected_client_devices[index] end |
#disconnect_client_by_ip!(ip) ⇒ Object
148 149 150 151 |
# File 'lib/sesh/tmux_control.rb', line 148 def disconnect_client_by_ip!(ip) device = get_device_from_ip(ip) Logger.fatal("#{ip} is not connected to project \"#{@project}\".") if device.nil? disconnect_client_by_device! device end |
#do_shell_operation!(options = ) ⇒ Object
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'lib/sesh/tmux_control.rb', line 199 def do_shell_operation!(=DEFAULT_OPTIONS[:shell]) unless [:spec].nil? rspec_cmd = if [:rspec_prefix].nil? then 'rspec' else "#{[:rspec_prefix]} rspec" end [:command] ||= "#{rspec_cmd} #{[:spec]}" end if [:and_return] [:command] = "return_to_sesh; #{[:command]}" end inferred_location = Inferences.infer_tmux_location # puts "Inferred location: #{inferred_location}" if [:pane].nil? # puts inferred_location.inspect if inferred_location[:project] != @project interrupt_and_send_command_to_project! [:command] return 0 else do_shell_operation_here! [:command] end else if inferred_location != { project: @project, pane: [:pane] } interrupt_and_send_command_to_pane! [:pane], [:command] return 0 else do_shell_operation_here! [:command] end end end |
#do_shell_operation_here!(cmd, allow_zeus_retry = true) ⇒ Object
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 |
# File 'lib/sesh/tmux_control.rb', line 223 def do_shell_operation_here!(cmd, allow_zeus_retry=true) # system cmd raw = '' PTY.spawn(cmd) do |reader, writer| reader.sync = true writer.sync = true Thread.new { loop { writer.print(STDIN.getc.chr) } } while (c=reader.getc) raw << c print c end end if allow_zeus_retry && raw =~ /Could not find command "rspec / return do_shell_operation_here! cmd, false end $?.exitstatus end |
#get_device_from_ip(ip) ⇒ Object
136 137 138 139 140 141 142 143 |
# File 'lib/sesh/tmux_control.rb', line 136 def get_device_from_ip(ip) return if ( connected_devs = connected_client_devices ).length == 0 return connected_devs.find{|d| get_ip_from_device(ip) == ip } if ip == '127.0.0.1' who_lines = `who -a 2> /dev/null | grep "#{ Regexp.escape("(#{ip})") }"`.strip.lines puts connected_devs.inspect return if who_lines.length == 0 connected_devs.find{|d| who_lines.find{|l| l =~ / #{d} / } } end |
#get_ip_from_device(devname) ⇒ Object
131 132 133 134 135 |
# File 'lib/sesh/tmux_control.rb', line 131 def get_ip_from_device(devname) ip_line = `who -a 2> /dev/null | grep " #{devname} "`.strip return '127.0.0.1' unless ip_line.length > 0 && ip_line =~ /\)$/ ip_line.split('(')[-1][0..-2] end |
#interrupt_and_send_command_to_pane!(pane, command) ⇒ Object
194 195 |
# File 'lib/sesh/tmux_control.rb', line 194 def interrupt_and_send_command_to_pane!(pane, command) interrupt_and_send_keys_to_pane!(pane, "\"#{command}\" Enter") end |
#interrupt_and_send_command_to_project!(command) ⇒ Object
190 191 |
# File 'lib/sesh/tmux_control.rb', line 190 def interrupt_and_send_command_to_project!(command) send_interrupt!; send_command_to_project!(command) end |
#interrupt_and_send_keys_to_pane!(pane, keys) ⇒ Object
185 186 187 |
# File 'lib/sesh/tmux_control.rb', line 185 def interrupt_and_send_keys_to_pane!(pane, keys) move_cursor_to_pane_and_interrupt! pane send_keys_to_project! keys end |
#interrupt_and_send_keys_to_project!(keys) ⇒ Object
180 181 |
# File 'lib/sesh/tmux_control.rb', line 180 def interrupt_and_send_keys_to_project!(keys) send_interrupt!; send_keys_to_project! keys end |
#issue_start_command! ⇒ Object
pn = @project.gsub ‘-’, ‘-’ “[t]mux.*[#pn</a>]#Sesh::TmuxControl.1.1..-1” end
31 32 33 34 35 36 37 38 39 |
# File 'lib/sesh/tmux_control.rb', line 31 def issue_start_command! # Add bundle exec to the sesh begin command for dev purposes. cmd = Sesh.format_command <<-BASH tmux -S "#{@socket_file}" new-session -d "eval \\"\$SHELL -l -c 'rvm use default; sesh begin #{@project}'\\"" 2>&1 BASH output = `#{cmd}`.strip return true if $? && output.length == 0 Logger.warn "Tmux failed to start with the following error: #{output}"; false end |
#issue_stop_command! ⇒ Object
41 42 43 44 45 46 |
# File 'lib/sesh/tmux_control.rb', line 41 def issue_stop_command! # puts 'issuing stop command...' output = `ps -ef | grep "[t]mux -u attach-session -t #{Regexp.escape(@project)}\\$" | grep -v grep | awk '{print $2}' | xargs kill -9` puts "stop command issued! Output: #{output}" output end |
#kill_process!(pid) ⇒ Object
if File.exists? @options
File.readlines(@options[:pids_file]).each{|pid|
puts "Killing #{pid}"
kill_process! pid }
File.delete @options[:pids_file]
end
98 |
# File 'lib/sesh/tmux_control.rb', line 98 def kill_process!(pid); `kill -9 #{pid} 2>&1` end |
#kill_running_processes ⇒ Object
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/sesh/tmux_control.rb', line 82 def kill_running_processes pane_count = `tmux list-panes -s -F "\#{pane_pid} \#{pane_current_command}" -t "#{@project}" 2>/dev/null`.strip.lines.count return if pane_count == 0 pane_count.times{|i| move_cursor_to_pane_and_interrupt! i; sleep 0.1 } sleep 1; puts 'Killing pids from session...' obtain_pids_from_session.each{|pid| puts `ps aux | grep #{pid} | grep -v grep`.strip kill_process! pid } puts 'All pids from session have been killed.' # if File.exists? @options[:pids_file] # File.readlines(@options[:pids_file]).each{|pid| # puts "Killing #{pid}" # kill_process! pid } # File.delete @options[:pids_file] # end end |
#list_running_processes ⇒ Object
78 79 80 81 |
# File 'lib/sesh/tmux_control.rb', line 78 def list_running_processes obtain_pids_from_session.each{|pid| puts `ps aux | grep #{pid} | grep -v grep`.strip } end |
#move_cursor_to_pane!(pane) ⇒ Object
196 |
# File 'lib/sesh/tmux_control.rb', line 196 def move_cursor_to_pane!(pane); send_keys_to_project! "C-a q #{pane}" end |
#move_cursor_to_pane_and_interrupt!(pane) ⇒ Object
197 198 |
# File 'lib/sesh/tmux_control.rb', line 197 def move_cursor_to_pane_and_interrupt!(pane) move_cursor_to_pane!(pane); send_interrupt! end |
#obtain_child_pids_from_pid(pid) ⇒ Object
71 72 73 74 75 76 |
# File 'lib/sesh/tmux_control.rb', line 71 def obtain_child_pids_from_pid(pid) output = `ps -ef | grep #{pid} | grep -v grep | grep -v "sesh begin #{@project}" | awk '{print $2}'`.strip.lines.map(&:strip) output -= [pid] output += output.map{|cpid| obtain_child_pids_from_pid(cpid) - [pid] }.flatten output.reverse end |
#obtain_pids_from_session ⇒ Object
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/sesh/tmux_control.rb', line 50 def obtain_pids_from_session tmux_processes = `tmux list-panes -s -F "\#{pane_pid} \#{pane_current_command}" -t "#{@project}" 2> /dev/null | grep -v tmux | awk '{print $1}'`.strip.lines.map(&:strip) + `tmux -S "#{@socket_file}" list-panes -s -F "\#{pane_pid} \#{pane_current_command}" 2> /dev/null | grep -v tmux | awk '{print $1}'`.strip.lines.map(&:strip) tmux_processes.delete_if {|pid| `ps aux | grep #{pid} | grep -v grep | grep -v "sesh begin #{@project}"`.strip.length == 0 } puts; puts "Tmux Processes:" tmux_processes.each{|pid| puts `ps aux | grep #{pid} | grep -v grep`.strip } return [] unless tmux_processes.any? spring_processes = []; other_processes = [] spring_app_pid = `ps -ef | grep "[s]pring app .*| #{@project} |" | grep -v grep | awk '{print $2}'`.strip spring_processes += `ps -ef | grep #{spring_app_pid} | grep -v grep | grep -v "[s]pring app" | awk '{print $2}'`.strip.lines.map(&:strip) if spring_app_pid.length > 0 spring_processes += `ps -ef | grep "[s]pring.*| #{@project} |" | grep -v grep | awk '{print $2}'`.strip.lines.map(&:strip) puts; puts 'Spring Processes:' spring_processes.each{|pid| puts `ps aux | grep #{pid} | grep -v grep`.strip } tmux_processes.each{|pid| other_processes += obtain_child_pids_from_pid(pid) - tmux_processes } puts; puts 'Other Processes:' other_processes.each{|pid| puts `ps aux | grep #{pid} | grep -v grep`.strip } puts ( spring_processes + other_processes + tmux_processes ).uniq end |
#options ⇒ Object
243 |
# File 'lib/sesh/tmux_control.rb', line 243 def ; @options end |
#project ⇒ Object
Getter methods for passthru to SshControl class
242 |
# File 'lib/sesh/tmux_control.rb', line 242 def project; @project end |
#project_name_matcher ⇒ Object
‘ps aux | grep “##project_name_matcher”`.strip.length > 0 end
25 26 27 |
# File 'lib/sesh/tmux_control.rb', line 25 def project_name_matcher "[t]mux.*#{Regexp.escape(@project)}.*" end |
#restart_project! ⇒ Object
126 |
# File 'lib/sesh/tmux_control.rb', line 126 def restart_project!; stop_project!; sleep 0.5; start_project! end |
#send_command_to_pane!(pane, command) ⇒ Object
192 193 |
# File 'lib/sesh/tmux_control.rb', line 192 def send_command_to_pane!(pane, command) send_keys_to_pane! pane, "\"#{command}\" Enter" end |
#send_command_to_project!(command) ⇒ Object
188 189 |
# File 'lib/sesh/tmux_control.rb', line 188 def send_command_to_project!(command) send_keys_to_project! "\"#{command}\" Enter" end |
#send_interrupt! ⇒ Object
179 |
# File 'lib/sesh/tmux_control.rb', line 179 def send_interrupt!; send_keys_to_project! 'C-c' end |
#send_keys_to_pane!(pane, keys) ⇒ Object
182 183 184 |
# File 'lib/sesh/tmux_control.rb', line 182 def send_keys_to_pane!(pane, keys) move_cursor_to_pane! pane send_keys_to_project! keys end |
#send_keys_to_project!(keys) ⇒ Object
176 177 178 |
# File 'lib/sesh/tmux_control.rb', line 176 def send_keys_to_project!(keys) `tmux -S "#{@socket_file}" send-keys #{keys}`.strip.length == 0 end |
#start_project! ⇒ Object
103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/sesh/tmux_control.rb', line 103 def start_project! if already_running? Logger.fatal "Sesh project '#{@project}' is already running!" end Logger.info "Starting Sesh project '#{@project}'..." kill_running_processes if issue_start_command! && Logger.show_progress_until(-> { already_running? }) sleep 1 if already_running? Logger.success 'Sesh started successfully.' puts else Logger.fatal 'Sesh failed to start!' end else Logger.fatal 'Sesh failed to start after ten seconds!' end end |
#stop_project! ⇒ Object
116 117 118 119 120 121 122 123 124 125 |
# File 'lib/sesh/tmux_control.rb', line 116 def stop_project! unless already_running? Logger.fatal "Sesh project '#{@project}' is not running!" end Logger.info "Stopping Sesh project '#{@project}'..." kill_running_processes if issue_stop_command! && Logger.show_progress_until(-> { !already_running? }) Logger.success 'Sesh stopped successfully.' puts else Logger.fatal 'Sesh failed to stop after ten seconds!' end end |