Skip to content

Commit 3fc84b3

Browse files
authored
serve hg more statically (#579)
* serve hg more statically * include a helpful readme
1 parent e053b53 commit 3fc84b3

File tree

3 files changed

+97
-19
lines changed

3 files changed

+97
-19
lines changed

salt/hg/config/hg.apache.conf.jinja

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@
2424
Require all granted
2525
</Directory>
2626

27+
Alias /icons/ "/usr/share/apache2/icons/"
28+
29+
<Directory "/usr/share/apache2/icons">
30+
Options FollowSymlinks
31+
AllowOverride None
32+
Require all granted
33+
</Directory>
34+
2735
# The lookup app is better run with few processes, since it uses a cache.
2836
WSGIDaemonProcess hglookup user=hg group=hg \
2937
threads=2 processes=1 maximum-requests=1000 \
@@ -36,38 +44,38 @@
3644

3745
WSGIScriptAlias /lookup /srv/hg/wsgi/lookup.wsgi
3846

39-
# We need enough threads/processes for many long-running clones to run in
40-
# parallel (e.g. for buildbots)
41-
# NOTE: maximum-requests suspected to corrupt responses, see
42-
# http://mercurial.selenic.com/bts/issue2595
43-
WSGIDaemonProcess hgweb user=hg group=hg \
44-
threads=2 processes=6 maximum-requests=1024 inactivity-timeout=300 \
45-
display-name=hgweb
46-
WSGIProcessGroup hgweb
47+
# A lightweight standin for revision app to maintain support for /lookup
48+
WSGIDaemonProcess hgrev user=hg group=hg \
49+
threads=2 processes=4 maximum-requests=1000 \
50+
display-name=hgrev
51+
# The Location hack ensures the lookup app is run within
52+
# this process group
53+
<LocationMatch "^(.*)/rev/([A-Fa-f0-9]{12,40})/?">
54+
WSGIProcessGroup hgrev
55+
</LocationMatch>
56+
57+
WSGIScriptAliasMatch "^(.*)/rev/([A-Fa-f0-9]{12,40})/?" /srv/hg/wsgi/rev.wsgi
4758

48-
WSGIScriptAlias / /srv/hg/wsgi/python.wsgi
59+
# Staticly serve hg repos over HTTP
60+
DocumentRoot /srv/hg/hg-static/
61+
<Directory /srv/hg/hg-static>
62+
Options Indexes FollowSymlinks
63+
IndexOptions FancyIndexing SuppressColumnSorting
64+
HeaderName README.html
65+
Require all granted
66+
</Directory>
4967

5068
ErrorDocument 410 /410.html
5169
RedirectMatch gone "/cpython/annotate/.*/NEWS$"
5270
RedirectMatch gone "/cpython-fullhistory/annotate/.*/NEWS$"
5371

54-
<Directory /srv/hg/wsgi>
55-
Require all granted
56-
</Directory>
57-
5872
# Static files: logo, CSS, favicon... (wired to .../static/)
5973
# This is optional but a bit faster than letting Mercurial serve the files
6074
# NOTE: needs to be changed if hg gets wired to another dir or python
6175
# version
6276

63-
AliasMatch /static/([^/]*) /usr/share/mercurial/templates/static/$1
64-
<Directory /usr/share/mercurial/templates/static/>
65-
Require all granted
66-
</Directory>
67-
6877
<Location /_server-status>
6978
SetHandler server-status
70-
7179
Require ip 127.0.0.1
7280
</Location>
7381
</VirtualHost>

salt/hg/files/hg/src/hgrev.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# hgrev.py
2+
#
3+
# Dump a revision of a given hg repo as a patch
4+
#
5+
# Written by Ee Durbin, 2025.
6+
7+
import os
8+
import shlex
9+
import subprocess
10+
from wsgiref.simple_server import make_server
11+
12+
13+
class hgrev(object):
14+
def __init__(self, verbose=False):
15+
self.verbose = verbose
16+
17+
def successful_response(self, response, contents):
18+
headers = [("Content-Type", "text/plain")]
19+
response("200 OK", headers)
20+
return [contents.encode()]
21+
22+
def failed_response(self, response, detail=""):
23+
headers = [("Content-Type", "text/plain")]
24+
response("404 Not Found", headers)
25+
return [
26+
detail.encode(),
27+
"\nUsage: path/to/hg/repo/rev/HGHEXNODE (12 or 40 hex characters)\n".encode(),
28+
]
29+
30+
def __call__(self, env, response):
31+
node = env.get("SCRIPT_NAME", "").strip("/")
32+
repository = os.path.dirname(node).rstrip("/rev")
33+
rev = os.path.basename(node)
34+
35+
hg_repo = os.path.join("/srv/hg/repos", repository, ".hg")
36+
if not os.path.exists(hg_repo):
37+
return self.failed_response(
38+
response,
39+
detail=f"repo not found ({repository}) ({rev})",
40+
)
41+
42+
command = ["hg", "log", "-v", "-p", "-r", shlex.quote(rev)]
43+
44+
try:
45+
result = subprocess.run(
46+
command, cwd=hg_repo, capture_output=True, text=True, shell=False
47+
)
48+
except Exception as e:
49+
return self.failed_response(response, detail=str(e))
50+
51+
return self.successful_response(response, result.stdout)
52+
53+
54+
if __name__ == "__main__":
55+
application = hgrev(verbose=True)
56+
57+
httpd = make_server("", 8124, application)
58+
sa = httpd.socket.getsockname()
59+
print("Serving HTTP on", sa[0], "port", sa[1], "...")
60+
httpd.serve_forever()

salt/hg/files/hg/wsgi/rev.wsgi

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import os
2+
import sys
3+
4+
home = os.path.expanduser('~hg')
5+
6+
sys.path.insert(0, os.path.join(home, 'src'))
7+
8+
from hgrev import hgrev
9+
10+
application = hgrev()

0 commit comments

Comments
 (0)