tweemux 0.0.1
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.
- data/.gemspec +16 -0
- data/.gitignore +2 -0
- data/Gemfile +4 -0
- data/Guardfile +1 -0
- data/LICENSE.txt +1 -0
- data/README.rdoc +49 -0
- data/Rakefile +1 -0
- data/bin/tweemux +3 -0
- data/lib/tweemux.rb +21 -0
- data/lib/tweemux/action.rb +87 -0
- data/lib/tweemux/action/at.rb +8 -0
- data/lib/tweemux/action/bro.rb +3 -0
- data/lib/tweemux/action/forward.rb +33 -0
- data/lib/tweemux/action/host.rb +7 -0
- data/lib/tweemux/action/share.rb +7 -0
- data/lib/tweemux/action/sis.rb +2 -0
- data/lib/tweemux/core_ext.rb +30 -0
- data/lib/tweemux/version.rb +3 -0
- data/test/action_test_helper.rb +25 -0
- data/test/test_helper.rb +9 -0
- data/test/tweemux/action/at_test.rb +16 -0
- data/test/tweemux/action/forward_test.rb +17 -0
- data/test/tweemux/action/host_test.rb +11 -0
- data/test/tweemux/action/share_test.rb +11 -0
- data/test/tweemux/action_test.rb +10 -0
- data/test/tweemux_test.rb +27 -0
- metadata +88 -0
data/.gemspec
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'working/gemspec'
|
3
|
+
$:.unshift './lib'
|
4
|
+
require 'tweemux/version'
|
5
|
+
|
6
|
+
Working.gemspec(
|
7
|
+
:name => 'tweemux',
|
8
|
+
:summary => Working.third_line_of_readme,
|
9
|
+
#:description => Working.readme_snippet(/== Usage/, /== TODO/),
|
10
|
+
:description => Working.third_line_of_readme,
|
11
|
+
:version => Tweemux::VERSION,
|
12
|
+
:authors => %w(☈king),
|
13
|
+
:email => 'rking-tweemux@sharpsaw.org',
|
14
|
+
:github => 'rking/tweemux',
|
15
|
+
:deps => %w(),
|
16
|
+
)
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'working/guard'
|
data/LICENSE.txt
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Copyleft (ↄ) 2013 ☈king — CC0/Public Domain.
|
data/README.rdoc
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
= tweemux
|
2
|
+
|
3
|
+
For Remote Pair Programming: A handy script to create-or-join a world-readable tmux session (smaller than wemux)
|
4
|
+
|
5
|
+
One of the aims of this script is to be convenient. Another of its aims is to be transparent (read: loud) about what it's doing. None of the parts of this are inherently complicated, and any of them is prone to need additional debugging, so it's best that `tweemux`'s behavior is visible.
|
6
|
+
|
7
|
+
== Problem
|
8
|
+
|
9
|
+
Though tmux is an amazing tool, some of the parts of it are low-level. As you can see from the tmux portion of this script's source, there isn't much you have to do to get a shared session, but it's still more than you want you or your pair to think about when you're trying to work. The `wemux` script is similar to this, but I don't care for its complexity/optionality/config (and the thing that really bothers me is that it has a 'read-only' mode that only is read-only because of the command used to connect to the socket — the socket itself is still `chmod 777`, so you might as well be honest about the idea that the session could be connected by any user in writeable mode).
|
10
|
+
|
11
|
+
Once you've solved the "shared tmux" problem, that's actually the easier part of it. This script goes a little further and helps with the user and SSH connection problems.
|
12
|
+
|
13
|
+
== Host Usage
|
14
|
+
|
15
|
+
For starters:
|
16
|
+
|
17
|
+
gem install tweemux
|
18
|
+
|
19
|
+
Then, create the user on your machine:
|
20
|
+
|
21
|
+
tweemux bro lwoodson # when their Github username == desired Unix username
|
22
|
+
# -or-
|
23
|
+
tweemux bro cirwin github: ConradIrwin
|
24
|
+
# -or-
|
25
|
+
tweemux sis ghopper # synonym for 'bro'
|
26
|
+
|
27
|
+
If you're on a machine behind a firewall, use one that is *not* behind a firewall that you also have SSH access to (in this example, sharpsaw.org is the one not behind the firewall):
|
28
|
+
|
29
|
+
tweemux forward local 22 from sharpsaw.org 3322
|
30
|
+
# ^ Or, if you're in control of the router, you can just open a port and
|
31
|
+
# point your pair at your actual IP (`curl ifconfig.me` comes in handy for
|
32
|
+
# finding the public IP)
|
33
|
+
|
34
|
+
Then finally:
|
35
|
+
|
36
|
+
tweemux host
|
37
|
+
|
38
|
+
== Guest Usage
|
39
|
+
|
40
|
+
gem install tweemux
|
41
|
+
tweemux at sharpsaw.org 3322 # uses the 'forward' set up from above
|
42
|
+
|
43
|
+
== Going Further
|
44
|
+
|
45
|
+
It's also nice to share a windowing environment session as well. For example, the "Guest" can host a VNC that you, as the tweemux host, can then connect to. This allows you to "point" at things with the mouse, and to share web browsing, etc.
|
46
|
+
|
47
|
+
== No Public Box?
|
48
|
+
|
49
|
+
If you don't have a public SSH account (like the way I use sharpsaw.org, above), let me know ( i-am-stuck@sharpsaw.org ). We'll solve that.
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'working/rake_tasks'
|
data/bin/tweemux
ADDED
data/lib/tweemux.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'tweemux/action'
|
2
|
+
require 'tweemux/core_ext'
|
3
|
+
|
4
|
+
class Tweemux
|
5
|
+
SOCK = '/tmp/tweemux.sock'
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def run args
|
9
|
+
action = understand args
|
10
|
+
action.call
|
11
|
+
end
|
12
|
+
|
13
|
+
def understand args
|
14
|
+
action = args.shift
|
15
|
+
klass = Tweemux::Action.const_get action.capitalize
|
16
|
+
klass.new args
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Tweemux
|
4
|
+
class Action
|
5
|
+
def initialize args; @args = args end
|
6
|
+
|
7
|
+
def call
|
8
|
+
run @args
|
9
|
+
end
|
10
|
+
|
11
|
+
class FunkyUsage < RuntimeError; end
|
12
|
+
|
13
|
+
def run args; raise 'Unimplemented' end
|
14
|
+
|
15
|
+
# Hrm. This is kinda gross, but I feel like it cleans up the subclasses
|
16
|
+
def explained_run *a; self.class.explained_run *a end
|
17
|
+
def tmux_S *a; self.class.tmux_S *a end
|
18
|
+
|
19
|
+
class DubiousSystemInvocation < RuntimeError; end
|
20
|
+
class << self
|
21
|
+
def tmux_S args, why
|
22
|
+
cmd = %W(tmux -S #{SOCK}) + args
|
23
|
+
explained_run cmd, why
|
24
|
+
end
|
25
|
+
|
26
|
+
def explained_run what, why
|
27
|
+
raise DubiousSystemInvocation, "Given string-arg of #{what}" \
|
28
|
+
if what.is_a? String
|
29
|
+
explain what, why
|
30
|
+
system_or_raise what
|
31
|
+
end
|
32
|
+
|
33
|
+
def explain what, why
|
34
|
+
warn highlight_command(what) + highlight_explanation(why)
|
35
|
+
end
|
36
|
+
|
37
|
+
def system_or_raise cmd
|
38
|
+
system *cmd or pseudo_restarts(*cmd)
|
39
|
+
end
|
40
|
+
|
41
|
+
def highlight_command arr
|
42
|
+
':Running'.color(:middle_blue) \
|
43
|
+
+ '; '.color(:gray245) \
|
44
|
+
+ colorize_tmux_command(arr)
|
45
|
+
end
|
46
|
+
|
47
|
+
def highlight_explanation msg
|
48
|
+
' # '.color(:orange) + msg.color(:lemon)
|
49
|
+
end
|
50
|
+
|
51
|
+
def colorize_tmux_command arr
|
52
|
+
# Index access in Ruby is a smell. @ConradIrwin, help me!!
|
53
|
+
socket_idx = arr.find_index '-S'
|
54
|
+
arr.inject [] do |a,e|
|
55
|
+
a <<
|
56
|
+
if socket_idx and (e == arr[socket_idx] or e == arr[socket_idx+1])
|
57
|
+
e.color :gray245
|
58
|
+
else
|
59
|
+
e.color :brighty_blue
|
60
|
+
end
|
61
|
+
end.join ' '
|
62
|
+
end
|
63
|
+
|
64
|
+
def pseudo_restarts cmd
|
65
|
+
warn '# failed ☹'.color :error
|
66
|
+
ctrl_c = 'Ctrl+c'.color :keypress, :prompt
|
67
|
+
enter = 'Enter'.color :keypress, :prompt
|
68
|
+
# TODO: work pry-rescue into this so we can offer a 'try-again'
|
69
|
+
# See also: https://github.com/ConradIrwin/pry-rescue/issues/29
|
70
|
+
warn <<-EOT.color :prompt
|
71
|
+
To give up, hit: #{ctrl_c}
|
72
|
+
To run anyway, hit: #{enter}
|
73
|
+
EOT
|
74
|
+
$stdin.readline
|
75
|
+
end
|
76
|
+
|
77
|
+
def load_all!
|
78
|
+
dir = __FILE__.sub /\.rb$/, ''
|
79
|
+
Dir[dir + '/*.rb'].each do |e|
|
80
|
+
require e.sub /.*(tweemux\/.+)\.rb$/, '\1'
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
Tweemux::Action.load_all!
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class Tweemux::Action::Forward < Tweemux::Action
|
2
|
+
def run args
|
3
|
+
usage_fail 'wrong number of thingies' unless 5 == args.size
|
4
|
+
host, hostport, from_, remote_host, port = args
|
5
|
+
usage_fail 'third thingy has to be numeric port' unless is_int? hostport
|
6
|
+
usage_fail 'fourth thingy has to be "from"' unless 'from' == from_
|
7
|
+
usage_fail 'fith thingy has to be numeric port' unless is_int? port
|
8
|
+
host = 'localhost' if 'local' == host
|
9
|
+
explained_run %W(ssh -fNR #{port}:#{host}:#{hostport} #{remote_host}),
|
10
|
+
'-f = background, -N = no remote command, -R = remote forward'
|
11
|
+
end
|
12
|
+
|
13
|
+
def usage_fail what_happened
|
14
|
+
raise FunkyUsage,
|
15
|
+
(what_happened+". Expected usage:\n ").color(:error_explanation) +
|
16
|
+
'tweemux '.color(:keyword) + colored_sample_command
|
17
|
+
end
|
18
|
+
|
19
|
+
def colored_sample_command
|
20
|
+
[
|
21
|
+
'forward'.color(:keyword),
|
22
|
+
'local'.color(:host),
|
23
|
+
'22'.color(:number),
|
24
|
+
'from'.color(:keyword),
|
25
|
+
'sharpsaw.org'.color(:host),
|
26
|
+
'3322'.color(:number),
|
27
|
+
].join ' '
|
28
|
+
end
|
29
|
+
|
30
|
+
def is_int? str
|
31
|
+
str.to_i.to_s == str
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class String
|
2
|
+
# Pallete viewable from:
|
3
|
+
# https://github.com/sharpsaw/tmux-dots/blob/master/bin/colortest
|
4
|
+
COLORS = {
|
5
|
+
:middle_blue => 69,
|
6
|
+
:brighty_blue => 39,
|
7
|
+
:gray245 => 245,
|
8
|
+
:orange => 172,
|
9
|
+
:pale_yellow => 180,
|
10
|
+
:lemon => 228,
|
11
|
+
|
12
|
+
:keypress => 157,
|
13
|
+
:prompt => 35,
|
14
|
+
:number => 48,
|
15
|
+
:keyword => 60,
|
16
|
+
:host => 148,
|
17
|
+
|
18
|
+
:error => 160,
|
19
|
+
:error_explanation => 178,
|
20
|
+
:default => 7
|
21
|
+
}
|
22
|
+
def color this_color, end_on = :default
|
23
|
+
[this_color, end_on].each{|e| fail "No color named #{e}" unless COLORS[e]}
|
24
|
+
if $stderr.tty?
|
25
|
+
"\e[38;5;#{COLORS[this_color]}m#{self}\e[38;5;#{COLORS[end_on]}m"
|
26
|
+
else
|
27
|
+
self
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
module TweemuxActionHelper
|
4
|
+
def test_run
|
5
|
+
explained_runs = []
|
6
|
+
Tweemux::Action.stub :explained_run, -> *a { explained_runs << a } do
|
7
|
+
Tweemux.run argv
|
8
|
+
end
|
9
|
+
got = explained_runs.map &:first
|
10
|
+
assert_equal got.to_yaml, expected_commands.to_yaml
|
11
|
+
end
|
12
|
+
|
13
|
+
def bad_runs; [] end
|
14
|
+
def test_bad_runs
|
15
|
+
bad_runs.each do |illegit|
|
16
|
+
begin
|
17
|
+
Tweemux.run illegit
|
18
|
+
rescue Tweemux::Action::FunkyUsage
|
19
|
+
else
|
20
|
+
fail "#{illegit} didn't raise expected error"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require_relative '../../action_test_helper'
|
2
|
+
class Tweemux::Action::AtTest < MiniTest::Unit::TestCase
|
3
|
+
include TweemuxActionHelper
|
4
|
+
def argv; %w'at sharpsaw.org 2233' end
|
5
|
+
def expected_commands
|
6
|
+
[ %w(ssh sharpsaw.org -p2233 -t tmux -S /tmp/tweemux.sock attach) ]
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class Tweemux::Action::AtImplicitPortTest < MiniTest::Unit::TestCase
|
11
|
+
include TweemuxActionHelper
|
12
|
+
def argv; %w'at sharpsaw.org' end
|
13
|
+
def expected_commands
|
14
|
+
[ %w(ssh sharpsaw.org -p22 -t tmux -S /tmp/tweemux.sock attach) ]
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require_relative '../../action_test_helper'
|
2
|
+
class Tweemux::Action::ForwardTest < MiniTest::Unit::TestCase
|
3
|
+
include TweemuxActionHelper
|
4
|
+
def argv; %w'forward local 22 from sharpsaw.org 3322' end
|
5
|
+
def expected_commands
|
6
|
+
[ %w(ssh -fNR 3322:localhost:22 sharpsaw.org) ]
|
7
|
+
end
|
8
|
+
|
9
|
+
def bad_runs
|
10
|
+
[
|
11
|
+
%w(forward asdf asdf),
|
12
|
+
%w(forward local hi from sharpsaw.org 3322),
|
13
|
+
%w(forward local 22 from sharpsaw.org hi),
|
14
|
+
%w(forward local 22 zipzip sharpsaw.org 3322),
|
15
|
+
]
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require_relative '../../action_test_helper'
|
2
|
+
class Tweemux::Action::HostTest < MiniTest::Unit::TestCase
|
3
|
+
include TweemuxActionHelper
|
4
|
+
def argv; %w'host' end
|
5
|
+
def expected_commands
|
6
|
+
[
|
7
|
+
%w(tmux -S /tmp/tweemux.sock start-server),
|
8
|
+
%w(tmux -S /tmp/tweemux.sock new) + ['tweemux share'],
|
9
|
+
]
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require_relative '../../action_test_helper'
|
2
|
+
class Tweemux::Action::ShareTest < MiniTest::Unit::TestCase
|
3
|
+
include TweemuxActionHelper
|
4
|
+
def argv; %w'share' end
|
5
|
+
def expected_commands
|
6
|
+
[
|
7
|
+
%w(chmod a+rw /tmp/tweemux.sock),
|
8
|
+
[ENV['SHELL']],
|
9
|
+
]
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require_relative '../test_helper'
|
2
|
+
|
3
|
+
class Tweemux::ActionTest < MiniTest::Unit::TestCase
|
4
|
+
def test_explained_run_strictness
|
5
|
+
Tweemux::Action.explained_run 'echo > /tmp/hi', 'not shell-interpreted'
|
6
|
+
rescue Tweemux::Action::DubiousSystemInvocation
|
7
|
+
else
|
8
|
+
fail 'Should have refused to run a sketchy shell command.'
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
|
3
|
+
class TweemuxTest < MiniTest::Unit::TestCase
|
4
|
+
def test_doc_claims
|
5
|
+
claimed_working = File.readlines('README.rdoc').grep /^ {4}tweemux/
|
6
|
+
claimed_working.each do |line|
|
7
|
+
line.sub! /#.*/, ''
|
8
|
+
fake_argv = line.sub(/^\s*tweemux/, '').split.map{|e| e.strip}
|
9
|
+
action = Tweemux.understand fake_argv
|
10
|
+
got_run = false
|
11
|
+
action.stub :run, -> *a { got_run = true } do
|
12
|
+
action.call
|
13
|
+
end
|
14
|
+
assert got_run, "running action for #{line}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_ruby18
|
19
|
+
# TODO: optimize this by running them all in one command
|
20
|
+
Dir['{bin,lib}/*.rb'].each do |e|
|
21
|
+
fail "ruby18 hates #{e}" unless "Syntax OK\n" == `ruby18 -c #{e}`
|
22
|
+
end
|
23
|
+
rescue Errno::ENOENT => e
|
24
|
+
skip 'Needs ruby18 executable in $PATH' if e.message[/ruby18/]
|
25
|
+
raise e
|
26
|
+
end
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tweemux
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- ☈king
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-02-25 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: ! 'For Remote Pair Programming: A handy script to create-or-join a world-readable
|
15
|
+
tmux session (smaller than wemux)
|
16
|
+
|
17
|
+
|
18
|
+
For Remote Pair Programming: A handy script to create-or-join a world-readable tmux
|
19
|
+
session (smaller than wemux)
|
20
|
+
|
21
|
+
'
|
22
|
+
email: rking-tweemux@sharpsaw.org
|
23
|
+
executables:
|
24
|
+
- tweemux
|
25
|
+
extensions: []
|
26
|
+
extra_rdoc_files: []
|
27
|
+
files:
|
28
|
+
- .gemspec
|
29
|
+
- .gitignore
|
30
|
+
- Gemfile
|
31
|
+
- Guardfile
|
32
|
+
- LICENSE.txt
|
33
|
+
- README.rdoc
|
34
|
+
- Rakefile
|
35
|
+
- bin/tweemux
|
36
|
+
- lib/tweemux.rb
|
37
|
+
- lib/tweemux/action.rb
|
38
|
+
- lib/tweemux/action/at.rb
|
39
|
+
- lib/tweemux/action/bro.rb
|
40
|
+
- lib/tweemux/action/forward.rb
|
41
|
+
- lib/tweemux/action/host.rb
|
42
|
+
- lib/tweemux/action/share.rb
|
43
|
+
- lib/tweemux/action/sis.rb
|
44
|
+
- lib/tweemux/core_ext.rb
|
45
|
+
- lib/tweemux/version.rb
|
46
|
+
- test/action_test_helper.rb
|
47
|
+
- test/test_helper.rb
|
48
|
+
- test/tweemux/action/at_test.rb
|
49
|
+
- test/tweemux/action/forward_test.rb
|
50
|
+
- test/tweemux/action/host_test.rb
|
51
|
+
- test/tweemux/action/share_test.rb
|
52
|
+
- test/tweemux/action_test.rb
|
53
|
+
- test/tweemux_test.rb
|
54
|
+
homepage: https://github.com/rking/tweemux
|
55
|
+
licenses: []
|
56
|
+
post_install_message:
|
57
|
+
rdoc_options: []
|
58
|
+
require_paths:
|
59
|
+
- lib
|
60
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ! '>='
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0'
|
72
|
+
requirements: []
|
73
|
+
rubyforge_project:
|
74
|
+
rubygems_version: 1.8.24
|
75
|
+
signing_key:
|
76
|
+
specification_version: 3
|
77
|
+
summary: ! 'For Remote Pair Programming: A handy script to create-or-join a world-readable
|
78
|
+
tmux session (smaller than wemux)'
|
79
|
+
test_files:
|
80
|
+
- test/action_test_helper.rb
|
81
|
+
- test/test_helper.rb
|
82
|
+
- test/tweemux/action/at_test.rb
|
83
|
+
- test/tweemux/action/forward_test.rb
|
84
|
+
- test/tweemux/action/host_test.rb
|
85
|
+
- test/tweemux/action/share_test.rb
|
86
|
+
- test/tweemux/action_test.rb
|
87
|
+
- test/tweemux_test.rb
|
88
|
+
has_rdoc:
|