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