Register | Login

Stacking Code

public interface IBlog { string Dump(Stream consciousness); }

Running a Mercurial Server on IIS 7.5

Thursday, 24 February, 2011 @ 7:33 PM < Adam Boddington
Tags: Mercurial

I recently ran through the process of setting up a Mercurial server on IIS 7.5. It was a little painful and not something I would recommend, more so now that Bitbucket is offering free unlimited private repositories for teams with five developers (if Bitbucket isn't your cup of tea, you can find more Mercurial hosters here). If however your client isn't comfortable with having source code hosted externally, you may want to get yourself a Linux/Apache box and follow this comprehensive guide. If you can't do that either, and really want to use IIS 7.5 on Windows 2008 R2, then keep reading (and keep the Advil handy).

This is based on the guide Jeremy Skinner provides on his blog. A few things have changed in Mercurial since it was written however, which prompted me to document exactly what I did differently, and for the sake of completeness, document what I did the same.

Requirements

These aren't requirements so much as just what I used. Later versions may work the same way (although they didn't for Jeremy's guide).

Mercurial is based on Python, and it seems to matter which version of Python is installed. To find out which version of Python Mercurial needs, install TortoiseHg either on the server or the client (or both) and have a look at the About TortoiseHg screen. This version of Mercurial uses Python 2.6.4, but I could only find Python 2.6.6. However, I think as long as the major and minor version numbers match, everything should be fine.

About TortoiseHg

I'm installing Mercurial the Python package, not Mercurial the stand-alone program. Make sure you grab the right one. Mercurial as a Python package means Python can find it easily. I also grabbed the 32 bit version of Python and Mercurial just to get this working as soon as possible. Things may work with the 64 bit versions, but it wasn't something I was willing to fiddle with. If you're feeling brave you might want to give it a go.

Mercurial Downloads

TortoiseHg is completely optional on your server. I installed it so I can browse my repositories with it while I'm logged into the server, but if you hardly ever RDP into your server you won't need it. It can be handy to init new repos with it, but that can be accomplished using your local machine and a network share.

IIS 7.5

If your Windows Server doesn't already have IIS 7.5 installed, go ahead and do that now using Server Manager. Make sure CGI and Basic Authentication are installed. (Ignore the other items have checked, they're for other applications.)

IIS 7.5 Roles

Python and Mercurial

Install Python 2.6. It's pretty straight forward and it will end up in a directory like C:\Python26 by default. Once that is complete, install the Mercurial Python package. This will look for Python and install itself in the C:\Python26\Lib\site-packages directory.

Mercurial Install

Web Application

This part of the process is very similar to Jeremy's guide, so I will go over this quickly. If you need more details, Jeremy's effort may have them.

Create an application under the default web site and call it whatever you like, I called it hg like Jeremy did. The physical location is C:\inetpub\wwwroot\hg.

IIS Manager hg

Next, go into Handler Mappings and add a Script Map for CGI. Enter *.cgi for the request path, C:\Python26\python.exe -u "%s" for the executable, and Python for the name.

CGI Handler Mapping

To test this works, create a simple Python script and save it in the application directory as test.cgi.

print 'Status: 200 OK'
print 'Content-Type: text/html'
print
print '<html><body><h1>It Works!</h1></body></html>'

Browse to http://localhost/hg/test.cgi to see if it works.

It Works!

The next thing to do is get a hold of hgweb.cgi, which isn't included in any of the Mercurial installations that were just performed as far as I can tell. One way to grab it is to browse the Mercurial source code here and download it, or you can cut and paste the following and save it to C:\inetpub\wwwroot\hg\hgweb.cgi.

#!/usr/bin/env python
#
# An example hgweb CGI script, edit as necessary
# See also http://mercurial.selenic.com/wiki/PublishingRepositories

# Path to repo or hgweb config to serve (see 'hg help hgweb')
config = "/inetpub/wwwroot/hg/hgweb.config"

# Uncomment and adjust if Mercurial is not installed system-wide:
#import sys; sys.path.insert(0, "/path/to/python/lib")

# Uncomment to send python tracebacks to the browser if an error occurs:
#import cgitb; cgitb.enable()

from mercurial import demandimport; demandimport.enable()
from mercurial.hgweb import hgweb, wsgicgi
application = hgweb(config)
wsgicgi.launch(application)

This differs from Jeremy's guide because hgwebdir.cgi and hgweb.cgi were combined in Mercurial 1.6. The hgweb.cgi file now handles both single and multiple repositories.

If you've downloaded the file, open it up and change line 7 to match line 7 above. This is the location of the Mercurial configuration file.

Next, create an empty file and call it hgweb.config. More information will go into it later -- right now an empty file equates to default settings. At this point there should be four files in the C:\inetpub\wwwroot\hg directory.

  • hgweb.cgi
  • hgweb.config
  • test.cgi
  • web.config (created automatically by the IIS Manager for the CGI handler mapping)

