pingpongpear 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 398675b7d66d9103e11e13a4c5e16d6dc4084dac
4
+ data.tar.gz: ebe8152549dae3433f437f563af4f6abe22b49b6
5
+ SHA512:
6
+ metadata.gz: 2794b44623036b5b525c332fe52e676e9b751585c0406ba63c77fb5fcc9b712b7956131114c00a12794a4de425be655a144b8202dc7a2d39e95a20a496a4a9bb
7
+ data.tar.gz: b61c396a9d1fd66ca26d9604e3f508f5969e7377c671378b690c0ea1abe72140d09c01fc5755511fd2e1e0a5baaca80f655a4ebf1d660d4d1fb66d9e5323de53
data/.autotest ADDED
@@ -0,0 +1,8 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'autotest/restart'
4
+
5
+ Autotest.add_hook :initialize do |at|
6
+ at.testlib = 'minitest/autorun'
7
+ at.find_directories = ARGV unless ARGV.empty?
8
+ end
data/.gemtest ADDED
File without changes
data/CHANGELOG.rdoc ADDED
@@ -0,0 +1,6 @@
1
+ === 1.0.0 / 2015-01-03
2
+
3
+ * 1 major enhancement
4
+
5
+ * Birthday!
6
+
data/Manifest.txt ADDED
@@ -0,0 +1,8 @@
1
+ .autotest
2
+ CHANGELOG.rdoc
3
+ Manifest.txt
4
+ README.rdoc
5
+ Rakefile
6
+ bin/pingpongpear
7
+ lib/ping_pong_pear.rb
8
+ test/test_ping_pong_pear.rb
data/README.rdoc ADDED
@@ -0,0 +1,52 @@
1
+ = ping_pong_pear
2
+
3
+ * http://github.com/tenderlove/pingpongpear
4
+
5
+ == DESCRIPTION:
6
+
7
+ It is for ping pong pearing
8
+
9
+ == FEATURES/PROBLEMS:
10
+
11
+ * many
12
+
13
+ == SYNOPSIS:
14
+
15
+ $ pingpongpear start team_fun
16
+
17
+ Other machine:
18
+
19
+ $ pingpongpear clone team_fun
20
+ $ cd team_fun
21
+ $ pingpongpear start
22
+
23
+ Then start pairing
24
+
25
+ == INSTALL:
26
+
27
+ * gem install pingpongpear
28
+
29
+ == LICENSE:
30
+
31
+ (The MIT License)
32
+
33
+ Copyright (c) 2015 Aaron Patterson
34
+
35
+ Permission is hereby granted, free of charge, to any person obtaining
36
+ a copy of this software and associated documentation files (the
37
+ 'Software'), to deal in the Software without restriction, including
38
+ without limitation the rights to use, copy, modify, merge, publish,
39
+ distribute, sublicense, and/or sell copies of the Software, and to
40
+ permit persons to whom the Software is furnished to do so, subject to
41
+ the following conditions:
42
+
43
+ The above copyright notice and this permission notice shall be
44
+ included in all copies or substantial portions of the Software.
45
+
46
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
47
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
48
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
49
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
50
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
51
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
52
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+
6
+ Hoe.plugins.delete :rubyforge
7
+ Hoe.plugin :minitest
8
+ Hoe.plugin :gemspec # `gem install hoe-gemspec`
9
+ Hoe.plugin :git # `gem install hoe-git`
10
+
11
+ Hoe.spec 'pingpongpear' do
12
+ developer('Aaron Patterson', 'aaron@tenderlovemaking.com')
13
+ self.readme_file = 'README.rdoc'
14
+ self.history_file = 'CHANGELOG.rdoc'
15
+ self.extra_rdoc_files = FileList['*.rdoc']
16
+ end
17
+
18
+ # vim: syntax=ruby
data/bin/pingpongpear ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'ping_pong_pear'
4
+ PingPongPear::Commands.run ARGV
@@ -0,0 +1,143 @@
1
+ require 'socket'
2
+ require 'ipaddr'
3
+ require 'json'
4
+
5
+ module PingPongPear
6
+ VERSION = '1.0.0'
7
+ UDP_PORT = 4545
8
+ MULTICAST_ADDR = "224.0.0.1"
9
+
10
+ class Broadcaster
11
+ def self.my_public_address
12
+ Socket.ip_address_list.reject { |addr|
13
+ addr.ipv4_loopback? || addr.ipv6_loopback? || addr.ipv6_linklocal?
14
+ }.first
15
+ end
16
+
17
+ def self.send_update args
18
+ new.send ['commit'] + args
19
+ end
20
+
21
+ def self.send_location name, address, port
22
+ new.send ['source', name, address, port]
23
+ end
24
+
25
+ def self.where_is? name
26
+ listener_t = Thread.new {
27
+ listener = Listener.new
28
+ listener.start { |cmd, n, *rest|
29
+ break(rest) if cmd == 'source' && n == name
30
+ }
31
+ }
32
+ broadcast = new
33
+ while listener_t.alive?
34
+ broadcast.send ['locate', name]
35
+ end
36
+ addr, port = listener_t.value
37
+ "http://#{addr}:#{port}/"
38
+ end
39
+
40
+ def initialize
41
+ @multicast_addr = MULTICAST_ADDR
42
+ @port = UDP_PORT
43
+ @socket = UDPSocket.open
44
+ @socket.setsockopt :IPPROTO_IP, :IP_MULTICAST_TTL, 1
45
+ end
46
+
47
+ def send message
48
+ @socket.send(JSON.dump(message), 0, @multicast_addr, @port)
49
+ end
50
+ end
51
+
52
+ class Listener
53
+ def initialize
54
+ @multicast_addr = MULTICAST_ADDR
55
+ @bind_addr = "0.0.0.0"
56
+ @port = UDP_PORT
57
+
58
+ @socket = UDPSocket.new
59
+ membership = IPAddr.new(@multicast_addr).hton + IPAddr.new(@bind_addr).hton
60
+
61
+ @socket.setsockopt :IPPROTO_IP, :IP_ADD_MEMBERSHIP, membership
62
+ @socket.setsockopt :SOL_SOCKET, :SO_REUSEPORT, 1
63
+
64
+ @socket.bind @bind_addr, @port
65
+ end
66
+
67
+ def start
68
+ loop do
69
+ message, _ = @socket.recvfrom 1024
70
+ yield JSON.parse(message)
71
+ end
72
+ end
73
+ end
74
+
75
+ module Commands
76
+ def self.start args
77
+ require 'webrick'
78
+ require 'securerandom'
79
+ require 'shellwords'
80
+
81
+ project_name = args.first || File.basename(Dir.pwd)
82
+ post_commit_hook = '.git/hooks/post-commit'
83
+ uuid = SecureRandom.uuid
84
+
85
+ system 'git update-server-info'
86
+
87
+ server = WEBrick::HTTPServer.new Port: 0, DocumentRoot: '.git'
88
+ http_port = server.listeners.map { |x| x.addr[1] }.first
89
+ my_address = Broadcaster.my_public_address.ip_address
90
+
91
+ File.open(post_commit_hook, 'w') { |f|
92
+ f.puts "#!/usr/bin/env ruby"
93
+ f.puts "ARGV[0] = 'update'"
94
+ f.puts "ARGV[1] = '#{project_name}'"
95
+ f.puts "ARGV[2] = '#{uuid}'"
96
+ f.puts "ARGV[3] = '#{my_address}'"
97
+ f.puts "ARGV[4] = '#{http_port}'"
98
+ f.write File.read __FILE__
99
+ }
100
+ File.chmod 0755, post_commit_hook
101
+
102
+ Thread.new {
103
+ listener = PingPongPear::Listener.new
104
+ listener.start { |cmd, name, *rest|
105
+ next unless name == project_name
106
+
107
+ case cmd
108
+ when 'locate' then Broadcaster.send_location project_name, my_address, http_port
109
+ when 'commit'
110
+ unless rest.first == uuid
111
+ url = "http://#{rest.drop(1).join(':')}"
112
+ system "git pull #{Shellwords.escape(url)}"
113
+ end
114
+ end
115
+ }
116
+ }
117
+
118
+ trap('INT') {
119
+ File.unlink post_commit_hook
120
+ server.shutdown
121
+ }
122
+ server.start
123
+ end
124
+
125
+ def self.clone args
126
+ require 'shellwords'
127
+ name = args.first
128
+ url = PingPongPear::Broadcaster.where_is? name
129
+ system "git clone #{Shellwords.escape(url)} #{Shellwords.escape(name)}"
130
+ end
131
+
132
+ def self.update args
133
+ system 'git update-server-info'
134
+ PingPongPear::Broadcaster.send_update args
135
+ end
136
+
137
+ def self.run args
138
+ send args.first, args.drop(1)
139
+ end
140
+ end
141
+ end
142
+
143
+ PingPongPear::Commands.run(ARGV) if $0 == __FILE__
@@ -0,0 +1,247 @@
1
+ require 'minitest/autorun'
2
+ require 'ping_pong_pear'
3
+ require 'tmpdir'
4
+
5
+ class TestPingPongPear < MiniTest::Test
6
+ def setup
7
+ super
8
+ @current_dir = Dir.pwd
9
+ Dir.chdir Dir.mktmpdir
10
+ end
11
+
12
+ def teardown
13
+ Dir.chdir @current_dir
14
+ super
15
+ end
16
+
17
+ def test_boot
18
+ system 'git init'
19
+ stderr_rd, stderr_wr = IO.pipe
20
+ pid = fork {
21
+ $stderr.reopen stderr_wr
22
+ PingPongPear::Commands.run ['start']
23
+ }
24
+ stderr_wr.close
25
+ loop do
26
+ line = stderr_rd.readline
27
+ break if line =~ /WEBrick::HTTPServer#start/
28
+ end
29
+ assert File.exist? '.git/hooks/post-commit'
30
+ ensure
31
+ Process.kill 'INT', pid
32
+ Process.waitpid pid
33
+ end
34
+
35
+ def test_shutdown
36
+ system 'git init'
37
+ stderr_rd, stderr_wr = IO.pipe
38
+ pid = fork {
39
+ $stderr.reopen stderr_wr
40
+ PingPongPear::Commands.run ['start']
41
+ }
42
+ stderr_wr.close
43
+ loop do
44
+ line = stderr_rd.readline
45
+ break if line =~ /WEBrick::HTTPServer#start/
46
+ end
47
+ assert File.exist? '.git/hooks/post-commit'
48
+ Process.kill 'INT', pid
49
+ Process.waitpid pid
50
+ refute File.exist? '.git/hooks/post-commit'
51
+ end
52
+
53
+ def test_clone
54
+ system 'git init'
55
+ File.write 'out.txt', 'aset'
56
+ system 'git add out.txt'
57
+ system 'git commit -m"xx"'
58
+
59
+ project_name = File.basename Dir.pwd
60
+ stderr_rd, stderr_wr = IO.pipe
61
+ pid = fork {
62
+ $stderr.reopen stderr_wr
63
+ PingPongPear::Commands.run ['start']
64
+ }
65
+ stderr_wr.close
66
+ loop do
67
+ line = stderr_rd.readline
68
+ break if line =~ /WEBrick::HTTPServer#start/
69
+ end
70
+
71
+ Dir.chdir Dir.mktmpdir
72
+ PingPongPear::Commands.run ['clone', project_name]
73
+
74
+ assert File.exist?(project_name), 'should be cloned'
75
+ assert File.exist? File.join(project_name, 'out.txt')
76
+ ensure
77
+ Process.kill 'INT', pid
78
+ Process.waitpid pid
79
+ end
80
+
81
+ def boot
82
+ stderr_rd, stderr_wr = IO.pipe
83
+ stdout_rd, stdout_wr = IO.pipe
84
+ pid = fork {
85
+ $stderr.reopen stderr_wr
86
+ $stdout.reopen stdout_wr
87
+ PingPongPear::Commands.run ['start']
88
+ }
89
+ stderr_wr.close
90
+ stdout_wr.close
91
+ loop do
92
+ line = stderr_rd.readline
93
+ break if line =~ /WEBrick::HTTPServer#start/
94
+ end
95
+ [pid, stderr_rd, stdout_rd]
96
+ end
97
+
98
+ def test_commit
99
+ system 'git init'
100
+ File.write 'out.txt', 'aset'
101
+ system 'git add out.txt'
102
+ system 'git commit -m"xx"'
103
+
104
+ project_name = File.basename Dir.pwd
105
+ pid, err, out = boot
106
+
107
+ original_dir = File.expand_path Dir.pwd
108
+ Dir.chdir Dir.mktmpdir
109
+ PingPongPear::Commands.run ['clone', project_name]
110
+ pid2 = nil
111
+ Dir.chdir(project_name) do
112
+ pid2, = boot
113
+
114
+ File.write 'out2.txt', 'ddddd'
115
+ system 'git add out2.txt'
116
+ system 'git commit -m"xx"'
117
+ end
118
+ require 'timeout'
119
+ Timeout.timeout(2) {
120
+ loop do
121
+ break if File.exist? File.join(original_dir, 'out2.txt')
122
+ end
123
+ }
124
+ ensure
125
+ Process.kill 'INT', pid
126
+ Process.waitpid pid
127
+ Process.kill 'INT', pid2
128
+ Process.waitpid pid2
129
+ end
130
+
131
+ def test_commit_works_on_branches
132
+ system 'git init'
133
+ File.write 'out.txt', 'aset'
134
+ system 'git checkout -b xxx'
135
+ system 'git add out.txt'
136
+ system 'git commit -m"xx"'
137
+
138
+ project_name = File.basename Dir.pwd
139
+ stderr_rd, stderr_wr = IO.pipe
140
+ pid = fork {
141
+ $stderr.reopen stderr_wr
142
+ PingPongPear::Commands.run ['start']
143
+ }
144
+ stderr_wr.close
145
+ loop do
146
+ line = stderr_rd.readline
147
+ break if line =~ /WEBrick::HTTPServer#start/
148
+ end
149
+
150
+ original_dir = File.expand_path Dir.pwd
151
+ newdir = Dir.mktmpdir
152
+ Dir.chdir newdir
153
+ PingPongPear::Commands.run ['clone', project_name]
154
+ Dir.chdir project_name
155
+
156
+ pid2 = fork { PingPongPear::Commands.run ['start'] }
157
+ loop do
158
+ break if File.exist? '.git/hooks/post-commit'
159
+ end
160
+ File.write 'out2.txt', 'ddddd'
161
+ system 'git checkout xxx'
162
+ system 'git add out2.txt'
163
+ system 'git commit -m"xx"'
164
+ require 'timeout'
165
+ Timeout.timeout(2) {
166
+ loop do
167
+ break if File.exist? File.join(original_dir, 'out2.txt')
168
+ end
169
+ }
170
+ ensure
171
+ Process.kill 'INT', pid
172
+ Process.waitpid pid
173
+ Process.kill 'INT', pid2
174
+ Process.waitpid pid2
175
+ end
176
+
177
+ def test_clone_works_with_name
178
+ system 'git init'
179
+ File.write 'out.txt', 'aset'
180
+ system 'git add out.txt'
181
+ system 'git commit -m"xx"'
182
+
183
+ project_name = 'foo'
184
+ stderr_rd, stderr_wr = IO.pipe
185
+ pid = fork {
186
+ $stderr.reopen stderr_wr
187
+ PingPongPear::Commands.run ['start', project_name]
188
+ }
189
+ stderr_wr.close
190
+ loop do
191
+ line = stderr_rd.readline
192
+ break if line =~ /WEBrick::HTTPServer#start/
193
+ end
194
+
195
+ Dir.chdir Dir.mktmpdir
196
+ PingPongPear::Commands.run ['clone', project_name]
197
+
198
+ assert File.exist?(project_name), 'should be cloned'
199
+ assert File.exist? File.join(project_name, 'out.txt')
200
+ ensure
201
+ Process.kill 'INT', pid
202
+ Process.waitpid pid
203
+ end
204
+
205
+ def test_commit_works_with_name
206
+ system 'git init'
207
+ File.write 'out.txt', 'aset'
208
+ system 'git add out.txt'
209
+ system 'git commit -m"xx"'
210
+
211
+ project_name = 'foo'
212
+ stderr_rd, stderr_wr = IO.pipe
213
+ pid = fork {
214
+ $stderr.reopen stderr_wr
215
+ PingPongPear::Commands.run ['start', project_name]
216
+ }
217
+ stderr_wr.close
218
+ loop do
219
+ line = stderr_rd.readline
220
+ break if line =~ /WEBrick::HTTPServer#start/
221
+ end
222
+
223
+ original_dir = File.expand_path Dir.pwd
224
+ Dir.chdir Dir.mktmpdir
225
+ PingPongPear::Commands.run ['clone', project_name]
226
+ Dir.chdir project_name
227
+
228
+ pid2 = fork { PingPongPear::Commands.run ['start', project_name] }
229
+ loop do
230
+ break if File.exist? '.git/hooks/post-commit'
231
+ end
232
+ File.write 'out2.txt', 'ddddd'
233
+ system 'git add out2.txt'
234
+ system 'git commit -m"xx"'
235
+ require 'timeout'
236
+ Timeout.timeout(2) {
237
+ loop do
238
+ break if File.exist? File.join(original_dir, 'out2.txt')
239
+ end
240
+ }
241
+ ensure
242
+ Process.kill 'INT', pid
243
+ Process.waitpid pid
244
+ Process.kill 'INT', pid2
245
+ Process.waitpid pid2
246
+ end
247
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pingpongpear
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Aaron Patterson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-01-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: minitest
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '5.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '5.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rdoc
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '4.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '4.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: hoe
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.13'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.13'
55
+ description: It is for ping pong pearing
56
+ email:
57
+ - aaron@tenderlovemaking.com
58
+ executables:
59
+ - pingpongpear
60
+ extensions: []
61
+ extra_rdoc_files:
62
+ - CHANGELOG.rdoc
63
+ - Manifest.txt
64
+ - README.rdoc
65
+ files:
66
+ - ".autotest"
67
+ - ".gemtest"
68
+ - CHANGELOG.rdoc
69
+ - Manifest.txt
70
+ - README.rdoc
71
+ - Rakefile
72
+ - bin/pingpongpear
73
+ - lib/ping_pong_pear.rb
74
+ - test/test_ping_pong_pear.rb
75
+ homepage: http://github.com/tenderlove/pingpongpear
76
+ licenses:
77
+ - MIT
78
+ metadata: {}
79
+ post_install_message:
80
+ rdoc_options:
81
+ - "--main"
82
+ - README.rdoc
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ requirements: []
96
+ rubyforge_project:
97
+ rubygems_version: 2.4.5
98
+ signing_key:
99
+ specification_version: 4
100
+ summary: It is for ping pong pearing
101
+ test_files:
102
+ - test/test_ping_pong_pear.rb