/
gitjour.rb
183 lines (150 loc) · 5.12 KB
/
gitjour.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
require 'rubygems'
require 'dnssd'
require 'set'
Thread.abort_on_exception = true
module Gitjour
VERSION = "6.3.0"
GitService = Struct.new(:name, :host, :port, :description)
class Application
class << self
def run(*args)
case args.shift
when "list"
list
when "clone"
clone(*args)
when "serve"
serve(*args)
when "remote"
remote(*args)
else
help
end
end
private
def list
service_list.each do |service|
puts "=== #{service.name} on #{service.host}:#{service.port} ==="
puts " gitjour clone #{service.name}"
if service.description != '' && service.description !~ /^Unnamed repository/
puts " #{service.description}"
end
puts
end
end
def clone(repository_name, *rest)
dir = rest.shift || repository_name
if File.exists?(dir)
exit_with! "ERROR: Clone directory '#{dir}' already exists."
end
puts "Cloning '#{repository_name}' into directory '#{dir}'..."
unless service = locate_repo(repository_name)
exit_with! "ERROR: Unable to find project named '#{repository_name}'"
end
puts "Connecting to #{service.host}:#{service.port}"
system "git clone git://#{service.host}:#{service.port}/ #{dir}/"
end
def remote(repository_name, *rest)
dir = rest.shift || repository_name
service = locate_repo repository_name
system "git remote add #{dir} git://#{service.host}:#{service.port}/"
end
def serve(path=Dir.pwd, *rest)
path = File.expand_path(path)
name = rest.shift || File.basename(path)
port = rest.shift || 9418
# If the name starts with ^, then don't apply the prefix
if name[0] == ?^
name = name[1..-1]
else
prefix = `git config --get gitjour.prefix`.chomp
prefix = ENV["USER"] if prefix.empty?
name = [prefix, name].compact.join("-")
end
if File.exists?("#{path}/.git")
announce_repo(path, name, port.to_i)
else
Dir["#{path}/*"].each do |dir|
if File.directory?(dir)
name = File.basename(dir)
announce_repo(dir, name, 9418)
end
end
end
`git-daemon --verbose --export-all --port=#{port} --base-path=#{path} --base-path-relaxed`
end
def help
puts "Gitjour #{Gitjour::VERSION::STRING}"
puts "Serve up and use git repositories via Bonjour/DNSSD."
puts "\nUsage: gitjour <command> [args]"
puts
puts " list"
puts " Lists available repositories."
puts
puts " clone <project> [<directory>]"
puts " Clone a gitjour served repository."
puts
puts " serve <path_to_project> [<name_of_project>] [<port>] or"
puts " <path_to_projects>"
puts " Serve up the current directory or projects via gitjour."
puts
puts " The name of your project is automatically prefixed with"
puts " `git config --get gitjour.prefix` or your username (preference"
puts " in that order). If you don't want a prefix, put a ^ on the front"
puts " of the name_of_project (the ^ is removed before announcing)."
puts
puts " remote <project> [<name>]"
puts " Add a Bonjour remote into your current repository."
puts " Optionally pass name to not use pwd."
puts
end
def exit_with!(message)
STDERR.puts message
exit!
end
class Done < RuntimeError; end
def discover(timeout=5)
waiting_thread = Thread.current
dns = DNSSD.browse "_git._tcp" do |reply|
DNSSD.resolve reply.name, reply.type, reply.domain do |resolve_reply|
service = GitService.new(reply.name,
resolve_reply.target,
resolve_reply.port,
resolve_reply.text_record['description'].to_s)
begin
yield service
rescue Done
waiting_thread.run
end
end
end
puts "Gathering for up to #{timeout} seconds..."
sleep timeout
dns.stop
end
def locate_repo(name)
found = nil
discover do |obj|
if obj.name == name
found = obj
raise Done
end
end
return found
end
def service_list
list = Set.new
discover { |obj| list << obj }
return list
end
def announce_repo(path, name, port)
return unless File.exists?("#{path}/.git")
tr = DNSSD::TextRecord.new
tr['description'] = File.read("#{path}/.git/description") rescue "a git project"
DNSSD.register(name, "_git._tcp", 'local', port, tr.encode) do |rr|
puts "Registered #{name} on port #{port}. Starting service."
end
end
end
end
end