tweemux 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gemspec +1 -1
- data/README.md +35 -4
- data/lib/tweemux/action/hubkey.rb +45 -0
- data/lib/tweemux/action.rb +19 -3
- data/lib/tweemux/version.rb +1 -1
- data/lib/tweemux.rb +3 -0
- data/test/action_test_helper.rb +8 -3
- data/test/tweemux/action/hubkey_test.rb +64 -0
- data/test/tweemux_test.rb +12 -7
- metadata +8 -9
- data/lib/tweemux/action/keys.rb +0 -3
data/.gemspec
CHANGED
@@ -7,7 +7,7 @@ Working.gemspec(
|
|
7
7
|
:name => 'tweemux',
|
8
8
|
:summary => Working.third_line_of_readme,
|
9
9
|
#:description => Working.readme_snippet(/== Usage/, /== TODO/),
|
10
|
-
:description =>
|
10
|
+
:description => 'tweemux host; tweemux at some_machine; plus some more stuff',
|
11
11
|
:version => Tweemux::VERSION,
|
12
12
|
:authors => %w(☈king),
|
13
13
|
:email => 'rking-tweemux@sharpsaw.org',
|
data/README.md
CHANGED
@@ -28,7 +28,16 @@ For starters:
|
|
28
28
|
|
29
29
|
Then, create the user on your machine (this varies. On decent Unices, it's `adduser` or `useradd`. On OS X you can either get an [adduser-like script](https://raw.github.com/sharpsaw/mac-dots/master/bin/adduser) or do it through the System Preferences GUI).
|
30
30
|
|
31
|
-
|
31
|
+
Now, you can install the 'keyholes' for this user from Github. This means they
|
32
|
+
will be able to get in without manually typing a password, just like when they
|
33
|
+
`git push` to Github:
|
34
|
+
|
35
|
+
tweemux hubkey cirwin github: ConradIrwin
|
36
|
+
# or, if their Unix username is their Github username, there's a shorthand:
|
37
|
+
tweemux hubkey rking
|
38
|
+
|
39
|
+
Now, they'll need a route to your sshd port. If you're on a machine behind a
|
40
|
+
firewall, you have these options:
|
32
41
|
|
33
42
|
* VPN. If you're on a business network with your pair, you probably can already `ping` each other's machines. Easy stuff, then.
|
34
43
|
* Directly open a port, such as going to your router config (perhaps at http://10.0.0.1 ?) and setting it to pass the external IP (see `curl ifconfig.me` or http://whatismyip.com ) through to your local box. This is smpler once you get it set up, as long as your location is stable and you have control over the router.
|
@@ -52,11 +61,33 @@ It's also nice to share a windowing environment session as well. For example, th
|
|
52
61
|
|
53
62
|
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.
|
54
63
|
|
64
|
+
## Comms
|
65
|
+
|
66
|
+
Once you've shared the tmux session, you've got a lot. But you still need more bandwidth with your pair.
|
67
|
+
|
68
|
+
There are a few solutions:
|
69
|
+
|
70
|
+
- *Use inline comments* to type back and forth. Properly configured shells can also allow you to prefix with a `#` and say whatever you want. The downside is that only one person can be typing at any one time. A custom that helps is if you type what you're saying, then leave a trailing `#` with no space after it to indicate that you're done and waiting on your pair to type. This is a bit crufty, but some people are shy and only want to do the tmux share.
|
71
|
+
- *Use IRC*. If you make a tmux pane and connect it to IRC (or some other text-based chat), both local users plus that shared session can all join the same channel. This provides the person that isn't driving the keyboard to switch to their local chat window and type something for the other person to see. (TODO: build this in as a tweemux feature).
|
72
|
+
- *Use voice*. A much more powerful way to work. You "can" use Skype, but it's a commercial product and is continuously flaky. A better alternative is to sign up for a [SIP account](https://ekiga.net/), then get a [SIP client](https://en.wikipedia.org/wiki/List_of_SIP_software#Clients). For Linux, I use (Ekiga)[https://en.wikipedia.org/wiki/Ekiga), and for OS X I use [Telephone.app](http://www.tlphn.com/). Don't forget, if you can't get the Internetty stuff to work, there's always calling each other on cell phones.
|
73
|
+
|
55
74
|
## TODO
|
56
75
|
|
57
|
-
|
58
|
-
|
59
|
-
|
76
|
+
# PeopleAdmin/tweemux open issues
|
77
|
+
* 18: Rogue Mode 3
|
78
|
+
* 17: Mention Google Voice/Hangouts 1
|
79
|
+
* 16: Promote regular tmux session to tweemux
|
80
|
+
* 15: Guest usage to connect to a different socket path
|
81
|
+
* 14: Add pseudo_restart of 'retry'
|
82
|
+
* 13: $TERM
|
83
|
+
* 12: Mosh possible
|
84
|
+
* 11: Basics of Tmux
|
85
|
+
* 8: Port fw without ssh [enhancement] [question]
|
86
|
+
* 7: adduser/useradd call [enhancement]
|
87
|
+
* 6: Mac setup instructions [easy-peasy] [Mac]
|
88
|
+
* 5: log [enhancement] [easy-peasy] [must-have]
|
89
|
+
* 4: brew install someurl [Mac]
|
90
|
+
* 1: --help and help [easy-peasy] [must-have]
|
60
91
|
|
61
92
|
## Thanks to
|
62
93
|
|
@@ -0,0 +1,45 @@
|
|
1
|
+
class Tweemux::Action::Hubkey < Tweemux::Action
|
2
|
+
attr_reader :github_user, :unix_user
|
3
|
+
def run args
|
4
|
+
@unix_user, @github_user = args
|
5
|
+
@github_user ||= @unix_user
|
6
|
+
run_with_both_usernames
|
7
|
+
end
|
8
|
+
|
9
|
+
def run_with_both_usernames
|
10
|
+
ensure_ssh_dir_exists
|
11
|
+
refuse_to_overwrite_authorized_keys_file
|
12
|
+
install_github_keys
|
13
|
+
end
|
14
|
+
|
15
|
+
def ensure_ssh_dir_exists
|
16
|
+
explained_run_as unix_user, %W(mkdir -p -m700 #{home_dir}/.ssh),
|
17
|
+
"Make sure #{unix_user} has a ~/.ssh/ dir"
|
18
|
+
end
|
19
|
+
|
20
|
+
def refuse_to_overwrite_authorized_keys_file
|
21
|
+
return unless File.exists? authorized_keys_path
|
22
|
+
# TODO: some color
|
23
|
+
raise NoRestartsException, <<-EOT
|
24
|
+
Refusing to overwrite #{authorized_keys_path}
|
25
|
+
Try: sudo cat #{authorized_keys_path}
|
26
|
+
Then maybe: sudo rm #{authorized_keys_path}
|
27
|
+
EOT
|
28
|
+
end
|
29
|
+
|
30
|
+
def install_github_keys
|
31
|
+
key_url = "https://github.com/#{github_user}.keys"
|
32
|
+
explained_run_as unix_user, %W(curl #{key_url} -o #{authorized_keys_path}),
|
33
|
+
"Download #{github_user}'s 'keyholes' from Github"
|
34
|
+
end
|
35
|
+
|
36
|
+
def home_dir
|
37
|
+
begin
|
38
|
+
their_dir = File.expand_path '~'+unix_user
|
39
|
+
rescue ArgumentError
|
40
|
+
raise NoSuchHomeDir
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def authorized_keys_path; home_dir+'/.ssh/authorized_keys' end
|
45
|
+
end
|
data/lib/tweemux/action.rb
CHANGED
@@ -9,12 +9,21 @@ class Tweemux
|
|
9
9
|
end
|
10
10
|
|
11
11
|
class FunkyUsage < RuntimeError; end
|
12
|
+
class NoSuchHomeDir < RuntimeError; end
|
13
|
+
class NoRestartsException < RuntimeError; end
|
12
14
|
|
13
15
|
def run args; raise 'Unimplemented' end
|
14
16
|
|
15
|
-
# Hrm.
|
16
|
-
def explained_run
|
17
|
-
|
17
|
+
# Hrm. These are kinda gross, but I feel like it cleans up the subclasses
|
18
|
+
def explained_run what, why
|
19
|
+
self.class.explained_run what, why
|
20
|
+
end
|
21
|
+
def explained_run_as who, what, why
|
22
|
+
self.class.explained_run_as who, what, why
|
23
|
+
end
|
24
|
+
def tmux_S *a
|
25
|
+
self.class.tmux_S *a
|
26
|
+
end
|
18
27
|
|
19
28
|
class DubiousSystemInvocation < RuntimeError; end
|
20
29
|
class << self
|
@@ -30,6 +39,11 @@ class Tweemux
|
|
30
39
|
system_or_raise what
|
31
40
|
end
|
32
41
|
|
42
|
+
def explained_run_as user, what, why
|
43
|
+
full_command = ['sudo', '-u', user] + what
|
44
|
+
explained_run full_command, why
|
45
|
+
end
|
46
|
+
|
33
47
|
def explain what, why
|
34
48
|
warn highlight_command(what) + highlight_explanation(why)
|
35
49
|
end
|
@@ -81,6 +95,8 @@ To run anyway, hit: #{enter}
|
|
81
95
|
require 'pry'
|
82
96
|
binding.pry
|
83
97
|
end
|
98
|
+
rescue Interrupt
|
99
|
+
exit 2
|
84
100
|
end
|
85
101
|
|
86
102
|
def load_all!
|
data/lib/tweemux/version.rb
CHANGED
data/lib/tweemux.rb
CHANGED
data/test/action_test_helper.rb
CHANGED
@@ -2,12 +2,17 @@ require_relative 'test_helper'
|
|
2
2
|
|
3
3
|
module TweemuxActionHelper
|
4
4
|
def test_run
|
5
|
+
assert_equal stubbed_run.to_yaml, expected_commands.to_yaml
|
6
|
+
end
|
7
|
+
|
8
|
+
def stubbed_run
|
5
9
|
explained_runs = []
|
6
|
-
Tweemux::Action.stub :explained_run, ->
|
10
|
+
Tweemux::Action.stub :explained_run, -> what, why {
|
11
|
+
explained_runs << [ what, why ]
|
12
|
+
} do
|
7
13
|
Tweemux.run argv
|
8
14
|
end
|
9
|
-
|
10
|
-
assert_equal got.to_yaml, expected_commands.to_yaml
|
15
|
+
explained_runs.map &:first # drop the explanations
|
11
16
|
end
|
12
17
|
|
13
18
|
def bad_runs; [] end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require_relative '../../action_test_helper'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
class Tweemux::Action::HubkeyTest < MiniTest::Unit::TestCase
|
5
|
+
include TweemuxActionHelper
|
6
|
+
def argv; %w(bro hi HiOnGithub) end
|
7
|
+
def test_run
|
8
|
+
commands_run = nil
|
9
|
+
with_fake_expand_path expecting: '~hi', expanding_to: '/home/hi' do
|
10
|
+
commands_run = stubbed_run
|
11
|
+
end
|
12
|
+
assert_equal %w(sudo -u hi mkdir -p -m700 /home/hi/.ssh),
|
13
|
+
commands_run.shift
|
14
|
+
assert_equal %W(
|
15
|
+
sudo -u hi
|
16
|
+
curl https://github.com/HiOnGithub.keys
|
17
|
+
-o /home/hi/.ssh/authorized_keys),
|
18
|
+
commands_run.shift
|
19
|
+
assert commands_run.empty?, commands_run
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_run_will_not_clobber_auth_keys_file
|
23
|
+
with_fake_expand_path expecting: '~hi', expanding_to: '/home/hi' do
|
24
|
+
File.stub :exists?, -> path {
|
25
|
+
assert_equal '/home/hi/.ssh/authorized_keys', path
|
26
|
+
true
|
27
|
+
} do
|
28
|
+
stubbed_run
|
29
|
+
end
|
30
|
+
end
|
31
|
+
fail "Shouldn't get here"
|
32
|
+
rescue Tweemux::Action::NoRestartsException => e
|
33
|
+
assert_match /refusing to overwrite.*authorized_keys/i, e.message
|
34
|
+
end
|
35
|
+
|
36
|
+
def with_fake_expand_path args
|
37
|
+
File.stub :expand_path, -> short_path {
|
38
|
+
assert_equal args[:expecting], short_path
|
39
|
+
args[:expanding_to]
|
40
|
+
} do yield end
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_no_homedir
|
44
|
+
File.stub :expand_path, -> path { raise ArgumentError, 'no such user' } do
|
45
|
+
stubbed_run
|
46
|
+
end
|
47
|
+
rescue Tweemux::Action::NoSuchHomeDir
|
48
|
+
else
|
49
|
+
fail 'Should be noisy about missing users'
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_shorthand_invocation
|
53
|
+
action = Tweemux::Action::Hubkey.new :unused
|
54
|
+
called = false
|
55
|
+
action.stub :run_with_both_usernames, -> {
|
56
|
+
called = true
|
57
|
+
} do
|
58
|
+
action.run 'a'
|
59
|
+
end
|
60
|
+
assert called
|
61
|
+
assert_equal 'a', action.unix_user
|
62
|
+
assert_equal 'a', action.github_user
|
63
|
+
end
|
64
|
+
end
|
data/test/tweemux_test.rb
CHANGED
@@ -16,14 +16,19 @@ class TweemuxTest < MiniTest::Unit::TestCase
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def test_ruby18
|
19
|
-
|
20
|
-
|
21
|
-
# TODO: optimize this by running them all in one command
|
22
|
-
Dir['{bin,lib}/*.rb'].each do |e|
|
23
|
-
fail "ruby18 hates #{e}" unless "Syntax OK\n" == `ruby18 -c #{e}`
|
24
|
-
end
|
19
|
+
got = `ruby18 -Ilib -rtweemux -e1`
|
20
|
+
fail "ruby18 hates us: #{got}" unless got.empty?
|
25
21
|
rescue Errno::ENOENT => e
|
26
|
-
|
22
|
+
# TODO: colorize
|
23
|
+
skip <<-EOT if e.message[/ruby18/]
|
24
|
+
This test needs a 'ruby18' executable in $PATH
|
25
|
+
If using rbenv, one such script might be:
|
26
|
+
|
27
|
+
#!/bin/sh -e
|
28
|
+
eval "$(rbenv init -)"
|
29
|
+
rbenv shell 1.8.7-p371
|
30
|
+
ruby "$@"
|
31
|
+
EOT
|
27
32
|
raise e
|
28
33
|
end
|
29
34
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tweemux
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,16 +9,13 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-03-05 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: ! 'For Remote Pair Programming: A handy script to create-or-join a world-readable
|
15
15
|
tmux session
|
16
16
|
|
17
17
|
|
18
|
-
|
19
|
-
session
|
20
|
-
|
21
|
-
'
|
18
|
+
tweemux host; tweemux at some_machine; plus some more stuff'
|
22
19
|
email: rking-tweemux@sharpsaw.org
|
23
20
|
executables:
|
24
21
|
- tweemux
|
@@ -40,7 +37,7 @@ files:
|
|
40
37
|
- lib/tweemux/action/at.rb
|
41
38
|
- lib/tweemux/action/forward.rb
|
42
39
|
- lib/tweemux/action/host.rb
|
43
|
-
- lib/tweemux/action/
|
40
|
+
- lib/tweemux/action/hubkey.rb
|
44
41
|
- lib/tweemux/action/share.rb
|
45
42
|
- lib/tweemux/core_ext.rb
|
46
43
|
- lib/tweemux/version.rb
|
@@ -50,6 +47,7 @@ files:
|
|
50
47
|
- test/tweemux/action/at_test.rb
|
51
48
|
- test/tweemux/action/forward_test.rb
|
52
49
|
- test/tweemux/action/host_test.rb
|
50
|
+
- test/tweemux/action/hubkey_test.rb
|
53
51
|
- test/tweemux/action/share_test.rb
|
54
52
|
- test/tweemux/action_test.rb
|
55
53
|
- test/tweemux_test.rb
|
@@ -60,17 +58,17 @@ rdoc_options: []
|
|
60
58
|
require_paths:
|
61
59
|
- lib
|
62
60
|
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
-
none: false
|
64
61
|
requirements:
|
65
62
|
- - ! '>='
|
66
63
|
- !ruby/object:Gem::Version
|
67
64
|
version: '0'
|
68
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
65
|
none: false
|
66
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
67
|
requirements:
|
71
68
|
- - ! '>='
|
72
69
|
- !ruby/object:Gem::Version
|
73
70
|
version: '0'
|
71
|
+
none: false
|
74
72
|
requirements: []
|
75
73
|
rubyforge_project:
|
76
74
|
rubygems_version: 1.8.23
|
@@ -85,6 +83,7 @@ test_files:
|
|
85
83
|
- test/tweemux/action/at_test.rb
|
86
84
|
- test/tweemux/action/forward_test.rb
|
87
85
|
- test/tweemux/action/host_test.rb
|
86
|
+
- test/tweemux/action/hubkey_test.rb
|
88
87
|
- test/tweemux/action/share_test.rb
|
89
88
|
- test/tweemux/action_test.rb
|
90
89
|
- test/tweemux_test.rb
|
data/lib/tweemux/action/keys.rb
DELETED