pairjour 0.1.0.pre1

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.
@@ -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