comparison mercurial/hgweb/hgwebdir_mod.py @ 8371:1bd0fdf4c1ec

hgwebdir: refresh configuration periodically The old default behaviour of hgwebdir was to maintain a list of repositories permanently. This interacted badly with persistent application hosting software such as WSGI containers. If a new repository was published, it would potentially never appear in the top-level list of repositories. This change causes the hgwebdir configuration and list of repositories served to be refreshed periodically (at most every 20 seconds).
author Bryan O'Sullivan <bos@serpentine.com>
date Wed, 13 May 2009 13:30:28 -0700
parents acc202b71619
children 4b798b100c32
comparison
equal deleted inserted replaced
8370:45ed015b524e 8371:1bd0fdf4c1ec
4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com> 4 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
5 # 5 #
6 # This software may be used and distributed according to the terms of the 6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2, incorporated herein by reference. 7 # GNU General Public License version 2, incorporated herein by reference.
8 8
9 import os 9 import os, time
10 from mercurial.i18n import _ 10 from mercurial.i18n import _
11 from mercurial import ui, hg, util, templater 11 from mercurial import ui, hg, util, templater
12 from mercurial import error, encoding 12 from mercurial import error, encoding
13 from common import ErrorResponse, get_mtime, staticfile, paritygen,\ 13 from common import ErrorResponse, get_mtime, staticfile, paritygen,\
14 get_contact, HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR 14 get_contact, HTTP_OK, HTTP_NOT_FOUND, HTTP_SERVER_ERROR
18 18
19 def cleannames(items): 19 def cleannames(items):
20 return [(util.pconvert(name).strip('/'), path) for name, path in items] 20 return [(util.pconvert(name).strip('/'), path) for name, path in items]
21 21
22 class hgwebdir(object): 22 class hgwebdir(object):
23 refreshinterval = 20
23 24
24 def __init__(self, conf, baseui=None): 25 def __init__(self, conf, baseui=None):
25 26 self.conf = conf
26 if baseui: 27 self.baseui = baseui
27 self.ui = baseui.copy() 28 self.lastrefresh = 0
29 self.refresh()
30
31 def refresh(self):
32 if self.lastrefresh + self.refreshinterval > time.time():
33 return
34
35 if self.baseui:
36 self.ui = self.baseui.copy()
28 else: 37 else:
29 self.ui = ui.ui() 38 self.ui = ui.ui()
30 self.ui.setconfig('ui', 'report_untrusted', 'off') 39 self.ui.setconfig('ui', 'report_untrusted', 'off')
31 self.ui.setconfig('ui', 'interactive', 'off') 40 self.ui.setconfig('ui', 'interactive', 'off')
32 41
33 if isinstance(conf, (list, tuple)): 42 if isinstance(self.conf, (list, tuple)):
34 self.repos = cleannames(conf) 43 self.repos = cleannames(conf)
35 elif isinstance(conf, dict): 44 elif isinstance(self.conf, dict):
36 self.repos = sorted(cleannames(conf.items())) 45 self.repos = sorted(cleannames(self.conf.items()))
37 else: 46 else:
38 self.ui.readconfig(conf, remap={'paths': 'hgweb-paths'}, trust=True) 47 self.ui.readconfig(self.conf, remap={'paths': 'hgweb-paths'}, trust=True)
39 self.repos = [] 48 self.repos = []
40 49
41 self.motd = self.ui.config('web', 'motd') 50 self.motd = self.ui.config('web', 'motd')
42 self.style = self.ui.config('web', 'style', 'paper') 51 self.style = self.ui.config('web', 'style', 'paper')
43 self.stripecount = self.ui.config('web', 'stripes', 1) 52 self.stripecount = self.ui.config('web', 'stripes', 1)
75 if name.startswith(prefix): 84 if name.startswith(prefix):
76 name = name[len(prefix):] 85 name = name[len(prefix):]
77 self.repos.append((name.lstrip(os.sep), repo)) 86 self.repos.append((name.lstrip(os.sep), repo))
78 87
79 self.repos.sort() 88 self.repos.sort()
89 self.lastrefresh = time.time()
80 90
81 def run(self): 91 def run(self):
82 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."): 92 if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."):
83 raise RuntimeError("This function is only intended to be called while running as a CGI script.") 93 raise RuntimeError("This function is only intended to be called while running as a CGI script.")
84 import mercurial.hgweb.wsgicgi as wsgicgi 94 import mercurial.hgweb.wsgicgi as wsgicgi
109 return True 119 return True
110 120
111 return False 121 return False
112 122
113 def run_wsgi(self, req): 123 def run_wsgi(self, req):
114
115 try: 124 try:
116 try: 125 try:
126 self.refresh()
117 127
118 virtual = req.env.get("PATH_INFO", "").strip('/') 128 virtual = req.env.get("PATH_INFO", "").strip('/')
119 tmpl = self.templater(req) 129 tmpl = self.templater(req)
120 ctype = tmpl('mimetype', encoding=encoding.encoding) 130 ctype = tmpl('mimetype', encoding=encoding.encoding)
121 ctype = templater.stringify(ctype) 131 ctype = templater.stringify(ctype)
243 rows.reverse() 253 rows.reverse()
244 for key, row in rows: 254 for key, row in rows:
245 row['parity'] = parity.next() 255 row['parity'] = parity.next()
246 yield row 256 yield row
247 257
258 self.refresh()
248 sortable = ["name", "description", "contact", "lastchange"] 259 sortable = ["name", "description", "contact", "lastchange"]
249 sortcolumn, descending = sortdefault 260 sortcolumn, descending = sortdefault
250 if 'sort' in req.form: 261 if 'sort' in req.form:
251 sortcolumn = req.form['sort'][0] 262 sortcolumn = req.form['sort'][0]
252 descending = sortcolumn.startswith('-') 263 descending = sortcolumn.startswith('-')
258 sort = [("sort_%s" % column, 269 sort = [("sort_%s" % column,
259 "%s%s" % ((not descending and column == sortcolumn) 270 "%s%s" % ((not descending and column == sortcolumn)
260 and "-" or "", column)) 271 and "-" or "", column))
261 for column in sortable] 272 for column in sortable]
262 273
274 self.refresh()
263 if self._baseurl is not None: 275 if self._baseurl is not None:
264 req.env['SCRIPT_NAME'] = self._baseurl 276 req.env['SCRIPT_NAME'] = self._baseurl
265 277
266 return tmpl("index", entries=entries, subdir=subdir, 278 return tmpl("index", entries=entries, subdir=subdir,
267 sortcolumn=sortcolumn, descending=descending, 279 sortcolumn=sortcolumn, descending=descending,