Configuring HTTP

Sam Hart

2007-05-24 15:04:59

Setting up the Hg http interface


As I said before, I used SVN via WebDAV and it was pretty painless once I got it going. Thus, I want Hg to behave exactly the same way when I do my pushes and pulls. Hg doesn't use WebDAV (at least, if it does, I didn't look deep enough into the documentation to figure out how to set it up), but it does come with a handy CGI script for giving you the same basic functionality.

Configuring hgwebdir.cgi


If you're only running one repo, there is a script called hgweb.cgi which is easy to configure and will handle your needs. However, since I run multiple repos, I decided to use another script called hgwebdir.cgi that serves up multiple Hg repos in one web-interface.

hgwebdir.cgi takes an external configuration file that defines the repos it will monitor. There are two ways you can configure this file for the repos.

The first is to use the [collections] directive which auto-magickally determines all of your Hg repos based upon some common root directory. For example, let's say that all your repos are under /var/repos:

/var/repos
projecta/
projectb/
projectc/foo

You would then place the following in your hgwebdir.cgi configuration file if you wanted to use the [collections] directive:

[collections]
/var/repos = /var/repos

This configuration file would make your repos available online as "projecta", "projectb" and "projectc/foo".

However, if your repos are not under some common directory, or if maybe there's other items that aren't repos alongside your repos, then you can use the [paths] directive to itemize each one:

[paths]
projecta = /home/fred/hg/projecta/
projectb = /var/repo/

Whichever you do, save the file (the name doesn't matter, I just used hgweb.config) and edit the line in hgwebdir.cgi to point to this newly created configuration file. For example:

def make_web_app():
return hgwebdir("/etc/hgweb/hgweb.config")


Configuring your webserver


Technically speaking, you're already set. Just stick the hgwebdir.cgi file someplace where CGI scripts can be executed and point your browser at it. However, at this point you can't push repository changes via this web interface. Additionally, I kind of wanted the URLs to look cleaner.

Clean URLs


You may be fine handing out repository URLs like http://someurl.com/cgi-bin/hgwebdir.cgi?mf=b22511d1eb56;path=/, but I'm not. I want my repository to have URLs that are clean as possible. So, I make sure mod_rewrite is enabled in my server (a2enmod rewrite, if you're running Apache2) and add the following to my Apache2 configuration entry on my hgwebdir.cgi:

<IfModule mod_rewrite.c>
RewriteEngine on
RewriteRule ^/(.*) /hgwebdir.cgi/$1
</IfModule>


User Authentication


Next up, I want users to have the ability to view and clone the repository anonymously, but need to be authenticated in order to push back to the server. Additionally, I want a central place for the htpasswd file (you could do this on a per-project basis, but I'll explain why you don't want to in a bit). So, I add the following to my Apache2 configuration entry on my hgwebdir.cgi:

AuthUserFile /etc/hg/htpasswd
AuthName "Dev Repo"
AuthType Basic
<Limit POST PUT>
Require valid-user
</Limit>

The <Limit> segment is the magic that allows us to have anonymous access to the repository but in order to push you must be authenticated.

Note that in my example here we're using "AuthType Basic", which is probably not the best way to do it. However, it is the most simple way to show for this example. I leave it to the reader to figure out how to use another AuthType (or, make pushes go across SSL).

Making the CGI the index


The final thing we need to do is make it so that the hgwebdir.cgi script is the index when the server attempts to serve up the page and to make sure the server can handle CGI.

DirectoryIndex hgwebdir.cgi
AddHandler cgi-script .cgi
Options ExecCGI
Order allow,deny
Allow from all


Putting it all together


If we put it all together and assign it to a virtual host, we get an entry like the following (which, if you're using Apache2 can just be placed as a file in sites-available):

<VirtualHost XXX.XXX.XXX.XXX:80>
ServerName hg.someplace.com
DocumentRoot /var/hg/hgweb
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteRule ^/(.*) /hgwebdir.cgi/$1
</IfModule>
<Directory /var/hg/hgweb>
DirectoryIndex hgwebdir.cgi
AddHandler cgi-script .cgi
Options ExecCGI
Order allow,deny
Allow from all
AuthUserFile /etc/hg/htpasswd
AuthName "Dev Repo"
AuthType Basic
<Limit POST PUT>
Require valid-user
</Limit>
</Directory>
</VirtualHost>


Configuring your hgrc file(s)


The hgrc file is the general configuration file for all things Mercurial. There are always at least two possible hgrc files for every repository:


Inside of these hgrcs, you can define a directive called [web] which controls the behavior of the web-interface used in hgweb.cgi and hgwebdir.cgi.

System-wide web hgrc settings


Hg's web-interface defaults using a style that I personally find to be ugly and confusing to use. I much prefer the "gitweb" style over the default Mercurial style. So I set the "style" parameter in the [web] section of the system-wide hgrc to "gitweb" to make it the default style.

Additionally, I want compressed archives to be made available, and I want to set a system-wide contact. Finally, if you're using the same setup I've detailed above, you aren't using SSL for your pushes, which means that the push over SSL requirement should be disabled.


[web]
style = gitweb
allow_archive = bz2 gz zip
contact = Myself, me@somewhere.com
push_ssl = false


Per repository hgrc settings and user authentication


For each repo, you can define a specific hgrc file that will override the system-wide settings from /etc/mercurial/hgrc.

Generally speaking, you want to at least define a description for the repository as well as who is allowed to push. Additionally, you can define new contact information if it differs from the system-wide setting.

[web]
description = An addressbook for keeping track of your "friends"
contact = Ted Haggard, tedh@ilikethemens.com
allow_push = tedh


Now, you can easily define per repository htpasswd files, however, this can get unwieldy and is completely unnecessary. Instead, it makes more sense to define a global htpasswd file, but then define push rights per repository in the hgrc.

So I could have a global htpasswd file that defines all of my users like this

tedh:HGand8176
fred:87JIkn7j1*9
joe:87/joiqKl91
jake:jasmn1%1tba

But then define the following project push rights via their hgrc's:

Project A

[web]
descrtiption = Project A
allow_push = tedh, joe


Project B

[web]
descrtiption = Project B
allow_push = jake, fred


Project C

[web]
descrtiption = Project C
allow_push = tedh, jake, joe