Patchwork hgwebdir: add support for explicit index files

login
register
mail settings
Submitter Matt Harbison
Date March 6, 2017, 5:35 a.m.
Message ID <3ff768dd4fe9faeca8cc.1488778542@Envy>
Download mbox | patch
Permalink /patch/18915/
State Changes Requested
Delegated to: Yuya Nishihara
Headers show

Comments

Matt Harbison - March 6, 2017, 5:35 a.m.
# HG changeset patch
# User Matt Harbison <matt_harbison@yahoo.com>
# Date 1488770552 18000
#      Sun Mar 05 22:22:32 2017 -0500
# Node ID 3ff768dd4fe9faeca8ccba040fa6a085584063aa
# Parent  023123a67b54d98bd4c8ef8ac8cfd7a96247519e
hgwebdir: add support for explicit index files

This is useful for when repositories are nested in --web-conf, and in the future
with hosted subrepositories.  The previous behavior was only to render an index
at each virtual directory.  There is now an explicit index.html for each virtual
directory.  Additionally, there is now an explicit index.html for every
repository directory with a nested repository below it.  This seems more
consistent with each virtual directory hosting an index, and more discoverable
than to only have an index for a directory that directly hosts a nested
repository.

I couldn't figure out how to close the loop and provide one in each directory
without a deeper nested repository, without blocking a committed 'index.html'.
Keeping that seems better than rendering an empty index.
Yuya Nishihara - March 10, 2017, 4:56 p.m.
On Mon, 06 Mar 2017 00:35:42 -0500, Matt Harbison wrote:
> # HG changeset patch
> # User Matt Harbison <matt_harbison@yahoo.com>
> # Date 1488770552 18000
> #      Sun Mar 05 22:22:32 2017 -0500
> # Node ID 3ff768dd4fe9faeca8ccba040fa6a085584063aa
> # Parent  023123a67b54d98bd4c8ef8ac8cfd7a96247519e
> hgwebdir: add support for explicit index files
> 
> This is useful for when repositories are nested in --web-conf, and in the future
> with hosted subrepositories.  The previous behavior was only to render an index
> at each virtual directory.  There is now an explicit index.html for each virtual
> directory.  Additionally, there is now an explicit index.html for every
> repository directory with a nested repository below it.  This seems more
> consistent with each virtual directory hosting an index, and more discoverable
> than to only have an index for a directory that directly hosts a nested
> repository.

I prefer 'index', 'repositories', 'list', etc. than 'index.html' for
consistency with the other method names.

> --- a/mercurial/hgweb/hgwebdir_mod.py
> +++ b/mercurial/hgweb/hgwebdir_mod.py
> @@ -254,34 +254,50 @@
>                  return []
>  
>              # top-level index
> -            elif not virtual:
> +            elif not virtual or virtual == 'index.html':
> +                if virtual:
> +                    req.env['PATH_INFO'] = '/'

Maybe it isn't nice to modify PATH_INFO. Can we pass it as an argument
 of makeindex() ?

> +            def checkrepos():
> +                if not virtual.endswith('/index.html'):
> +                    return True
> +
> +                pdir = virtual[:-len('index.html')]
> +                return not any(r.startswith(pdir) for r in repos)

This seems a little confusing. Instead, can we test the virtual path and call
makeindex() directly here?

We might want to show hgweb page if there's a repo named 'index.html'. Other
methods such as '/rev' can be overridden by repository names.

Patch

diff --git a/mercurial/hgweb/hgwebdir_mod.py b/mercurial/hgweb/hgwebdir_mod.py
--- a/mercurial/hgweb/hgwebdir_mod.py
+++ b/mercurial/hgweb/hgwebdir_mod.py
@@ -254,34 +254,50 @@ 
                 return []
 
             # top-level index
-            elif not virtual:
+            elif not virtual or virtual == 'index.html':
+                if virtual:
+                    req.env['PATH_INFO'] = '/'
+
                 req.respond(HTTP_OK, ctype)
                 return self.makeindex(req, tmpl)
 
             # nested indexes and hgwebs
 
             repos = dict(self.repos)
-            virtualrepo = virtual
-            while virtualrepo:
-                real = repos.get(virtualrepo)
-                if real:
-                    req.env['REPO_NAME'] = virtualrepo
-                    try:
-                        # ensure caller gets private copy of ui
-                        repo = hg.repository(self.ui.copy(), real)
-                        return hgweb_mod.hgweb(repo).run_wsgi(req)
-                    except IOError as inst:
-                        msg = inst.strerror
-                        raise ErrorResponse(HTTP_SERVER_ERROR, msg)
-                    except error.RepoError as inst:
-                        raise ErrorResponse(HTTP_SERVER_ERROR, str(inst))
 
