sesh 0.0.8 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +47 -0
- data/LICENSE +675 -0
- data/README.md +17 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/exe/sesh +3 -0
- data/lib/sesh/cli.rb +244 -0
- data/lib/sesh/inferences.rb +36 -0
- data/lib/sesh/logger.rb +30 -0
- data/lib/sesh/ssh_control.rb +46 -0
- data/lib/sesh/tmux_control.rb +51 -0
- data/lib/sesh/version.rb +1 -1
- data/lib/sesh.rb +46 -0
- data/sesh.gemspec +32 -0
- metadata +103 -5
- data/bin/sesh +0 -254
metadata
CHANGED
@@ -1,15 +1,57 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sesh
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- MacKinley Smith
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: exe
|
10
10
|
cert_chain: []
|
11
11
|
date: 2015-03-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.10'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.10'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
13
55
|
- !ruby/object:Gem::Dependency
|
14
56
|
name: tmuxinator
|
15
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -24,6 +66,48 @@ dependencies:
|
|
24
66
|
- - "~>"
|
25
67
|
- !ruby/object:Gem::Version
|
26
68
|
version: 0.6.9
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: deep_merge
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: awesome_print
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: colorize
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
27
111
|
description: Remote background sessions powered by tmux and tmuxinator.
|
28
112
|
email: smithmackinley@gmail.com
|
29
113
|
executables:
|
@@ -31,9 +115,24 @@ executables:
|
|
31
115
|
extensions: []
|
32
116
|
extra_rdoc_files: []
|
33
117
|
files:
|
34
|
-
-
|
118
|
+
- ".gitignore"
|
119
|
+
- Gemfile
|
120
|
+
- Gemfile.lock
|
121
|
+
- LICENSE
|
122
|
+
- README.md
|
123
|
+
- Rakefile
|
124
|
+
- bin/console
|
125
|
+
- bin/setup
|
126
|
+
- exe/sesh
|
127
|
+
- lib/sesh.rb
|
35
128
|
- lib/sesh/assets/sample.yml
|
129
|
+
- lib/sesh/cli.rb
|
130
|
+
- lib/sesh/inferences.rb
|
131
|
+
- lib/sesh/logger.rb
|
132
|
+
- lib/sesh/ssh_control.rb
|
133
|
+
- lib/sesh/tmux_control.rb
|
36
134
|
- lib/sesh/version.rb
|
135
|
+
- sesh.gemspec
|
37
136
|
homepage: http://rubygems.org/gems/sesh
|
38
137
|
licenses:
|
39
138
|
- GNU GPLv3
|
@@ -54,9 +153,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
54
153
|
version: '0'
|
55
154
|
requirements: []
|
56
155
|
rubyforge_project:
|
57
|
-
rubygems_version: 2.
|
156
|
+
rubygems_version: 2.4.8
|
58
157
|
signing_key:
|
59
158
|
specification_version: 4
|
60
159
|
summary: Sesh
|
61
160
|
test_files: []
|
62
|
-
has_rdoc:
|
data/bin/sesh
DELETED
@@ -1,254 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
require 'tmuxinator'
|
3
|
-
|
4
|
-
if ARGV.empty? or ARGV.include? '-h' or ARGV.include? '--help' then puts <<-HELP
|
5
|
-
Sesh: remote background sessions powered by tmux and tmuxinator.
|
6
|
-
Runs a headless tmuxinator session for remote slave machines to connect to.
|
7
|
-
|
8
|
-
Usage: #{File.basename $0} command [project]
|
9
|
-
|
10
|
-
Commands:
|
11
|
-
|
12
|
-
sesh new Create a new tmuxinator configuration.
|
13
|
-
sesh edit [project] Edit an existing tmuxinator configuration.
|
14
|
-
sesh start [project] Start a Sesh session for a project.
|
15
|
-
sesh stop [project] Stop a Sesh session for a project.
|
16
|
-
sesh list List running Sesh sessions on this machine.
|
17
|
-
sesh enslave [project] user@host Connect a slave to a local Sesh session.
|
18
|
-
sesh connect [project] [user@host] Connect as a slave to a Sesh session.
|
19
|
-
|
20
|
-
Leave project blank to use the name of your current directory.
|
21
|
-
|
22
|
-
HELP
|
23
|
-
exit; end
|
24
|
-
|
25
|
-
def warn(msg) $stderr.puts msg end
|
26
|
-
def fatal(msg) $stderr.puts msg; $stderr.puts; exit 1 end
|
27
|
-
def debug(msg) $stderr.puts "> #{msg}" end
|
28
|
-
|
29
|
-
@command = ARGV[0]
|
30
|
-
@options = ARGV[1..-1] || []
|
31
|
-
# debug("Options received: #{@options}") if @options.any?
|
32
|
-
|
33
|
-
def get_local_username
|
34
|
-
`echo $USER`.strip
|
35
|
-
end
|
36
|
-
def get_local_hostname
|
37
|
-
`scutil --get LocalHostName`.strip.downcase + '.local'
|
38
|
-
end
|
39
|
-
@local_ssh_addr = "#{get_local_username}@#{get_local_hostname}"
|
40
|
-
|
41
|
-
def get_project_name
|
42
|
-
return if @command == 'list'
|
43
|
-
new_project = @command == 'new'
|
44
|
-
remote_project = @command == 'connect' && @options.select{|o| o.include? '@' }.any?
|
45
|
-
if @options.any? && !@options[0].include?('@')
|
46
|
-
return @options.shift if new_project || remote_project ||
|
47
|
-
Tmuxinator::Config.exists?(@options[0])
|
48
|
-
end
|
49
|
-
output = `printf '%q\n' "${PWD##*/}"`.strip
|
50
|
-
return output if new_project || Tmuxinator::Config.exists?(output)
|
51
|
-
puts "Sesh project '#{output}' could not be found."
|
52
|
-
fatal "Hint: run sesh new or specify an existing project after your commmand."
|
53
|
-
end
|
54
|
-
@project_name = get_project_name
|
55
|
-
|
56
|
-
|
57
|
-
@socket_file = "/tmp/#{@project_name}.sock"
|
58
|
-
@pids_file = "/tmp/#{@project_name}.pids.txt"
|
59
|
-
|
60
|
-
def get_remote_address
|
61
|
-
@options[@options.length>1 ? 1:0] if @options.any?
|
62
|
-
end
|
63
|
-
@remote_address = get_remote_address
|
64
|
-
|
65
|
-
def get_term_app
|
66
|
-
output = `osascript -e 'try' -e 'get exists application "iTerm"' -e 'end try'`.strip
|
67
|
-
output.length > 0 ? 'iTerm' : fatal("iTerm 2 is not installed.") # 'Terminal'
|
68
|
-
end
|
69
|
-
|
70
|
-
def get_editor
|
71
|
-
editor = `echo $EDITOR`.strip
|
72
|
-
editor = 'vim' unless editor.length > 0
|
73
|
-
editor
|
74
|
-
end
|
75
|
-
|
76
|
-
def project_name_matcher
|
77
|
-
pn = @project_name.gsub '-', '\-'
|
78
|
-
"[t]mux.*[#{pn[0]}]#{pn[1..-1]}"
|
79
|
-
end
|
80
|
-
|
81
|
-
def already_running?
|
82
|
-
output = `ps aux | grep "#{project_name_matcher}"`.strip
|
83
|
-
output.length > 0
|
84
|
-
end
|
85
|
-
|
86
|
-
def format_command(command) command.gsub(/\ [ ]+/, ' ').strip end
|
87
|
-
def format_and_run_command(command) `#{format_command(command)}`.strip end
|
88
|
-
|
89
|
-
def issue_server_start_command!
|
90
|
-
`tmux -S "#{@socket_file}" new-session -d "env TMUX='' mux start #{@project_name}"`
|
91
|
-
end
|
92
|
-
|
93
|
-
def issue_server_stop_command!
|
94
|
-
`pkill -f "#{project_name_matcher}"`
|
95
|
-
end
|
96
|
-
|
97
|
-
def connection_command(local=false, addr=nil)
|
98
|
-
addr ||= @local_ssh_addr
|
99
|
-
local_command = "tmux -S #{@socket_file} a"
|
100
|
-
return local_command if local
|
101
|
-
"ssh #{addr} -t '#{local_command}'"
|
102
|
-
end
|
103
|
-
|
104
|
-
def enter_slave_mode_command(local=false, addr=nil)
|
105
|
-
@term_app ||= get_term_app
|
106
|
-
case @term_app
|
107
|
-
when 'iTerm'
|
108
|
-
tell_term_app = 'tell application "' + @term_app + '"'
|
109
|
-
tell_term_process = 'tell application "System Events" to tell process "' +
|
110
|
-
@term_app + '"'
|
111
|
-
format_command <<-BASH
|
112
|
-
osascript \
|
113
|
-
-e '#{tell_term_app} to activate' \
|
114
|
-
-e '#{tell_term_process} to keystroke \"n\" using command down' \
|
115
|
-
-e 'delay 1' \
|
116
|
-
-e "#{tell_term_app.gsub('"', '\\"')} to tell session -1 of current \
|
117
|
-
terminal to write text \\"#{connection_command(local, addr)}\\"" \
|
118
|
-
-e '#{tell_term_process} to keystroke return using command down'
|
119
|
-
BASH
|
120
|
-
when 'Terminal'
|
121
|
-
format_command connection_command(local, addr)
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
def show_progress_until(condition_lambda, timeout=10)
|
126
|
-
started_progress_at = Time.now
|
127
|
-
return true if condition_lambda.call
|
128
|
-
print '> '
|
129
|
-
until condition_lambda.call or Time.now - started_progress_at > timeout
|
130
|
-
print '.'
|
131
|
-
$stdout.flush
|
132
|
-
sleep 0.5
|
133
|
-
end
|
134
|
-
puts
|
135
|
-
return condition_lambda.call
|
136
|
-
end
|
137
|
-
|
138
|
-
def kill_running_processes
|
139
|
-
if File.exists? @pids_file
|
140
|
-
File.readlines(@pids_file).each{|pid| kill_process! pid }
|
141
|
-
File.delete @pids_file
|
142
|
-
end
|
143
|
-
end
|
144
|
-
def kill_process!(pid)
|
145
|
-
`kill -9 #{pid}`
|
146
|
-
end
|
147
|
-
|
148
|
-
if @command
|
149
|
-
case @command
|
150
|
-
when 'start'
|
151
|
-
fatal("Sesh project '#{@project_name}' is already running!") if already_running?
|
152
|
-
debug "Starting Sesh project '#{@project_name}'..."
|
153
|
-
kill_running_processes
|
154
|
-
output = issue_server_start_command!
|
155
|
-
success = show_progress_until ->{ already_running? }
|
156
|
-
if success
|
157
|
-
sleep 1
|
158
|
-
if $? && already_running?
|
159
|
-
pids = `tmux list-panes -s -F "\#{pane_pid} \#{pane_current_command}" | grep -v tmux | awk '{print $1}'`.strip.lines
|
160
|
-
File.open(@pids_file, 'w') {|f| pids.each{|pid| f.puts pid } }
|
161
|
-
debug 'Sesh started successfully.'
|
162
|
-
debug "To connect: #{connection_command}"
|
163
|
-
# TODO: show other sesh commands (connect, enslave), specify full
|
164
|
-
# command as what is needed for machines without sesh.
|
165
|
-
puts
|
166
|
-
else
|
167
|
-
fatal 'Sesh failed to start!'
|
168
|
-
end
|
169
|
-
else
|
170
|
-
fatal 'Sesh failed to start after ten seconds!'
|
171
|
-
end
|
172
|
-
when 'stop'
|
173
|
-
fatal("Sesh project '#{@project_name}' is not running!") unless already_running?
|
174
|
-
debug "Stopping Sesh project '#{@project_name}'..."
|
175
|
-
kill_running_processes
|
176
|
-
output = issue_server_stop_command!
|
177
|
-
success = show_progress_until ->{ !already_running? }
|
178
|
-
if success && $?
|
179
|
-
debug 'Sesh stopped successfully.'
|
180
|
-
puts
|
181
|
-
else
|
182
|
-
fatal 'Sesh failed to stop after ten seconds!'
|
183
|
-
end
|
184
|
-
when 'restart'
|
185
|
-
fatal("Sesh project '#{@project_name}' is not running!") unless already_running?
|
186
|
-
puts `sesh stop #{@project_name} #{@options if @options.any?}`.strip
|
187
|
-
sleep(0.5)
|
188
|
-
puts `sesh start #{@project_name} #{@options if @options.any?}`.strip
|
189
|
-
when 'new'
|
190
|
-
name = @project_name
|
191
|
-
config = Tmuxinator::Config.project(name)
|
192
|
-
|
193
|
-
unless Tmuxinator::Config.exists?(name)
|
194
|
-
template = File.join(File.dirname(File.expand_path(__FILE__)), "../lib/sesh/assets/sample.yml")
|
195
|
-
erb = Erubis::Eruby.new(File.read(template)).result(binding)
|
196
|
-
File.open(config, "w") { |f| f.write(erb) }
|
197
|
-
end
|
198
|
-
|
199
|
-
Kernel.system("#{get_editor} #{config}") || Tmuxinator::Cli.new.doctor
|
200
|
-
puts
|
201
|
-
when 'edit'
|
202
|
-
config = Tmuxinator::Config.project(@project_name)
|
203
|
-
if Tmuxinator::Config.exists? @project_name
|
204
|
-
Kernel.system("#{get_editor} #{config}") || Tmuxinator::Cli.new.doctor
|
205
|
-
puts
|
206
|
-
else
|
207
|
-
fatal "Sesh project '#{@project_name}' does not exist yet!"
|
208
|
-
end
|
209
|
-
when 'list'
|
210
|
-
output = format_and_run_command <<-BASH
|
211
|
-
ps aux | grep tmux | grep "env TMUX='' mux start" \
|
212
|
-
| grep -v "[g]rep" | sed -e "s/.*mux start \\(.*\\)/\\1/"
|
213
|
-
BASH
|
214
|
-
running_projects = output.split("\n")
|
215
|
-
pcount = running_projects.count
|
216
|
-
if pcount > 0
|
217
|
-
puts "#{pcount} project#{pcount>1 ? 's':''} currently running:"
|
218
|
-
puts running_projects
|
219
|
-
puts
|
220
|
-
else
|
221
|
-
fatal "There are no Sesh projects currently running."
|
222
|
-
end
|
223
|
-
when 'connect'
|
224
|
-
addr = @remote_address
|
225
|
-
if (is_local=@remote_address.nil?)
|
226
|
-
addr = @local_ssh_addr
|
227
|
-
fatal("Sesh project '#{@project_name}' is not running!") unless already_running?
|
228
|
-
end
|
229
|
-
command = enter_slave_mode_command(is_local, addr)
|
230
|
-
puts `#{command}`.strip
|
231
|
-
when 'enslave'
|
232
|
-
fatal("Sesh project '#{@project_name}' is not running!") unless already_running?
|
233
|
-
fatal("You must specify a machine to enslave! Eg: user@ip") if @remote_address.nil?
|
234
|
-
debug "Attempting to connect #{@remote_address} to Sesh project '#{@project_name}'..."
|
235
|
-
# ssh #{@remote_address} "#{enter_slave_mode_command.gsub(/([^\\])"/, '\1\\"')}"
|
236
|
-
# puts <<-BASH
|
237
|
-
output = format_and_run_command <<-BASH
|
238
|
-
ssh #{@remote_address} "sesh connect #{@project_name} #{@local_ssh_addr}"
|
239
|
-
BASH
|
240
|
-
# 1>/dev/null 2>&1 &
|
241
|
-
puts output
|
242
|
-
if $?
|
243
|
-
debug "Sesh client connected successfully."
|
244
|
-
puts
|
245
|
-
else
|
246
|
-
fatal 'Sesh client failed to start.'
|
247
|
-
end
|
248
|
-
else
|
249
|
-
fatal "Command not recognized!"
|
250
|
-
end
|
251
|
-
exit 0
|
252
|
-
end
|
253
|
-
|
254
|
-
fatal 'You must specify a command.'
|