That's enough to get Mercurial running. There's no need to copy the Mercurial library or template directory locally, that's what the Mercurial Python package was for. Browse to http://localhost/hg/hgweb.cgi and the following should be displayed.

Mercurial Repositories

Pretty URLs

Jeremy had a great suggestion at this point to create a rewrite rule to eliminate hgweb.cgi from the URL. To do this, URL Rewrite must be added to IIS 7.5 first. The easiest way to do that is to install Web Platform Installer and then install URL Rewrite 2.0 through that.

Now you can follow Jeremy's guide to create the rewrite rule via the interface, or you can cut and paste the <rewrite> section below into your web.config file, just after </handlers>.

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <handlers>
            <add name="Python" path="*.cgi" verb="*" modules="CgiModule" scriptProcessor="C:\Python26\python.exe -u &quot;%s&quot;" resourceType="Unspecified" requireAccess="Script" />
        </handlers>
        <rewrite>
            <rules>
                <clear />
                <rule name="hgweb.cgi" enabled="true" patternSyntax="Wildcard">
                    <match url="*" />
                    <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
                        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
                    </conditions>
                    <action type="Rewrite" url="hgweb.cgi/{R:1}" />
                </rule>
            </rules>
        </rewrite>
    </system.webServer>
</configuration>

Next, edit the hgweb.config file to let Mercurial know how URLs should be generated.

[web]
baseurl = /hg

Browse to http://localhost/hg to see the same screen as before.

Test Repo

Now to choose a location for the Mercurial repositories. I went with C:\Repositories\Hg. To tell Mercurial about the location, open hgweb.config and modify it like so...

[collections]
/Repositories/Hg = /Repositories/Hg

[web]
baseurl = /hg

Create a new repo, Test, in the directory and refresh the browser.

Mercurial Repositories with Test

You may encounter permission errors when browsing now or later on after some changesets have been pushed. If you do, grant the local IIS_IUSR group permission to read the C:\Repositories\Hg directory.

IIS_IUSRS Permissions

The Test repository should be clonable, but pushing should fail. Over on a client machine, run some quick checks.

Clone and Push

All pushes require SSL, lest credentials be transmitted in the clear. For now we'll disable SSL by adding another line to hgweb.config.

[collections]
/Repositories/Hg = /Repositories/Hg

[web]
baseurl = /hg
push_ssl = false

Now the push should fail on authorisation.

Push with SSL Off

Modify hgweb.config again to let everyone push.

[collections]
/Repositories/Hg = /Repositories/Hg

[web]
allow_push = *
baseurl = /hg
push_ssl = false

Try the push again, this time it should work.

Push with Allow Push Everyone

Refreshing the browser will show the test commit.

Mercurial Repositories with Test Commit

Authentication and Authorisation

Letting everyone push probably isn't a great idea. This is remedied by turning on authentication in IIS and telling Mercurial to authorise. To get started, go back to IIS Manager and enable Basic Authentication. Setting the default domain and realm can also be handy.

IIS Manager hg Authentication

Now edit the hgweb.config file to limit who can push.

[collections]
/Repositories/Hg = /Repositories/Hg

[web]
allow_push = Adam.Boddington
baseurl = /hg
push_ssl = false

Pushing another test will result in Mercurial asking for a username and password, authenticated against the domain.

Push with Authentication and Authorisation

If you get bad gateway errors here the pusher may not have write permissions on the directory. You may want to try giving all pushers write permissions on C:\Repositories\Hg. Alternatively, a local group on the server for pushers which has the required permissions may make life easier.

SSL

Passing credentials around in the clear, even on a corporate LAN, is never a good idea. Luckily setting up SSL isn't difficult. First, create a self-signed certificate on the server (or use a real certificate if you have one).

IIS Manager Server Certificates

Go into the bindings for the default web site.

IIS Manager Bindings

Add a new HTTPS binding using the self-signed certificate.

HTTPS Site Binding

Turn SSL back on for Mercurial by removing the push_ssl = false line from the hgweb.config file.

[collections]
/Repositories/Hg = /Repositories/Hg

[web]
allow_push = Adam.Boddington
baseurl = /hg

New in Mercurial 1.7.3 is the automatic checking of certificates against Certification Authorities (CAs). This is a slight problem for self-signed certificates -- the check will always fail. Luckily in Mercurial 1.7.5 there is the ability to skip the check by adding an --insecure option to the command.

Clone with SSL

To avoid having to type --insecure with every command there are two ways to tell Mercurial to trust the self-signed certificate. The first is to export the certificate in X.509 PEM format and append it to the cacert.pem file in each client's TortoiseHg program files folder. (Firefox can export the certificate and gvim can edit the cacert.pem file.) The second much easier way is to simply add the "fingerprint" of the server to each client's Mercurial global settings (mercurial.ini). If you're using a build server running under a system account that doesn't have a user profile, the fingerprint can be added to the mercurial.ini file in the TortoiseHg program files folder (the mercurial.ini file may have to be created).

