##// END OF EJS Templates
zeroconf: add extension documentation
David Soria Parra -
r7606:e86ca711 default
parent child Browse files
Show More
@@ -1,136 +1,159 b''
1 # zeroconf.py - zeroconf support for Mercurial
1 # zeroconf.py - zeroconf support for Mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of
5 # This software may be used and distributed according to the terms of
6 # the GNU General Public License (version 2), incorporated herein by
6 # the GNU General Public License (version 2), incorporated herein by
7 # reference.
7 # reference.
8
8
9 '''zeroconf support for mercurial repositories
10
11 Zeroconf enabled repositories will be announced in a network without the need
12 to configure a server or a service. They can be discovered without knowing
13 their actual IP address.
14
15 To use the zeroconf extension add the following entry to your hgrc file:
16
17 [extensions]
18 hgext.zeroconf =
19
20 To allow other people to discover your repository using run "hg serve" in your
21 repository.
22
23 $ cd test
24 $ hg serve
25
26 You can discover zeroconf enabled repositories by running "hg paths".
27
28 $ hg paths
29 zc-test = http://example.com:8000/test
30 '''
31
9 import Zeroconf, socket, time, os
32 import Zeroconf, socket, time, os
10 from mercurial import ui
33 from mercurial import ui
11 from mercurial import extensions
34 from mercurial import extensions
12 from mercurial.hgweb import hgweb_mod
35 from mercurial.hgweb import hgweb_mod
13 from mercurial.hgweb import hgwebdir_mod
36 from mercurial.hgweb import hgwebdir_mod
14
37
15 # publish
38 # publish
16
39
17 server = None
40 server = None
18 localip = None
41 localip = None
19
42
20 def getip():
43 def getip():
21 # finds external-facing interface without sending any packets (Linux)
44 # finds external-facing interface without sending any packets (Linux)
22 try:
45 try:
23 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
46 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
24 s.connect(('1.0.0.1', 0))
47 s.connect(('1.0.0.1', 0))
25 ip = s.getsockname()[0]
48 ip = s.getsockname()[0]
26 return ip
49 return ip
27 except:
50 except:
28 pass
51 pass
29
52
30 # Generic method, sometimes gives useless results
53 # Generic method, sometimes gives useless results
31 dumbip = socket.gethostbyaddr(socket.gethostname())[2][0]
54 dumbip = socket.gethostbyaddr(socket.gethostname())[2][0]
32 if not dumbip.startswith('127.'):
55 if not dumbip.startswith('127.'):
33 return dumbip
56 return dumbip
34
57
35 # works elsewhere, but actually sends a packet
58 # works elsewhere, but actually sends a packet
36 try:
59 try:
37 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
60 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
38 s.connect(('1.0.0.1', 1))
61 s.connect(('1.0.0.1', 1))
39 ip = s.getsockname()[0]
62 ip = s.getsockname()[0]
40 return ip
63 return ip
41 except:
64 except:
42 pass
65 pass
43
66
44 return dumbip
67 return dumbip
45
68
46 def publish(name, desc, path, port):
69 def publish(name, desc, path, port):
47 global server, localip
70 global server, localip
48 if not server:
71 if not server:
49 try:
72 try:
50 server = Zeroconf.Zeroconf()
73 server = Zeroconf.Zeroconf()
51 except socket.gaierror:
74 except socket.gaierror:
52 # if we have no internet connection, this can happen.
75 # if we have no internet connection, this can happen.
53 return
76 return
54 ip = getip()
77 ip = getip()
55 localip = socket.inet_aton(ip)
78 localip = socket.inet_aton(ip)
56
79
57 parts = socket.gethostname().split('.')
80 parts = socket.gethostname().split('.')
58 host = parts[0] + ".local"
81 host = parts[0] + ".local"
59
82
60 # advertise to browsers
83 # advertise to browsers
61 svc = Zeroconf.ServiceInfo('_http._tcp.local.',
84 svc = Zeroconf.ServiceInfo('_http._tcp.local.',
62 name + '._http._tcp.local.',
85 name + '._http._tcp.local.',
63 server = host,
86 server = host,
64 port = port,
87 port = port,
65 properties = {'description': desc,
88 properties = {'description': desc,
66 'path': "/" + path},
89 'path': "/" + path},
67 address = localip, weight = 0, priority = 0)
90 address = localip, weight = 0, priority = 0)
68 server.registerService(svc)
91 server.registerService(svc)
69
92
70 # advertise to Mercurial clients
93 # advertise to Mercurial clients
71 svc = Zeroconf.ServiceInfo('_hg._tcp.local.',
94 svc = Zeroconf.ServiceInfo('_hg._tcp.local.',
72 name + '._hg._tcp.local.',
95 name + '._hg._tcp.local.',
73 server = host,
96 server = host,
74 port = port,
97 port = port,
75 properties = {'description': desc,
98 properties = {'description': desc,
76 'path': "/" + path},
99 'path': "/" + path},
77 address = localip, weight = 0, priority = 0)
100 address = localip, weight = 0, priority = 0)
78 server.registerService(svc)
101 server.registerService(svc)
79
102
80 class hgwebzc(hgweb_mod.hgweb):
103 class hgwebzc(hgweb_mod.hgweb):
81 def __init__(self, repo, name=None):
104 def __init__(self, repo, name=None):
82 super(hgwebzc, self).__init__(repo, name)
105 super(hgwebzc, self).__init__(repo, name)
83 name = self.reponame or os.path.basename(repo.root)
106 name = self.reponame or os.path.basename(repo.root)
84 desc = self.repo.ui.config("web", "description", name)
107 desc = self.repo.ui.config("web", "description", name)
85 publish(name, desc, name, int(repo.ui.config("web", "port", 8000)))
108 publish(name, desc, name, int(repo.ui.config("web", "port", 8000)))
86
109
87 class hgwebdirzc(hgwebdir_mod.hgwebdir):
110 class hgwebdirzc(hgwebdir_mod.hgwebdir):
88 def run(self):
111 def run(self):
89 for r, p in self.repos:
112 for r, p in self.repos:
90 u = ui.ui(parentui=self.parentui)
113 u = ui.ui(parentui=self.parentui)
91 u.readconfig(os.path.join(p, '.hg', 'hgrc'))
114 u.readconfig(os.path.join(p, '.hg', 'hgrc'))
92 n = os.path.basename(r)
115 n = os.path.basename(r)
93 publish(n, "hgweb", p, int(u.config("web", "port", 8000)))
116 publish(n, "hgweb", p, int(u.config("web", "port", 8000)))
94 return super(hgwebdirzc, self).run()
117 return super(hgwebdirzc, self).run()
95
118
96 # listen
119 # listen
97
120
98 class listener(object):
121 class listener(object):
99 def __init__(self):
122 def __init__(self):
100 self.found = {}
123 self.found = {}
101 def removeService(self, server, type, name):
124 def removeService(self, server, type, name):
102 if repr(name) in self.found:
125 if repr(name) in self.found:
103 del self.found[repr(name)]
126 del self.found[repr(name)]
104 def addService(self, server, type, name):
127 def addService(self, server, type, name):
105 self.found[repr(name)] = server.getServiceInfo(type, name)
128 self.found[repr(name)] = server.getServiceInfo(type, name)
106
129
107 def getzcpaths():
130 def getzcpaths():
108 server = Zeroconf.Zeroconf()
131 server = Zeroconf.Zeroconf()
109 l = listener()
132 l = listener()
110 browser = Zeroconf.ServiceBrowser(server, "_hg._tcp.local.", l)
133 browser = Zeroconf.ServiceBrowser(server, "_hg._tcp.local.", l)
111 time.sleep(1)
134 time.sleep(1)
112 server.close()
135 server.close()
113 for v in l.found.values():
136 for v in l.found.values():
114 n = v.name[:v.name.index('.')]
137 n = v.name[:v.name.index('.')]
115 n.replace(" ", "-")
138 n.replace(" ", "-")
116 u = "http://%s:%s%s" % (socket.inet_ntoa(v.address), v.port,
139 u = "http://%s:%s%s" % (socket.inet_ntoa(v.address), v.port,
117 v.properties.get("path", "/"))
140 v.properties.get("path", "/"))
118 yield "zc-" + n, u
141 yield "zc-" + n, u
119
142
120 def config(orig, self, section, key, default=None, untrusted=False):
143 def config(orig, self, section, key, default=None, untrusted=False):
121 if section == "paths" and key.startswith("zc-"):
144 if section == "paths" and key.startswith("zc-"):
122 for n, p in getzcpaths():
145 for n, p in getzcpaths():
123 if n == key:
146 if n == key:
124 return p
147 return p
125 return orig(self, section, key, default, untrusted)
148 return orig(self, section, key, default, untrusted)
126
149
127 def configitems(orig, self, section, untrusted=False):
150 def configitems(orig, self, section, untrusted=False):
128 r = orig(self, section, untrusted)
151 r = orig(self, section, untrusted)
129 if section == "paths":
152 if section == "paths":
130 r += getzcpaths()
153 r += getzcpaths()
131 return r
154 return r
132
155
133 extensions.wrapfunction(ui.ui, 'config', config)
156 extensions.wrapfunction(ui.ui, 'config', config)
134 extensions.wrapfunction(ui.ui, 'configitems', configitems)
157 extensions.wrapfunction(ui.ui, 'configitems', configitems)
135 hgweb_mod.hgweb = hgwebzc
158 hgweb_mod.hgweb = hgwebzc
136 hgwebdir_mod.hgwebdir = hgwebdirzc
159 hgwebdir_mod.hgwebdir = hgwebdirzc
General Comments 0
You need to be logged in to leave comments. Login now