-                up = virtualrepo.rfind('/')
-                if up < 0:
-                    break
-                virtualrepo = virtualrepo[:up]
+            def checkrepos():
+                if not virtual.endswith('/index.html'):
+                    return True
+
+                pdir = virtual[:-len('index.html')]
+                return not any(r.startswith(pdir) for r in repos)
+
+            if checkrepos():
+                virtualrepo = virtual
+                while virtualrepo:
+                    real = repos.get(virtualrepo)
+                    if real:
+                        req.env['REPO_NAME'] = virtualrepo
+                        try:
+                            # ensure caller gets private copy of ui
+                            repo = hg.repository(self.ui.copy(), real)
+                            return hgweb_mod.hgweb(repo).run_wsgi(req)
+                        except IOError as inst:
+                            msg = inst.strerror
+                            raise ErrorResponse(HTTP_SERVER_ERROR, msg)
+                        except error.RepoError as inst:
+                            raise ErrorResponse(HTTP_SERVER_ERROR, str(inst))
+
+                    up = virtualrepo.rfind('/')
+                    if up < 0:
+                        break
+                    virtualrepo = virtualrepo[:up]
 
             # browse subdirectories
+            if virtual.endswith('/index.html'):
+                virtual = virtual[:-len('/index.html')]
+                req.env['PATH_INFO'] = '/' + virtual
+
             subdir = virtual + '/'
             if [r for r in repos if r.startswith(subdir)]:
                 req.respond(HTTP_OK, ctype)
diff --git a/tests/test-hgwebdir.t b/tests/test-hgwebdir.t
--- a/tests/test-hgwebdir.t
+++ b/tests/test-hgwebdir.t
@@ -1566,6 +1566,96 @@ 
   /b/
   /c/
   