[hostfingerprints]
enebrisabs01 = c0:86:63:78:44:6f:f4:62:a4:b3:92:30:c1:3e:e9:71:53:6e:0a:ce

The fingerprint is displayed by Mercurial when using the --insecure option.

Further Security

I haven't tried this yet myself, but if permission is required on a repo by repo basis, try setting up .NET authorisation on each repo folder. It should be possible to set up allow/deny rules on any folder that requires it.

There are 8 comments.


Comments

Nick wrote on Wednesday, 6 April, 2011 @ 4:33 AM

This was an excellent post, It was very well put together. I actually tried these instructions on a 64bit windows 7 system and pretty much everything worked. The only problem was Python, I had to get the 64bit download, called "mercurial-1.8.1.win-amd64-py2.6.exe", try to get the .exe and not the .msi. For some reason I think the .msi doesn't register python 2.6.6. into the Windows 7 registry. Other then that everything works on 1.8.1. mercurial.

Thanks a bunch, Nick

Ali T wrote on Sunday, 8 May, 2011 @ 1:12 AM

Adam, Thanks a lot for putting this together... :) one of the statements/step above have left me little bewildered though - "•web.config (created automatically to contain the CGI handler mapping)" i am not sure where that(web.config) came from?

right now i am left to stare at the following error! Would greatly appreciate any sort of help :)

Thanks in advance.

Nick Ciereck wrote on Tuesday, 10 May, 2011 @ 4:00 AM

what is the error your getting?

Ive set up this same mercurial server like 4 times, and everytime some little error pops up that I didn't get the other times...Really frustrating! The first two times I set up the Mercurial server, the "web.config" WAS automatically generated, The last two times I had to Manually make & insert the proper code.

Adam Boddington wrote on Thursday, 12 May, 2011 @ 1:21 PM

Ali, IIS Manager creates web.config automatically when you create the CGI handler mapping. You can find that step in the section titled Web Application. Would you be able to post the error you're getting?

derbjoerm wrote on Thursday, 17 November, 2011 @ 12:11 AM

Hey first of all thx a lot....this is f...... brilliant!

But I have a little problem with the whole Auth&Co. + Push

So everytime i try to HG Push on the PowerShell i get: abort: "repository default-push not found". Everything from the beginning until^works fine, but now...:(

I also searched for a solution, but didnt find anything! I appreciate your help/hint/....! Pls can anyone help me?

thx a lot

Bjoern

JamisonWhite wrote on Saturday, 10 December, 2011 @ 6:22 AM

Adam, great update to Jeremy's article! Have you tried getting Mercurial 2.0's extension working with IIS7?

jed3d wrote on Sunday, 11 March, 2012 @ 4:19 PM

Thanks for a great post! I got it working with Windows 7 Pro with 64-bit versions of everything and even .NET Auth. See my article here http://jediscode.blogspot.com/2012/03/securely-running-64-bit-mercurial.html

Anton Gogolev wrote on Monday, 17 June, 2013 @ 8:40 PM

Or else you can install HgLab and be up an running (and integrated with ActiveDirectory) in a couple minutes. Plus you'll get pushlog, fancy graphs and code analysis tools.

Leave a Comment

Please register or login to leave a comment.


Older
IDependencyResolver and Generics

Newer
Dave Thomas' Code Kata 4, Part One

Older
IDependencyResolver and Generics

Newer
Dave Thomas' Code Kata 4, Part One

browse with Pivot


About


Projects

Building Neno


RSS
Recent Posts

Codility Nitrogenium Challenge
OS X Lock
HACT '13
Codility Challenges
Priority Queue


Tags

Architecture (13)
ASP.NET (2)
ASP.NET MVC (13)
Brisbane Flood (1)
Building Neno (38)
C# (4)
Challenges (3)
Collections (1)
Communicator (1)
Concurrency Control (2)
Configuration (1)
CSS (5)
DataAnnotations (2)
Database (1)
DotNetOpenAuth (2)
Entity Framework (1)
FluentNHibernate (2)
Inversion of Control (5)
JavaScript (1)
jQuery (4)
Kata (2)
Linq (7)
Markdown (4)
Mercurial (5)
NHibernate (20)
Ninject (2)
OpenID (3)
OS X (1)
Pivot (6)
PowerShell (8)
Prettify (2)
RSS (1)
Spring (3)
SQL Server (5)
T-SQL (2)
Validation (2)
Vim (1)
Visual Studio (2)
Windows Forms (3)
Windows Service (1)


Archives


Powered by Neno, ASP.NET MVC, NHibernate, and small furry mammals. Copyright 2010 - 2011 Adam Boddington.
Version 1.0 Alpha (d9e7e4b68c07), Build Date Sunday, 30 January, 2011 @ 11:37 AM