pingpongpear 1.0.0 → 2.0.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/bin/pingpongpear +1 -1
- data/lib/ping_pong_pear.rb +140 -116
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7c0839e52767ce0d7c55c2089253fba002345785
|
4
|
+
data.tar.gz: c83a92f8743ae021c67edb43022946df58eadce4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ab94cabb5d3a0ec278810519fa08f9416c2baac554819ce3eafdd33819dd8b247c3e99edb14b66ffba62339ef7fefe2e765c030a72c785eeb745b1f9ca7dca14
|
7
|
+
data.tar.gz: 7294e1f3f09f9dca66f6ff5b317d8f95e59b6a6abc15015714e86afa0ee51c1195de12cbaf8a786eb73665170cd5065b7be7f18edeac2122afff61ca0b0a22da
|
data/bin/pingpongpear
CHANGED
data/lib/ping_pong_pear.rb
CHANGED
@@ -1,143 +1,167 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
3
|
-
require '
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
1
|
+
require 'dnssd'
|
2
|
+
require 'webrick'
|
3
|
+
require 'securerandom'
|
4
|
+
require 'set'
|
5
|
+
require 'net/http'
|
6
|
+
require 'logger'
|
7
|
+
require 'shellwords'
|
8
|
+
|
9
|
+
class PingPongPear
|
10
|
+
VERSION = '2.0.0'
|
11
|
+
|
12
|
+
SERVICE = "_http._tcp,pingpongpear"
|
13
|
+
|
14
|
+
def self.run args
|
15
|
+
new.public_send args.first, *args.drop(1)
|
16
|
+
rescue
|
17
|
+
cmds = public_instance_methods(false).find_all { |x|
|
18
|
+
instance_method(x).arity < 0
|
19
|
+
}
|
20
|
+
$stderr.puts "USAGE: pingpongpear #{cmds}"
|
21
|
+
exit 1
|
22
|
+
end
|
16
23
|
|
17
|
-
|
18
|
-
new.send ['commit'] + args
|
19
|
-
end
|
24
|
+
attr_reader :pull_requests, :peers, :logger
|
20
25
|
|
21
|
-
|
22
|
-
new
|
23
|
-
|
26
|
+
def initialize
|
27
|
+
@pull_requests = Queue.new
|
28
|
+
@send_pull_requests = Queue.new
|
29
|
+
@peers = Set.new
|
30
|
+
@logger = Logger.new $stdout
|
31
|
+
end
|
24
32
|
|
25
|
-
|
26
|
-
|
27
|
-
|
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
|
33
|
+
def start name = File.basename(Dir.pwd)
|
34
|
+
post_commit_hook = '.git/hooks/post-commit'
|
35
|
+
pidfile = '.git/pingpongpear.pid'
|
39
36
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
@socket.setsockopt :IPPROTO_IP, :IP_MULTICAST_TTL, 1
|
37
|
+
if File.exist? pidfile
|
38
|
+
raise "Another instance of Ping Pong Pear is running"
|
39
|
+
else
|
40
|
+
File.open(pidfile, 'w') { |f| f.write $$ }
|
45
41
|
end
|
46
42
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
43
|
+
File.open(post_commit_hook, 'w') { |f|
|
44
|
+
f.write <<-eof
|
45
|
+
#!/bin/sh
|
46
|
+
|
47
|
+
git update-server-info
|
48
|
+
kill -INFO $(cat #{pidfile})
|
49
|
+
eof
|
50
|
+
}
|
51
|
+
File.chmod 0755, post_commit_hook
|
52
|
+
|
53
|
+
system "git update-server-info"
|
54
|
+
|
55
|
+
at_exit {
|
56
|
+
File.unlink pidfile
|
57
|
+
File.unlink post_commit_hook
|
58
|
+
}
|
59
|
+
|
60
|
+
identifier = make_ident name
|
61
|
+
|
62
|
+
logger.info "SEND THIS TO YOUR PEAR `git clone #{name}`"
|
63
|
+
logger.info "CTRL-T sends pull requests to all peers"
|
64
|
+
logger.debug "MY PROJECT NAME: #{name} IDENT: #{identifier}"
|
51
65
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
@bind_addr = "0.0.0.0"
|
56
|
-
@port = UDP_PORT
|
66
|
+
server = start_server pull_requests
|
67
|
+
http_port = server.listeners.map { |x| x.addr[1] }.first
|
68
|
+
hostname = Socket.gethostname
|
57
69
|
|
58
|
-
|
59
|
-
|
70
|
+
discover identifier, name, peers
|
71
|
+
process_pull_requests pull_requests
|
72
|
+
process_send_pull_requests @send_pull_requests
|
60
73
|
|
61
|
-
|
62
|
-
@socket.setsockopt :SOL_SOCKET, :SO_REUSEPORT, 1
|
74
|
+
trap('INFO') { send_pull_requests peers, hostname, http_port }
|
63
75
|
|
64
|
-
|
76
|
+
advertise(identifier, name, hostname, http_port).each { |x| x }
|
77
|
+
end
|
78
|
+
|
79
|
+
def clone name, dir = nil
|
80
|
+
browser = DNSSD::Service.browse SERVICE
|
81
|
+
browser.each do |response|
|
82
|
+
r = response.resolve
|
83
|
+
if r.text_record['project'] == name
|
84
|
+
url = "http://#{r.target}:#{r.port}"
|
85
|
+
system "git clone #{Shellwords.escape(url)} #{dir || Shellwords.escape(name)}"
|
86
|
+
break
|
87
|
+
end
|
65
88
|
end
|
89
|
+
end
|
66
90
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
91
|
+
private
|
92
|
+
|
93
|
+
def start_server pull_requests
|
94
|
+
server = WEBrick::HTTPServer.new Port: 0,
|
95
|
+
DocumentRoot: '.git',
|
96
|
+
Logger: logger
|
97
|
+
server.mount_proc '/pull' do |req, res|
|
98
|
+
host = req.query['host']
|
99
|
+
port = req.query['port']
|
100
|
+
if host && port
|
101
|
+
logger.info "ADDED PR: #{host}:#{port}"
|
102
|
+
pull_requests << [host, port.to_i]
|
71
103
|
end
|
104
|
+
res.body = ''
|
72
105
|
end
|
106
|
+
Thread.new { server.start }
|
107
|
+
server
|
108
|
+
end
|
109
|
+
|
110
|
+
def advertise ident, name, hostname, http_port
|
111
|
+
txt = DNSSD::TextRecord.new 'project' => name
|
112
|
+
DNSSD::Service.register ident, SERVICE, nil, http_port, hostname, txt
|
113
|
+
end
|
114
|
+
|
115
|
+
def make_ident name
|
116
|
+
"#{name} (#{SecureRandom.hex.slice(0, 4)})"
|
73
117
|
end
|
74
118
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
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
|
119
|
+
def discover ident, name, peers
|
120
|
+
browser = DNSSD::Service.browse SERVICE
|
121
|
+
browser.async_each do |response|
|
122
|
+
if response.flags.to_i > 0
|
123
|
+
logger.debug "SAW: #{response.name}"
|
124
|
+
unless response.name == ident
|
125
|
+
r = response.resolve
|
126
|
+
if r.text_record['project'] == name
|
127
|
+
logger.info "PEER: #{response.name}"
|
128
|
+
peers << [response.name, r.target, r.port]
|
114
129
|
end
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
server.shutdown
|
121
|
-
}
|
122
|
-
server.start
|
130
|
+
end
|
131
|
+
else
|
132
|
+
peers.delete_if { |id, _, _| id == response.name }
|
133
|
+
logger.info "REMOVED: #{response.name}"
|
134
|
+
end
|
123
135
|
end
|
136
|
+
end
|
124
137
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
url = PingPongPear::Broadcaster.where_is? name
|
129
|
-
system "git clone #{Shellwords.escape(url)} #{Shellwords.escape(name)}"
|
138
|
+
def send_pull_requests peers, http_host, http_port
|
139
|
+
peers.each do |_, host, port|
|
140
|
+
@send_pull_requests << [host, port, http_host, http_port]
|
130
141
|
end
|
142
|
+
end
|
131
143
|
|
132
|
-
|
133
|
-
|
134
|
-
|
144
|
+
def process_send_pull_requests requests
|
145
|
+
Thread.new do
|
146
|
+
while pr = requests.pop
|
147
|
+
host, port, http_host, http_port = *pr
|
148
|
+
http = Net::HTTP.new host, port
|
149
|
+
request = Net::HTTP::Post.new '/pull'
|
150
|
+
request.set_form_data 'host' => http_host, 'port' => http_port
|
151
|
+
http.request request
|
152
|
+
end
|
135
153
|
end
|
154
|
+
end
|
136
155
|
|
137
|
-
|
138
|
-
|
156
|
+
def process_pull_requests pull_requests
|
157
|
+
Thread.new do
|
158
|
+
while pr = pull_requests.pop
|
159
|
+
url = "http://#{pr.join(':')}"
|
160
|
+
logger.debug "git pull #{Shellwords.escape(url)}"
|
161
|
+
system "git pull #{Shellwords.escape(url)}"
|
162
|
+
end
|
139
163
|
end
|
140
164
|
end
|
141
165
|
end
|
142
166
|
|
143
|
-
PingPongPear
|
167
|
+
PingPongPear.run(ARGV) if $0 == __FILE__
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pingpongpear
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aaron Patterson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-03-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: minitest
|