diff --git a/salt/hg/config/hg.apache.conf.jinja b/salt/hg/config/hg.apache.conf.jinja
index c11bbfb5..16cb7433 100644
--- a/salt/hg/config/hg.apache.conf.jinja
+++ b/salt/hg/config/hg.apache.conf.jinja
@@ -24,6 +24,14 @@
Require all granted
+ Alias /icons/ "/usr/share/apache2/icons/"
+
+
+ Options FollowSymlinks
+ AllowOverride None
+ Require all granted
+
+
# The lookup app is better run with few processes, since it uses a cache.
WSGIDaemonProcess hglookup user=hg group=hg \
threads=2 processes=1 maximum-requests=1000 \
@@ -36,38 +44,38 @@
WSGIScriptAlias /lookup /srv/hg/wsgi/lookup.wsgi
- # We need enough threads/processes for many long-running clones to run in
- # parallel (e.g. for buildbots)
- # NOTE: maximum-requests suspected to corrupt responses, see
- # http://mercurial.selenic.com/bts/issue2595
- WSGIDaemonProcess hgweb user=hg group=hg \
- threads=2 processes=6 maximum-requests=1024 inactivity-timeout=300 \
- display-name=hgweb
- WSGIProcessGroup hgweb
+ # A lightweight standin for revision app to maintain support for /lookup
+ WSGIDaemonProcess hgrev user=hg group=hg \
+ threads=2 processes=4 maximum-requests=1000 \
+ display-name=hgrev
+ # The Location hack ensures the lookup app is run within
+ # this process group
+
+ WSGIProcessGroup hgrev
+
+
+ WSGIScriptAliasMatch "^(.*)/rev/([A-Fa-f0-9]{12,40})/?" /srv/hg/wsgi/rev.wsgi
- WSGIScriptAlias / /srv/hg/wsgi/python.wsgi
+ # Staticly serve hg repos over HTTP
+ DocumentRoot /srv/hg/hg-static/
+
+ Options Indexes FollowSymlinks
+ IndexOptions FancyIndexing SuppressColumnSorting
+ HeaderName README.html
+ Require all granted
+
ErrorDocument 410 /410.html
RedirectMatch gone "/cpython/annotate/.*/NEWS$"
RedirectMatch gone "/cpython-fullhistory/annotate/.*/NEWS$"
-
- Require all granted
-
-
# Static files: logo, CSS, favicon... (wired to .../static/)
# This is optional but a bit faster than letting Mercurial serve the files
# NOTE: needs to be changed if hg gets wired to another dir or python
# version
- AliasMatch /static/([^/]*) /usr/share/mercurial/templates/static/$1
-
- Require all granted
-
-
SetHandler server-status
-
Require ip 127.0.0.1
diff --git a/salt/hg/files/hg/src/hgrev.py b/salt/hg/files/hg/src/hgrev.py
new file mode 100644
index 00000000..36599e65
--- /dev/null
+++ b/salt/hg/files/hg/src/hgrev.py
@@ -0,0 +1,60 @@
+# hgrev.py
+#
+# Dump a revision of a given hg repo as a patch
+#
+# Written by Ee Durbin, 2025.
+
+import os
+import shlex
+import subprocess
+from wsgiref.simple_server import make_server
+
+
+class hgrev(object):
+ def __init__(self, verbose=False):
+ self.verbose = verbose
+
+ def successful_response(self, response, contents):
+ headers = [("Content-Type", "text/plain")]
+ response("200 OK", headers)
+ return [contents.encode()]
+
+ def failed_response(self, response, detail=""):
+ headers = [("Content-Type", "text/plain")]
+ response("404 Not Found", headers)
+ return [
+ detail.encode(),
+ "\nUsage: path/to/hg/repo/rev/HGHEXNODE (12 or 40 hex characters)\n".encode(),
+ ]
+
+ def __call__(self, env, response):
+ node = env.get("SCRIPT_NAME", "").strip("/")
+ repository = os.path.dirname(node).rstrip("/rev")
+ rev = os.path.basename(node)
+
+ hg_repo = os.path.join("/srv/hg/repos", repository, ".hg")
+ if not os.path.exists(hg_repo):
+ return self.failed_response(
+ response,
+ detail=f"repo not found ({repository}) ({rev})",
+ )
+
+ command = ["hg", "log", "-v", "-p", "-r", shlex.quote(rev)]
+
+ try:
+ result = subprocess.run(
+ command, cwd=hg_repo, capture_output=True, text=True, shell=False
+ )
+ except Exception as e:
+ return self.failed_response(response, detail=str(e))
+
+ return self.successful_response(response, result.stdout)
+
+
+if __name__ == "__main__":
+ application = hgrev(verbose=True)
+
+ httpd = make_server("", 8124, application)
+ sa = httpd.socket.getsockname()
+ print("Serving HTTP on", sa[0], "port", sa[1], "...")
+ httpd.serve_forever()
diff --git a/salt/hg/files/hg/wsgi/rev.wsgi b/salt/hg/files/hg/wsgi/rev.wsgi
new file mode 100644
index 00000000..1217018f
--- /dev/null
+++ b/salt/hg/files/hg/wsgi/rev.wsgi
@@ -0,0 +1,10 @@
+import os
+import sys
+
+home = os.path.expanduser('~hg')
+
+sys.path.insert(0, os.path.join(home, 'src'))
+
+from hgrev import hgrev
+
+application = hgrev()