+  $ killdaemons.py
+  $ cat > paths.conf << EOF
+  > [paths]
+  > /dir1/a_repo = $root/a
+  > /dir1/a_repo/b_repo = $root/b
+  > EOF
+  $ hg serve -p $HGPORT1 -d --pid-file hg.pid --webdir-conf paths.conf
+  $ cat hg.pid >> $DAEMON_PIDS
+
+  $ echo 'index file' > $root/a/index.html
+  $ hg --cwd $root/a ci -Am 'add index file'
+  adding index.html
+
+  $ get-with-headers.py localhost:$HGPORT1 '' | grep 'a_repo'
+  <td><a href="/dir1/a_repo/">dir1/a_repo</a></td>
+  <a href="/dir1/a_repo/atom-log" title="subscribe to repository atom feed">
+  <td><a href="/dir1/a_repo/b_repo/">dir1/a_repo/b_repo</a></td>
+  <a href="/dir1/a_repo/b_repo/atom-log" title="subscribe to repository atom feed">
+
+  $ get-with-headers.py localhost:$HGPORT1 'index.html' | grep 'a_repo'
+  <td><a href="/dir1/a_repo/">dir1/a_repo</a></td>
+  <a href="/dir1/a_repo/atom-log" title="subscribe to repository atom feed">
+  <td><a href="/dir1/a_repo/b_repo/">dir1/a_repo/b_repo</a></td>
+  <a href="/dir1/a_repo/b_repo/atom-log" title="subscribe to repository atom feed">
+
+  $ get-with-headers.py localhost:$HGPORT1 'dir1' | grep 'a_repo'
+  <td><a href="/dir1/a_repo/">a_repo</a></td>
+  <a href="/dir1/a_repo/atom-log" title="subscribe to repository atom feed">
+  <td><a href="/dir1/a_repo/b_repo/">a_repo/b_repo</a></td>
+  <a href="/dir1/a_repo/b_repo/atom-log" title="subscribe to repository atom feed">
+
+  $ get-with-headers.py localhost:$HGPORT1 'dir1/index.html' | grep 'a_repo'
+  <td><a href="/dir1/a_repo/">a_repo</a></td>
+  <a href="/dir1/a_repo/atom-log" title="subscribe to repository atom feed">
+  <td><a href="/dir1/a_repo/b_repo/">a_repo/b_repo</a></td>
+  <a href="/dir1/a_repo/b_repo/atom-log" title="subscribe to repository atom feed">
+
+  $ get-with-headers.py localhost:$HGPORT1 'dir1' | grep 'a_repo'
+  <td><a href="/dir1/a_repo/">a_repo</a></td>
+  <a href="/dir1/a_repo/atom-log" title="subscribe to repository atom feed">
+  <td><a href="/dir1/a_repo/b_repo/">a_repo/b_repo</a></td>
+  <a href="/dir1/a_repo/b_repo/atom-log" title="subscribe to repository atom feed">
+
+  $ get-with-headers.py localhost:$HGPORT1 'dir1/index.html' | grep 'a_repo'
+  <td><a href="/dir1/a_repo/">a_repo</a></td>
+  <a href="/dir1/a_repo/atom-log" title="subscribe to repository atom feed">
+  <td><a href="/dir1/a_repo/b_repo/">a_repo/b_repo</a></td>
+  <a href="/dir1/a_repo/b_repo/atom-log" title="subscribe to repository atom feed">
+
+  $ get-with-headers.py localhost:$HGPORT1 'dir1/a_repo' | grep 'a_repo'
+  <link rel="icon" href="/dir1/a_repo/static/hgicon.png" type="image/png" />
+  <link rel="stylesheet" href="/dir1/a_repo/static/style-paper.css" type="text/css" />
+  <script type="text/javascript" src="/dir1/a_repo/static/mercurial.js"></script>
+  <title>dir1/a_repo: log</title>
+     href="/dir1/a_repo/atom-log" title="Atom feed for dir1/a_repo" />
+     href="/dir1/a_repo/rss-log" title="RSS feed for dir1/a_repo" />
+  <img src="/dir1/a_repo/static/hglogo.png" alt="mercurial" /></a>
+  <li><a href="/dir1/a_repo/graph/tip">graph</a></li>
+  <li><a href="/dir1/a_repo/tags">tags</a></li>
+  <li><a href="/dir1/a_repo/bookmarks">bookmarks</a></li>
+  <li><a href="/dir1/a_repo/branches">branches</a></li>
+  <li><a href="/dir1/a_repo/rev/tip">changeset</a></li>
+  <li><a href="/dir1/a_repo/file/tip">browse</a></li>
+   <li><a href="/dir1/a_repo/help">help</a></li>
+  <a href="/dir1/a_repo/atom-log" title="subscribe to atom feed">
+  <img class="atom-logo" src="/dir1/a_repo/static/feed-icon-14x14.png" alt="atom feed" />
+  <h2 class="breadcrumb"><a href="/">Mercurial</a> &gt; <a href="/dir1">dir1</a> &gt; <a href="/dir1/a_repo">a_repo</a> </h2>
+  <form class="search" action="/dir1/a_repo/log">
+  number or hash, or <a href="/dir1/a_repo/help/revsets">revset expression</a>.</div>
+  <a href="/dir1/a_repo/shortlog/tip?revcount=30">less</a>
+  <a href="/dir1/a_repo/shortlog/tip?revcount=120">more</a>
+  | rev 1: <a href="/dir1/a_repo/shortlog/8580ff50825a">(0)</a> <a href="/dir1/a_repo/shortlog/tip">tip</a> 
+     <a href="/dir1/a_repo/rev/c096a83eb8a4">add index file</a>
+     <a href="/dir1/a_repo/rev/8580ff50825a">a</a>
+  <a href="/dir1/a_repo/shortlog/tip?revcount=30">less</a>
+  <a href="/dir1/a_repo/shortlog/tip?revcount=120">more</a>
+  | rev 1: <a href="/dir1/a_repo/shortlog/8580ff50825a">(0)</a> <a href="/dir1/a_repo/shortlog/tip">tip</a> 
+              '/dir1/a_repo/shortlog/%next%',
+
+  $ get-with-headers.py localhost:$HGPORT1 'dir1/a_repo/index.html' | grep 'a_repo'
+  <h2 class="breadcrumb"><a href="/">Mercurial</a> &gt; <a href="/dir1">dir1</a> &gt; <a href="/dir1/a_repo">a_repo</a> </h2>
+  <td><a href="/dir1/a_repo/b_repo/">b_repo</a></td>
+  <a href="/dir1/a_repo/b_repo/atom-log" title="subscribe to repository atom feed">
+
+  $ get-with-headers.py localhost:$HGPORT1 'dir1/a_repo/raw-file/tip/index.html'
+  200 Script output follows
+  
+  index file
+
+  $ killdaemons.py
 
 paths errors 1