pairjour 0.1.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ pkg
@@ -0,0 +1,26 @@
1
+ == 6.6.0 2009-08-18
2
+
3
+ * Rely on latest DNSSD (1.3)
4
+
5
+ == 6.5.0 2009-03-17
6
+
7
+ * Added web command to launch and announce instaweb
8
+ * Added browse command to see all available instawebs
9
+
10
+ == 6.4.0 2009-03-13
11
+
12
+ * Add pull command
13
+ * Support git 1.6+
14
+ * Require dnssd 0.7.1+ to support Avahi
15
+
16
+ == 6.3.0 2008-08-23
17
+
18
+ * Simplify project layout, remove newgem dependency
19
+ * Add a prefix to projects' served name
20
+
21
+ [...]
22
+
23
+ == 0.0.1 2008-05-29
24
+
25
+ * 1 major enhancement:
26
+ * Initial release
@@ -0,0 +1,9 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.rdoc
4
+ Rakefile
5
+ bin/pairjour
6
+ lib/pairjour.rb
7
+ lib/pairjour/browser.rb
8
+ test/test_pairjour.rb
9
+ test/test_helper.rb
@@ -0,0 +1,37 @@
1
+ Pairjour
2
+ ===
3
+
4
+
5
+ Use the power of gitjour and git for easy pair programming, This originated as a fork of gitjour
6
+
7
+
8
+ 3 simple commands
9
+ ---
10
+
11
+ - pairjour start [project_name]
12
+ - pairjour switch
13
+ - pairjour finish
14
+
15
+
16
+
17
+
18
+ Recommended workfow
19
+ ---
20
+
21
+ Concept
22
+ ---
23
+
24
+ pairjour start [project_name]
25
+ ENV[project_name] = project_name
26
+ clear ENV[remote_project_name]
27
+
28
+ `pairjour serve . project_name`
29
+ git checkout -b 'pairing'
30
+
31
+ pairjour switch
32
+ unless ENV[remote_project_name]
33
+ ENV['remote_project_name'] = taylorluk-project_name
34
+ end
35
+
36
+ git commit -am 'pair session commit Timestamp'
37
+ gitjour pull [remote_project_name]
@@ -0,0 +1,20 @@
1
+ # -*- ruby -*-
2
+
3
+ require File.dirname(__FILE__) + '/lib/pairjour'
4
+
5
+ begin
6
+ require 'jeweler'
7
+ Jeweler::Tasks.new do |s|
8
+ s.version = Pairjour::VERSION
9
+ s.name = "pairjour"
10
+ s.summary = "pair programming using bonjour and git"
11
+ s.description = "pair programming using bonjour and git, originalted as a fork of gitjour"
12
+ s.email = "taylor.luk@idealian.net"
13
+ s.homepage = "http://github.com/idealian/pairjour"
14
+ s.authors = ["Taylor luk", "Khoa Nguyne", 'Chad Fowler', 'Evan Phoenix', 'Rich Kilmer', 'Phil Hagelberg']
15
+ s.add_dependency 'dnssd', "~> 1.3"
16
+
17
+ end
18
+ rescue LoadError
19
+ puts "Jeweler not available. Install it with: gem install jeweler"
20
+ end
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
3
+
4
+ require 'pairjour'
5
+
6
+ trap "INT" do
7
+ exit!
8
+ end
9
+
10
+ Pairjour::Application.run(*ARGV)
@@ -0,0 +1,303 @@
1
+ require 'rubygems'
2
+ require 'dnssd'
3
+ require 'set'
4
+ require 'timeout'
5
+ require 'fileutils'
6
+
7
+ Thread.abort_on_exception = true
8
+
9
+ module Pairjour
10
+ VERSION = "0.1.0.pre1"
11
+ GitService = Struct.new(:name, :host, :port, :description)
12
+
13
+ class Application
14
+
15
+ class << self
16
+ def run(*args)
17
+ case args.shift
18
+ when "list"
19
+ list
20
+ when "pull"
21
+ pull(*args)
22
+ when "clone"
23
+ clone(*args)
24
+ when "serve"
25
+ serve(*args)
26
+ when "remote"
27
+ remote(*args)
28
+ when "web"
29
+ web(*args)
30
+ when "browse"
31
+ browse(*args)
32
+ when "start"
33
+ start(*args)
34
+ when "finish"
35
+ finish(*args)
36
+ when "switch"
37
+ switch(*args)
38
+ else
39
+ help
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ #
46
+ def start(name)
47
+ set 'project-name', name
48
+
49
+ `git config --unset pairjour.service-name`
50
+
51
+ system "pairjour serve . #{name} &"
52
+
53
+ `git checkout -b pairing`
54
+ end
55
+
56
+
57
+ def switch
58
+ project = get('project-name')
59
+ service_name = get('service-name')
60
+
61
+ if service_name == ''
62
+ service = service_list.find do |s|
63
+ s.name.include?(project) && !s.name.include?(prefix)
64
+ end
65
+
66
+ set 'service-name', service_name = service.name
67
+ end
68
+
69
+ `git commit -am "pair session switch #{Time.now.to_s}"`
70
+ `pairjour pull #{service_name} pairing`
71
+ end
72
+
73
+ def pid_file_path
74
+ path = ENV['HOME'] + '/.pairjour/'
75
+
76
+ FileUtils.mkdir_p path
77
+
78
+ path + '.pid'
79
+ end
80
+
81
+ def finish
82
+ `kill #{open(pid_file_path).read}`
83
+ end
84
+
85
+ def get(name)
86
+ `git config pairjour.#{name}`.chomp
87
+ end
88
+
89
+ def set(name, value)
90
+ `git config pairjour.#{name} #{value}`
91
+ end
92
+
93
+ def prefix
94
+ prefix = get('prefix')
95
+ prefix = ENV["USER"] if prefix.empty?
96
+
97
+ prefix
98
+ end
99
+
100
+ def service_name(name)
101
+ # If the name starts with ^, then don't apply the prefix
102
+ if name[0] == ?^
103
+ name = name[1..-1]
104
+ else
105
+ name = [prefix, name].compact.join("-")
106
+ end
107
+ name
108
+ end
109
+
110
+ def list
111
+ service_list.each do |service|
112
+ puts "=== #{service.name} on #{service.host}:#{service.port} ==="
113
+ puts " pairjour (clone|pull) #{service.name}"
114
+ if service.description != '' && service.description !~ /^Unnamed repository/
115
+ puts " #{service.description}"
116
+ end
117
+ puts
118
+ end
119
+ end
120
+
121
+ def pull(repository_name, branch = "master")
122
+ service = locate_repo(repository_name) or
123
+ abort "ERROR: Unable to find project named '#{repository_name}'"
124
+
125
+ puts "Connecting to #{service.host}:#{service.port}"
126
+
127
+ system "git pull git://#{service.host}:#{service.port}/ #{branch}"
128
+ end
129
+
130
+ def clone(repository_name, *rest)
131
+ dir = rest.shift || repository_name
132
+ if File.exists?(dir)
133
+ abort "ERROR: Clone directory '#{dir}' already exists."
134
+ end
135
+
136
+ puts "Cloning '#{repository_name}' into directory '#{dir}'..."
137
+
138
+ service = locate_repo(repository_name) or
139
+ abort "ERROR: Unable to find project named '#{repository_name}'"
140
+
141
+ puts "Connecting to #{service.host}:#{service.port}"
142
+
143
+ system "git clone git://#{service.host}:#{service.port}/ #{dir}"
144
+ end
145
+
146
+ def remote(repository_name, *rest)
147
+ dir = rest.shift || repository_name
148
+ service = locate_repo repository_name
149
+ system "git remote add #{dir} git://#{service.host}:#{service.port}/"
150
+ end
151
+
152
+ def serve(path=Dir.pwd, *rest)
153
+ path = File.expand_path(path)
154
+ name = service_name(rest.shift || File.basename(path))
155
+ port = rest.shift || 9418
156
+
157
+ if File.exists?("#{path}/.git")
158
+ announce_git(path, name, port.to_i)
159
+ else
160
+ Dir["#{path}/*"].each do |dir|
161
+ if File.directory?(dir)
162
+ name = File.basename(dir)
163
+ announce_git(dir, name, 9418)
164
+ end
165
+ end
166
+ end
167
+
168
+ system "git daemon --verbose --export-all --port=#{port} --base-path=#{path} --base-path-relaxed --pid-file=#{pid_file_path}"
169
+ end
170
+
171
+ def web(path=Dir.pwd, *rest)
172
+ path = File.expand_path(path)
173
+ name = service_name(rest.shift || File.basename(path))
174
+ port = rest.shift || 1234
175
+ httpd = rest.shift || "webrick"
176
+
177
+ system("git instaweb --httpd=#{httpd} --port=#{port}") or
178
+ abort "Unable to launch git instaweb."
179
+
180
+ announce_web(path, name, port.to_i)
181
+
182
+ trap("INT") do
183
+ puts "Stopping instaweb..."
184
+ system "git instaweb stop"
185
+ exit
186
+ end
187
+
188
+ Thread.stop
189
+ end
190
+
191
+ def help
192
+ puts "Pairjour #{Pairjour::VERSION}"
193
+ puts "Serve up and use git repositories via ZeroConf."
194
+ puts "\nUsage: pairjour <command> [args]"
195
+ puts
196
+ puts " list"
197
+ puts " Lists available repositories."
198
+ puts
199
+ puts " clone <project> [<directory>]"
200
+ puts " Clone a pairjour-served repository."
201
+ puts
202
+ puts " serve <path_to_project> [<name_of_project>] [<port>] or"
203
+ puts " <path_to_projects>"
204
+ puts " Serve up the current directory or projects via pairjour."
205
+ puts
206
+ puts " The name of your project is automatically prefixed with"
207
+ puts " `git config --get pairjour.prefix` or your username (preference"
208
+ puts " in that order). If you don't want a prefix, put a ^ on the front"
209
+ puts " of the name_of_project (the ^ is removed before announcing)."
210
+ puts
211
+ puts " pull <project> [<branch>]"
212
+ puts " Pull from a pairjour-served repository."
213
+ puts
214
+ puts " remote <project> [<name>]"
215
+ puts " Add a ZeroConf remote into your current repository."
216
+ puts " Optionally pass name to not use pwd."
217
+ puts
218
+ puts " web <path_to_project> [<name_of_project>] [<port>] [<httpd_daemon>]"
219
+ puts " Serve up the current directory via git instaweb for browsers."
220
+ puts " The default port is 1234 and the httpd_daemon is defaulted to"
221
+ puts " webrick. Other options are 'lighttpd' and 'apache2' (See the"
222
+ puts " git-instaweb man page for more details)"
223
+ puts
224
+ puts " browse [<port>] [<browser>]"
225
+ puts " Browse git repositories published with the 'web' command (see"
226
+ puts " above). This command takes two optional arguments: the first"
227
+ puts " is the port for the local web server (default 9850), the second"
228
+ puts " is the path to your web browser (see man git-web--browse for"
229
+ puts " details)."
230
+ puts
231
+ end
232
+
233
+ class Done < RuntimeError; end
234
+
235
+ def discover(timeout=5)
236
+ waiting_thread = Thread.current
237
+
238
+ dns = DNSSD.browse "_git._tcp" do |reply|
239
+ DNSSD.resolve reply.name, reply.type, reply.domain do |resolve_reply|
240
+ service = GitService.new(reply.name,
241
+ resolve_reply.target,
242
+ resolve_reply.port,
243
+ resolve_reply.text_record['description'].to_s)
244
+ begin
245
+ yield service
246
+ rescue Done
247
+ waiting_thread.run
248
+ end
249
+ end
250
+ end
251
+
252
+ puts "Gathering for up to #{timeout} seconds..."
253
+ sleep timeout
254
+ dns.stop
255
+ end
256
+
257
+ def locate_repo(name)
258
+ found = nil
259
+
260
+ discover do |obj|
261
+ if obj.name == name
262
+ found = obj
263
+ raise Done
264
+ end
265
+ end
266
+
267
+ return found
268
+ end
269
+
270
+ def service_list
271
+ list = Set.new
272
+ discover { |obj| list << obj }
273
+
274
+ return list
275
+ end
276
+
277
+ def browse(*args)
278
+ require "pairjour/browser"
279
+ Browser.new(*args).start
280
+ end
281
+
282
+ def announce_repo(path, name, port, type)
283
+ return unless File.exists?("#{path}/.git")
284
+
285
+ tr = DNSSD::TextRecord.new
286
+ tr['description'] = File.read("#{path}/.git/description") rescue "a git project"
287
+ tr['pairjour'] = 'true' # distinguish instaweb from other HTTP servers
288
+
289
+ DNSSD.register(name, type, 'local', port, tr) do |rr|
290
+ puts "Registered #{name} on port #{port}. Starting service."
291
+ end
292
+ end
293
+
294
+ def announce_git(path, name, port)
295
+ announce_repo(path, name, port, "_git._tcp")
296
+ end
297
+
298
+ def announce_web(path, name, port)
299
+ announce_repo(path, name, port, "_http._tcp")
300
+ end
301
+ end
302
+ end
303
+ end
@@ -0,0 +1,111 @@
1
+ require "webrick"
2
+ require "erb"
3
+ require "set"
4
+ require "thread"
5
+
6
+ module Pairjour
7
+ class Browser
8
+
9
+ def initialize(*args)
10
+ @port = args.shift || 9850
11
+ @browser = args.shift
12
+ @services = Set.new
13
+ @mutex = Mutex.new
14
+ end
15
+
16
+ def start
17
+ DNSSD.browse("_http._tcp") do |reply|
18
+ begin
19
+ DNSSD.resolve reply.name, reply.type, reply.domain do |resolve_reply|
20
+ next unless resolve_reply.text_record['pairjour']
21
+ service = GitService.new(reply.name,
22
+ resolve_reply.target,
23
+ resolve_reply.port,
24
+ resolve_reply.text_record['description'].to_s)
25
+
26
+ @mutex.synchronize do
27
+ if @services.member? service
28
+ @services.delete service
29
+ else
30
+ @services << service
31
+ end
32
+ end
33
+ end
34
+ rescue ArgumentError # usually a jacked DNS text record
35
+ end
36
+ end
37
+
38
+ http = WEBrick::HTTPServer.new(:Port => @port.to_i)
39
+ http.mount_proc("/") { |req, res| index(req, res) }
40
+ http.mount_proc("/style.css") { |req, res| stylesheet(req, res) }
41
+ trap("INT") { http.shutdown }
42
+ t = Thread.new { http.start }
43
+
44
+ url = "http://localhost:#{@port}"
45
+ if @browser
46
+ `git web--browse -b '#{@browser}' http://localhost:9850`
47
+ else
48
+ `git web--browse -c "instaweb.browser" http://localhost:9850`
49
+ end
50
+ t.join
51
+ end
52
+
53
+ def index(req, res)
54
+ res['Content-Type'] = 'text/html'
55
+ res.body = index_html.result(binding)
56
+ end
57
+
58
+ def index_html
59
+ @index_html ||= ERB.new(<<-HTML)
60
+ <html>
61
+ <body>
62
+ <head>
63
+ <link rel="stylesheet" href="/style.css" type="text/css" media="screen"/>
64
+ <title>Browseable Git Repositories</title>
65
+ </head>
66
+ <h1>Browseable Git Repositories</h1>
67
+ <ul>
68
+ <% @mutex.synchronize do %>
69
+ <% @services.map do |s| %>
70
+ <li>
71
+ <a href='http://<%= s.host %>:<%= s.port %>' target="_new">
72
+ <%= s.name %>
73
+ </a>
74
+ <%= s.description unless s.description =~ /^Unnamed repository/ %>
75
+ </li>
76
+ <% end %>
77
+ <% end %>
78
+ </ul>
79
+ </body>
80
+ </html>
81
+ HTML
82
+ end
83
+
84
+ def stylesheet(req, res)
85
+ res['Content-Type'] = 'text/css'
86
+ res.body = css
87
+ end
88
+
89
+ def css
90
+ @css ||= <<-CSS
91
+ body {
92
+ font-family: sans-serif;
93
+ font-size: 12px;
94
+ background-color: #fff;
95
+ }
96
+
97
+ h1 {
98
+ font-size: 20px;
99
+ font-weight: bold;
100
+ }
101
+
102
+ ul {
103
+ border: 1px dashed #999;
104
+ padding: 10 10 10 20;
105
+ background-color: #ccc;
106
+ }
107
+ CSS
108
+ end
109
+ end
110
+
111
+ end
@@ -0,0 +1,56 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{pairjour}
8
+ s.version = "0.1.0.pre1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Taylor luk", "Khoa Nguyne", "Chad Fowler", "Evan Phoenix", "Rich Kilmer", "Phil Hagelberg"]
12
+ s.date = %q{2010-08-30}
13
+ s.default_executable = %q{pairjour}
14
+ s.description = %q{pair programming using bonjour and git, originalted as a fork of gitjour}
15
+ s.email = %q{taylor.luk@idealian.net}
16
+ s.executables = ["pairjour"]
17
+ s.extra_rdoc_files = [
18
+ "README.md"
19
+ ]
20
+ s.files = [
21
+ ".gitignore",
22
+ "History.txt",
23
+ "Manifest.txt",
24
+ "README.md",
25
+ "Rakefile",
26
+ "bin/pairjour",
27
+ "lib/pairjour.rb",
28
+ "lib/pairjour/browser.rb",
29
+ "pairjour.gemspec",
30
+ "test/test_helper.rb",
31
+ "test/test_pairjour.rb"
32
+ ]
33
+ s.homepage = %q{http://github.com/idealian/pairjour}
34
+ s.rdoc_options = ["--charset=UTF-8"]
35
+ s.require_paths = ["lib"]
36
+ s.rubygems_version = %q{1.3.7}
37
+ s.summary = %q{pair programming using bonjour and git}
38
+ s.test_files = [
39
+ "test/test_helper.rb",
40
+ "test/test_pairjour.rb"
41
+ ]
42
+
43
+ if s.respond_to? :specification_version then
44
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
45
+ s.specification_version = 3
46
+
47
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
48
+ s.add_runtime_dependency(%q<dnssd>, ["~> 1.3"])
49
+ else
50
+ s.add_dependency(%q<dnssd>, ["~> 1.3"])
51
+ end
52
+ else
53
+ s.add_dependency(%q<dnssd>, ["~> 1.3"])
54
+ end
55
+ end
56
+
@@ -0,0 +1,3 @@
1
+ require 'test/unit'
2
+ require 'fileutils'
3
+ require File.dirname(__FILE__) + '/../lib/pairjour'
@@ -0,0 +1,23 @@
1
+ require 'net/telnet'
2
+ require File.dirname(__FILE__) + '/test_helper.rb'
3
+
4
+ class TestPairjour < Test::Unit::TestCase
5
+ def test_thread_friendly
6
+ repo = File.dirname(__FILE__) + '/repo'
7
+ port = 3289
8
+ FileUtils.rm_rf repo
9
+ `mkdir -p #{repo}; cd #{repo}; git init`
10
+
11
+ thread = Thread.new do
12
+ Pairjour::Application.send(:serve, repo, 'test', port)
13
+ end
14
+
15
+ sleep 1
16
+ Net::Telnet::new("Host" => "localhost", "Port" => port)
17
+
18
+ thread.kill
19
+ assert_raises(Errno::ECONNREFUSED) do
20
+ Net::Telnet::new("Host" => "localhost", "Port" => port)
21
+ end
22
+ end
23
+ end
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pairjour
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: true
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ - pre1
10
+ version: 0.1.0.pre1
11
+ platform: ruby
12
+ authors:
13
+ - Taylor luk
14
+ - Khoa Nguyne
15
+ - Chad Fowler
16
+ - Evan Phoenix
17
+ - Rich Kilmer
18
+ - Phil Hagelberg
19
+ autorequire:
20
+ bindir: bin
21
+ cert_chain: []
22
+
23
+ date: 2010-08-30 00:00:00 +10:00
24
+ default_executable: pairjour
25
+ dependencies:
26
+ - !ruby/object:Gem::Dependency
27
+ name: dnssd
28
+ prerelease: false
29
+ requirement: &id001 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ~>
33
+ - !ruby/object:Gem::Version
34
+ segments:
35
+ - 1
36
+ - 3
37
+ version: "1.3"
38
+ type: :runtime
39
+ version_requirements: *id001
40
+ description: pair programming using bonjour and git, originalted as a fork of gitjour
41
+ email: taylor.luk@idealian.net
42
+ executables:
43
+ - pairjour
44
+ extensions: []
45
+
46
+ extra_rdoc_files:
47
+ - README.md
48
+ files:
49
+ - .gitignore
50
+ - History.txt
51
+ - Manifest.txt
52
+ - README.md
53
+ - Rakefile
54
+ - bin/pairjour
55
+ - lib/pairjour.rb
56
+ - lib/pairjour/browser.rb
57
+ - pairjour.gemspec
58
+ - test/test_helper.rb
59
+ - test/test_pairjour.rb
60
+ has_rdoc: true
61
+ homepage: http://github.com/idealian/pairjour
62
+ licenses: []
63
+
64
+ post_install_message:
65
+ rdoc_options:
66
+ - --charset=UTF-8
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ none: false
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ segments:
75
+ - 0
76
+ version: "0"
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ none: false
79
+ requirements:
80
+ - - ">"
81
+ - !ruby/object:Gem::Version
82
+ segments:
83
+ - 1
84
+ - 3
85
+ - 1
86
+ version: 1.3.1
87
+ requirements: []
88
+
89
+ rubyforge_project:
90
+ rubygems_version: 1.3.7
91
+ signing_key:
92
+ specification_version: 3
93
+ summary: pair programming using bonjour and git
94
+ test_files:
95
+ - test/test_helper.rb
96
+ - test/test_pairjour.rb