O2 UK – mobile phone telco is crap, grass is green, bears etc

Two days ago I decide to order a Moto G handset to replace the one I destroyed earlier this year. So I head over to the O2 website to get me a new phone (O2 because my PAYG SIM is O2 and not much else works in my rural environs, or certainly it was the case three years ago).

The O2 website shows the Moto G 3rd gen handset is “in stock” and there’s a tenner off bringing the cost to GBP109.00 for a new phone. Win!

I add the phone to my basket and pick an airtime tariff. Annoyingly you *have* to pick an airtime tariff even though you just want a handset, there’s no “I already have a bloody SIM” option. And this truly annoys me because they allocate you a number on a new SIM which is just a complete waste of a number and plastic and packaging, etc.

So, I then click checkout. Despite already being logged into my account the checkout wants me to login *again* (sigh).

Next up, delivery address. The last Moto was bought from O2’s local bricks and morter shop so my account doesn’t have my postal address, I only ever used my account to do online topups and was never asked to provide my home address. At the checkout it’s currently filled in with some weirdo default template text. So I hunt around for a way to update my address, turns out you can’t. Their FAQ swears blind there’s an option to do this, but I’ll be damned if I can find it. What to do?

To solve the delivery address problem I hit up their online support chat doodah. Before I get to chat to anyone I’m asked to fill in my Name and registered mobile number, I do this and what’s the first thing the human on the other end asks? “Can you tell me your mobile number?” (deep sigh).

Anyway after going through various security checks I explain my address problem, the blandly helpful bod on the other end says he can fix this and I provide him with my address. After confirming he’s updated my address I’m told this will take up to 48hrs to happen (another deep sigh). Seriously 48hrs? At this point I decide to eat lunch.

Unconvinced that it really takes 48hrs to update an account’s postal address I decide to try and purchase this sodding phone again. I get to the checkout and find my address has actually updated but there’s something wrong with it, it shows as:

house number

instead of:

house number

(more sighing because there are courier drivers out there who won’t deliver your package unless the address is 100% correct, for all I know my phone might be delivered by Yodel, enough said)

So I ping the blandly helpful online support folks on chat and after the several minutes of security checks I explain that my address is wrong. He/she assures me it is fine and will print out correctly on the courier’s labels etc.

Whilst chatting I also explain that my contact number (presumably so the courier can call if they get lost) on the checkout is wrong, it’s set to my mobile number, which is completely unusable because I don’t have a functioning mobile, hence this purchase. There’s no way for me to change it on the website.

I try to explain this to the blandly helpful support bod but he/she doesn’t seem to comprehend this Catch 22 problem. I ask for the contact number on the order to be changed to my house land line number (in case the courier can’t find me and needs to phone for directions) but this is a futile and fruitless effort. The blandly helpful support bod says he/she will remain on chat to make sure my order goes through properly, so I click through my payment details only to be logged out by the site, again.


So, I re-login, restart the purchase, pick phone, pick unnecessary tariff, add to basket, click checkout which makes me login again, pick incorrectly displayed address, sigh at unusable and non-editable contact number, click through to payment page. I enter my card details, click confirm, and then for the first time in forever my bank’s 3D secure anti-fraud checker decides it wants to do its own security check (more deep sighing), fortunately it just asks for my card’s bank sort code, account number, expiry date and CVV number on the back, which I have in my hand; if it had asked anything else and I’d be kicking off on the gin early. One more click and I get the order confirmation page and an order number….yay!

I copy/paste the order number to the blandly helpful chat bod and he/she confirms my order is fine, we exchange pleasantries and part company. I give the chat bod a very favourable rating in that “was our support helpful” survey screen before closing the window.

A few minutes later I check my email and see that there’s an order confirmation message complete with order number, so far so good.

Now during the whole laborious process O2’s e-commerce website showed my handset as “In Stock – Free delivery next working day when you order by midnight”, I double check this before getting back to work, and it’s still showing as in-stock.

Later in the afternoon I get a second email confirming that “Your order is being processed”, again, progress. However, dismayingly, the message also says:

“We’ll email or send you an SMS to confirm when your order has been despatched so that you’ll know when to expect your delivery”

Grr…SMS, to a number that I CAN’T BLOODY USE UNTIL I HAVE A WORKING SODDING PHONE, which is why I went to the trouble of trying to explain why my contact number needed to be changed…deep, deep sighing and eye-rolling.

Fast forward to the next day, I check my email for order progress updates, there’s not a peep from O2. It’s still early, I’ll check again in the afternoon. I take a late lunch at around 3pm, check my email and still nothing from O2.

I also once again check that my handset is still showing as in-stock, because I have that sneaky feeling something is not right.

So I logon to my account to, as the last O2 email explained, “keep tabs on your order at https://myo2services.o2.co.uk/myorders”. My account screen under “Recent Orders” states “You haven’t ordered anything in the last 31 days.”, I have a hunt around to see if there’s maybe a different “Orders” page, but no, that’s the only one. Gathering my remaining reserves of patience for the day I decide to hit up the blandly helpful support bods on chat again. Once more, several minutes of doing the security thing, copy paste my order number, then explain that I don’t see my order on my online account, nor have I received any further updates about my order. There’s a longer than usual pause before the support bod comes back to me and tells me that the phone is out of stock and on back-order. What the cocking hell!?

I maintain my online calm and explain that the handset is showing as “in-stock” on their website (right there and then I check once more, yep, “in stock”), support bod blandly apologies and explains to me that the website is wrong and it’ll be at least a week before any more Moto G’s are in stock. This is no bloody good. In a fit of polite and constrained temper I tell the remote support bod to cancel the order and refund my money which he/she confirms is done but it’ll take up to 14 days to process the refund. YES…FOURTEEN TWATTING DAYS!?

Knowing there’s no point trying to argue the toss with the blandly helpful remote support bod, who’s clearly in a far away land several timezones ahead of me, I politely protest about how stupid their website is, grumble about the 14 day refund thing, part company, and leave a far less favourable rating on their “how did we do?” end-of-chat survey.

Annoyed I decide to phone O2 Sales to double check if the remote support bod is maybe seeing incorrect stock data or is just plain lying.

One does not simply

After 15 minutes of queuing and heading up several ACR/ACD/ACH blind alleys I eventually get to speak to a sales person, and one on the same continent as me, and quite possibly even on the same land mass just north of the cost of France.

I provide my order number, but they insist on more security checks, I provide my order number again (doesn’t anyone have a pen and paper?) and the sales dude confirms that new Moto G’s are definitely out of stock and would be on back order. I explain to the sales person that yesterday and all of today the phone is showing “in stock”. Out of idle curiosity I refresh the Moto G’s product item page and lo-and-behold its stock status changes to “Home delivery in up to 1 week”. I have a moan to the sales bloke about how much this experience has sucked from start to finish but he, politely, doesn’t really give a shit. I get him to check the stock status of the refurbished Moto G 3rd Gen which even as we were speaking was still showing “in stock for home delivery” on their website, unsurprisingly it’s also out of stock. Exasperated, I ask why they still show it as “in stock”, he doesn’t really answer my question, he doesn’t give a crap. I ask about my refund and get him to confirm that it’s really happening, he confirms it’s all fine and I should have my money back in five working days, not fourteen (but who bloody knows what might happen between now and then?).

From start to finish my whole order experience was one hurdle after another. Seriously, it’s 2016, and O2 can’t provide a real-time stock status from whatever big iron ERP runs their back office? I think what really pisses me off is that they didn’t provide any sort of update to explain that my phone went from “processing” to “on back order”. Oh no, I had piss about for an hour or so to find this out for myself, and couldn’t leave the house in case the courier turned up. It makes me wonder how many sales they lose when their website shows items as “out of stock” but are really in stock because they aren’t reflecting their stock status in real time. Or maybe they do, but maybe showing as “in stock”, for items that really aren’t, is just a dark sales pattern where they hope you don’t have the mental energy to cancel an order. Either way it’s shit.

The upside was that Argos has unlocked/sim-free refurbished Moto G 3rd Gen handsets for 99 quid advertised on ebay. I ordered that instead, it means I’m now no longer tied to O2. I’m rather hoping we’ve got better coverage around my village from other providers so I can ditch O2 completely.

Posted in o2, telcos | Leave a comment

Beware of Mr Cloud T-Shirts

Watch out for this guy – http://www.mrcloud.com. I ordered a T-shirt on the 10th of October and paid via PayPal. What struck me as slightly odd was that I never got a confirmation message from his shopping cart app, but I put it down to “one of those things”.

After around two weeks the T-shirt hadn’t arrived, this is unusual because although I’m based in Ireland most things posted from the UK arrive within 2-5 working days, even on second class post.

I sent an email on the 23rd of October asking if Mr Cloud had received my order whilst explaining that I didn’t get any sort of order confirmation, and if they did, had they shipped the order yet.

Later that day I got my one and only communication from Mr Cloud which answered none of the questions raised above, all it said was:


It may take up to 28 days


A quick check of Mr Cloud’s Terms page did indeed confirm that non-UK orders would take up to 28 days, fair enough, but a little bit more info in the reply would have been nice.

I replied on the 24th of October asking if they could maybe nudge the order seeing as it was for Ciaran’s birthday on the 2nd of November, and asked again if Mr Cloud (or Craig Hesmondhalgh who is apparently the proprietor of the business) could confirm if the order had shipped.

There was no reply and I emailed again on the 25th of October asking politely if he could answer my question about whether the order had shipped. I got no response and decided to leave chasing the order until 28 days was up.

On the 7th of November the postman had been and gone and still no t-shirt, and still no communication from Mr Cloud. I emailed him again explaining that 28 days had now passed since I placed my order and that there was nothing in the post from him, could he confirm the order had been posted.

Having had no reply by 2pm on the 8th of November I emailed once again:

Hi Craig,

It’s now 29 days since I ordered this item and it still hasn’t arrived. Ireland is not the other side of the world and the postal service between the UK and ROI is fairly efficient with most items being delivered within 2-3 days of posting.

Where is my t-shirt?


Having had no response by mid-day on the 9th of November I opened a PayPal dispute which was resolved this morning with a refund and no explanation from Mr Cloud. Getting the refund is fine, but what a fucking waste of time.

With hindsight this will teach me not to Google around for complaints and disputes about a company I’ve never dealt with previously before spending money with them. Having done that in the past few days turned up these links about Mr Cloud’s past form:








I know this is a bit of a speil but if Google indexes this article and at least one person is saved the hassle of dealing with this company then my work is done.

Posted in Uncategorized | Leave a comment

MS SoapClient and WSDLReader error ‘80020009’

This trips me up every year with a blob of legacy classic ASP code I have on my debug and test environment:

wsdl = "https://mysecure.service.com/Service.asmx?wsdl"
Set service = CreateObject("MSSOAP.SoapClient30")
service.ClientProperty("ServerHTTPRequest") = True
Call service.MSSoapInit(wsdl)

The above code suddenly starts throwing this error for no apparent good reason:

WSDLReader error '80020009'

WSDLReader:XML Parser failed at linenumber 0, lineposition 0, reason is: System error: -2147012721. HRESULT=0x1: Incorrect function. - WSDLReader:Loading of the WSDL file failed HRESULT=0x80070057: The parameter is incorrect. - Client:One of the parameters supplied is invalid. HRESULT=0x80070057: The parameter is incorrect.

/services/WS.inc, line 5

Then after half an hour of head scratching I realise the SSL certificate has probably expired…and lo and behold it has.

Maybe next year the penny will drop more quickly. :)

Posted in IIS7 | Leave a comment

Mercurial and mercurial-server on CentOS

In my previous two posts I explained how I configured Python 2.7 and Mercurial 1.9.3 on CentOS 5.5 x64 for HTTP access. HTTP access is fine but life is simpler when using SSH.

mercurial-server is one of many shared SSH publishing solutions to allow your development team have access to your central repository collection.

It kind of works like this. A single SSH account (hg) is created on your Mercurial central repo server. This account acts as a gateway account and it also owns all of the repositories on the server. The account is heavily restricted. You can’t logon to it using a username and password, only SSH keys are permitted and these keys must be imported using mercurial-server’s refresh-auth script (or via upload to a special administrative repository once the administrator’s public key has been installed).

Each developer generates a public and private SSH key pair. Their public key is uploaded to central repository server and is added to the hg account’s ~hg/.ssh/authorized_keys file using the refresh-auth script (or via the hgadmin administrative repository.

Each authorized_keys record also has the command option specified which restricts the hg logon to a single operation regardless of any commands specified by the client (which are ignored). For example:

no-pty,no-port-forwarding,no-X11-forwarding,no-agent-forwarding,command="/usr/local/share/mercurial-server/hg-ssh root/bob/bob.pub" ssh-dss AAAAB3NzaC1kc...Qfa02w== root@vostro1700.zygonia.net

For a more detailed explanation of how this works see: How mercurial-server works

First off, download and unzip mercurial-server from here:

mercurial-server 1.2 sources

wget http://dev.lshift.net/paul/mercurial-server/mercurial-server_1.2.tar.gz
tar zxf mercurial-server_1.2.tar.gz
cd mercurial-server_1.2.orig

Before building and installing there’s some tweaks I needed to make for my CentOS install.

On CentOS we need to prevent the docbook files being built. They’re not necessary and the build will fail on CentOS if you try to build them.

Open up Makefile, at around line 53 you’ll see this step:

installfiles: installetc installdoc pythoninstall 

I removed the installdoc reference, so it should look like this:

installfiles: installetc pythoninstall 

Next we need to amend the useradd command switches at around line 61. CentOS doesn’t support the --system switch and we need to use -r instead:

        useradd -r --shell /bin/sh \
            --home /var/lib/mercurial-server --create-home \
            --comment  "Mercurial repositories" $(NEWUSER)

Finally, because I have both Python 2.4 and 2.7 installed and my Mercurial installation has been installed for Python 2.7, I need to ensure that Python 2.7 is called.

To make this easier I modified the Makefile just a bit more and added a PYTHON variable to point to the default version of python I want to use. This is because make doesn’t expand bash aliases, so even if you’ve aliased python to Python 2.7 it’ll be ignored. Setting a PYTHON variable and using that makes life easier. It also means you can override the python version from the command line. This is my revised Makefile:

#!/usr/bin/env make -f



build: build/html/index.html pythonbuild

setup-adduser: installfiles adduser inituser

# WARNING: this is experimental
setup-useradd: installfiles useradd inituser

        rm -Rfv $(DESTDIR)$(ETCDIR)
        rm -Rfv $(DESTDIR)$(LIBDIR)
        userdel -rf $(NEWUSER)

        $(INSTALL) -d $(DESTDIR)$(ETCDIR)
        $(INSTALL) -m 644 -t $(DESTDIR)$(ETCDIR) \
        $(INSTALL) -d $(DESTDIR)$(ETCDIR)/remote-hgrc.d
        $(INSTALL) -m 644 -t $(DESTDIR)$(ETCDIR)/remote-hgrc.d \
            src/init/conf/remote-hgrc.d/access.rc \
        $(INSTALL) -d $(DESTDIR)$(ETCDIR)/keys/root
        $(INSTALL) -d $(DESTDIR)$(ETCDIR)/keys/users

installdoc: build/html/index.html
        $(INSTALL) -d $(DESTDIR)$(DOCDIR)
        $(INSTALL) -m 644 -t $(DESTDIR)$(DOCDIR) README
        $(INSTALL) -d $(DESTDIR)$(DOCDIR)/html
        $(INSTALL) -m 644 -t $(DESTDIR)$(DOCDIR)/html build/html/index.html

build/html/index.html: doc/manual.docbook
        xsltproc --nonet -o $@ $(DOCBOOK_XSL)/html/docbook.xsl $^

build/pdf/manual.pdf: doc/manual.docbook
        mkdir -p build/pdf
        fop -xml $^ -xsl $(DOCBOOK_XSL)/fo/docbook.xsl $@

        $(PYTHON) setup.py build

        $(PYTHON) setup.py install \
            --install-purelib=$(DESTDIR)$(LIBDIR) \
            --install-platlib=$(DESTDIR)$(LIBDIR) \
            --install-scripts=$(DESTDIR)$(LIBDIR) \

installfiles: installetc pythoninstall

        adduser --system --shell /bin/sh --group --disabled-password \
            --home /var/lib/mercurial-server \
            --gecos  "Mercurial repositories" $(NEWUSER)

# WARNING: this is experimental
        useradd -r --shell /bin/sh \
            --home /var/lib/mercurial-server --create-home \
            --comment  "Mercurial repositories" $(NEWUSER)

        su -l -c "$(DESTDIR)$(LIBDIR)/init/hginit $(DESTDIR)$(LIBDIR)" $(NEWUSER)

I also added a remove rule to clean up and remove mercurial-server so you can do make remove. I left the docbook rules in place because one day I might get around to fixing that for CentOS, however they don’t get called.

All that remains now is to build and install mercurial-server, do this by running:

make setup-useradd

The first thing we need to do is generate and install the administrators SSH public and private key pairs. If you already have a pair of key files you want to use then skip the next step:

To generate a new public/private key pair then use ssh-keygen:

[root@vostro1700 ~]# ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): kevin_rsa
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in kevin_rsa.
Your public key has been saved in kevin_rsa.pub.
The key fingerprint is:
86:fc:13:23:ec:3b:41:01:fb:59:b8:75:a9:da:cb:57 root@vostro1700.zygonia.net
[root@vostro1700 ~]#

You can do this on the mercurial server machine or you can do this on your workstation. Ultimately we want to get the public key file (kevin_rsa.pub) onto the server.

Once you’ve generated a pair of key files and you’ve got the public key file onto the mercurial server we need to copy it to the mercurial-server keys/root folder and then instruct mercurial-server to “import” it to the hg user’s authorized_keys file:

sudo mkdir -p /etc/mercurial-server/keys/root/kevin
sudo cp kevin_rsa.pub /etc/mercurial-server/keys/root/kevin
sudo -u hg /usr/local/share/mercurial-server/refresh-auth

We’re now ready to create new repositories.

Connecting From Linux
If your client machine is Linux and you’re already using SSH keys to authenticate against remote servers then the following will be familiar. First we start ssh-agent, then we add our private key. This means we don’t have to re-type our password:

$ eval `ssh-agent`
Agent pid 13464

What we just did there was launch ssh-agent but evaluate the output, this sets up a couple of handy environment variables (SSH_AUTH_SOCK and SSH_AGENT_PID). If we just did ssh-agent on its own then we’d see:

SSH_AUTH_SOCK=/tmp/ssh-RnDfd13515/agent.13515; export SSH_AUTH_SOCK;
echo Agent pid 13516;

Next we need to add our private key:

$ ssh-add kevin_rsa
Enter passphrase for kevin_rsa:
Identity added: kevin_rsa (kevin_rsa)

Finally lets see if we can create a new repository on the remote server:

$ hg init ssh://hg@hg.mydomain.net/helloworld

If all is well then you should see a helloworld folder appear in /var/mercurial-server/repos.

Connecting From Windows – TortoiseHg
On my Windows Workstation I have the Selenic TortoiseHg bits installed. I also have PuTTY and its related tools installed as well because I use PuTTY but I also need PuTTYgen as well.

There’s a bit of a gotcha with PuTTY and SSH key files. If you generated your keyfiles using ssh-keygen then they need to be converted to work with PuTTY and Pageant (PuTTY’s equivalent of ssh-agent).

To do this launch PuTTYgen and select “Conversions -> Import key”:

Locate your private key file (kevin_rsa) which was generated using ssh-keygen and open it. You’ll be prompted for the private key’s password, enter it and hit enter. If the key file is imported successfully then PuTTYgen will pop up a notice and tell you what to do next:

Click OK and then click on “Save private key”, I saved mine as “kevin_rsa_putty.ppk” (the PuTTY tools like to see the .ppk extension so you may as well go along with that).

As an aside I save my keys in a hidden “.ssh” folder in my %USERPROFILE% folder.

As I mentioned earlier, Pageant does the equivalent job of ssh-agent. When you launch Pageant it’ll minimise itself into the notification area:

Load up your newly converted private key into Pageant by right clicking on it’s icon and selecting “Add Key”:

You’ll prompted for your private key file’s password so enter it and hit enter. To confirm the key is loaded right click on Pageant’s notification icon and select “View Keys”. All being well we should see our key in the key list:

Next we need to tell TortoiseHg about our SSH settings. In your Windows %USERPROFILE% directory there should be a file called mercurial.ini. If there isn’t then create one. Mine looks like this:

ui.language = en_GB
vdiff = kdiff3

merge = kdiff3
username = kevin <kevin.kenny@mydomain.net>
verbose = True
ssh="C:\Program Files\TortoiseHg\TortoisePlink.exe" -ssh -2 -agent -v

The important line line is the one that says:

ssh="C:\Program Files\TortoiseHg\TortoisePlink.exe" -ssh -2 -agent -v

TortoisePlink.exe is a recompiled variant of PuTTY’s Plink tool. Plink is the equivalent of Unix’s ssh tool.

That line is instructing TortoiseHg to connect to our remote Mercurial server using TortoisePlink and pickup our private key along the way from Pageant.

Save mercurial.ini and then open a command line window. Next try to create a new repository on your remote Mercurial server (I have all the verbosity switches turned on just so we can see what’s happening under the bonnet):

hg init ssh://hg@hg.mydomain.net/anewrepo

This should result in output that looks similar to this:

running "C:\Program Files\TortoiseHg\TortoisePlink.exe" -ssh -2 -agent -v hg@hg.zygonia.net "hg init anewrepo"
running ""C:\Program Files\TortoiseHg\TortoisePlink.exe" -ssh -2 -agent -v hg@hg.zygonia.net "hg -R anewrepo serve --stdio""
remote: Looking up host "hg.zygonia.net"
remote: Connecting to port 22
remote: Server version: SSH-2.0-OpenSSH_4.3
remote: We claim version: SSH-2.0-PuTTY_Local:_Feb_27_2009_19:14:38
remote: Using SSH protocol version 2
remote: Doing Diffie-Hellman group exchange
remote: Doing Diffie-Hellman key exchange with hash SHA-1
remote: Host key fingerprint is:
remote: ssh-rsa 2048 6f:23:ab:c8:4a:12:8a:d9:b0:36:09:b4:49:54:97:c0
remote: Initialised AES-256 SDCTR client->server encryption
remote: Initialised HMAC-SHA1 client->server MAC algorithm
remote: Initialised AES-256 SDCTR server->client encryption
remote: Initialised HMAC-SHA1 server->client MAC algorithm
remote: Pageant is running. Requesting keys.
remote: Pageant has 1 SSH-2 keys
remote: Using username "hg".
remote: Trying Pageant key #0
remote: Remote debug message: Pty allocation disabled.
remote: Remote debug message: Port forwarding disabled.
remote: Remote debug message: X11 forwarding disabled.
remote: Remote debug message: Agent forwarding disabled.
remote: Remote debug message: Forced command: /usr/local/share/mercurial-server/hg-ssh root/kevin/kevin_rsa.pub
remote: Authenticating with public key "imported-openssh-key" from agent
remote: Sending Pageant's response
remote: Remote debug message: Pty allocation disabled.
remote: Remote debug message: Port forwarding disabled.
remote: Remote debug message: X11 forwarding disabled.
remote: Remote debug message: Agent forwarding disabled.
remote: Remote debug message: Forced command: /usr/local/share/mercurial-server/hg-ssh root/kevin/kevin_rsa.pub
remote: Access granted
remote: Opened channel for session
remote: Started a shell/command
remote: Sent EOF message
remote: Server sent command exit status 0
remote: Disconnected: All channels closed

Logon to your repository server and look in the /var/lib/mercurial-server/repos folder, there should be a new directory in there called anewrepo.

A Gotcha: When running Pageant and TortoiseHg, make sure that if you run either as “Administrator”, both run as administrator. I had an hour or so of head scratching when running hg init ssh://hg@hg.mydomain.net/anewrepo from an command prompt opened using “Run As Administrator” whilst Pageant was running under my normal Windows account.

In the next article I’ll cover adding new users and joining up the mercurial-server repository with Apache.

Very useful resources:

Sharing Mercurial repositories with mercurial-server
Using ssh-agent with ssh
Using Pageant for authentication

Posted in Mercurial, Python | 1 Comment

Another Python/Mercurial/mod_wsgi on CentOS HOWTO – 2/2

In part 1 I covered how to install Python, Mercurial and mod_wsgi. In this article I’ll explain how to publish your repositories.

Configure Apache + mod_wsgi
Load up your apache httpd.conf file into your favourite editor and add the following lines:

LoadModule wsgi_module modules/mod_wsgi.so
AddHandler wsgi-script .wsgi
WSGIPythonHome /opt/python2.7.2

That last line ensures that mod_wsgi knows which Python to invoke.

On my system I’m using named virtual hosts, this is what my Mercurial’s site virtual host configuration looks like:

 ServerName mercurial
 ServerAlias hg.mydomain.com
 ServerAdmin root@localhost
 DocumentRoot /sites/mercurial/htdocs
 WSGIScriptAlias / /sites/mercurial/htdocs/cgi-bin/hgweb.wsgi

Under your document root create a folder called cgi-bin and create a file in there called hgweb.wsgi.

To verify that mod_wsgi is working I used the trouble-shooting script found on the “Installation Issues” section of the mod_wsgi site. So drop that code into the newly created hgweb.wsgi file and save it:

import sys

def application(environ, start_response):
    status = '200 OK'
    output = 'Hello World!'

    response_headers = [('Content-type', 'text/plain'),
                        ('Content-Length', str(len(output)))]
    start_response(status, response_headers)

    print >> sys.stderr, 'sys.prefix = %s' % repr(sys.prefix)
    print >> sys.stderr, 'sys.path = %s' % repr(sys.path)

    return [output]

When you browse to your site you should see: Hello World! in my browser. So far so good.

Mercurial Configuration
If the above works then replace the contents of hgweb.wsgi with the following:

# An example WSGI for use with mod_wsgi, edit as necessary
# See http://mercurial.selenic.com/wiki/modwsgi for more information

# Path to repo or hgweb config to serve (see 'hg help hgweb')
config = "/sites/mercurial/htdocs/cgi-bin/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()

# enable demandloading to reduce startup time
from mercurial import demandimport; demandimport.enable()

from mercurial.hgweb import hgweb
application = hgweb(config)

You can find this file in the Mercurial source in the contrib folder. If you’ve followed my instructions to the word

Next create a new file in the cgi-bin folder called hgweb.config and add the following:

/ = /sites/mercurial/htdocs/repos/**
allow_push = *
push_ssl = false

Create a folder for your repositories under your document root and initialise a new repository:

mkdir repos
cd repos
hg init testrepo

You should now be able open the Mercurial repository browser by opening: http://hg.mydomain.net

You also need to ensure that your web server’s user can read and write to your repositories. On a default apache install on CentOS this should look like:

chown -R apache:apache /sites/mercurial/htdocs/repos
chmod -R g+rw /sites/mercurial/htdocs/repos
chmod g+x /sites/mercurial/htdocs/repos

You also need to make each repository folder and it’s .hg folder executable as well:

chmod g+x /sites/mercurial/htdocs/repos/testrepo
chmod g+x /sites/mercurial/htdocs/repos/testrepo/.hg

For more info see: Mercurial – 8. Troubleshooting

To test your Mercurial server go to a different machine and do:

E:\>md hg_test

E:\>cd hg_test

E:\hg_test>hg clone http://hg.zygonia.net/testrepo
destination directory: testrepo
no changes found
updating to branch default
resolving manifests
0 files updated, 0 files merged, 0 files removed, 0 files unresolved

E:\hg_test>cd testrepo

E:\hg_test\testrepo>copy con test.txt
Hello World!
        1 file(s) copied.

E:\hg_test\testrepo>hg add test.txt
adding test.txt

E:\hg_test\testrepo>hg commit test.txt -m "Test commit"
committed changeset 0:1904a8ab9a97

E:\hg_test\testrepo>hg push http://hg.zygonia.net/testrepo
pushing to http://hg.zygonia.net/testrepo
searching for changes
1 changesets found
remote: adding changesets
remote: adding manifests
remote: adding file changes
remote: added 1 changesets with 1 changes to 1 files


If we browse to our repository we can see the file we just pushed:

If you follow all of the steps exactly you should end up with a basic but working Mercurial server that you can use to push and pull changesets to.

Obviously you should secure this server if it’s public facing (mine is secured with a simple .htaccess/.htpassword affair for now).

There were a number of sites and documents online that helped me to get this far, these included:

The following Mercurial articles also helped:

Posted in CentOS, Mercurial, Python | Leave a comment

Another Python/Mercurial/mod_wsgi on CentOS HOWTO – 1/2

This is how I got Mercurial up and running on CentOS 5.5 x64. The goal here was to deploy this as a “central” repository Mercurial server so the dev team can push their updates to this box.

For this build I used:

Python 2.7.2
PyPi Setup Tools 0.6c11
mod_wsgi 3.3
Docutils 0.8.1
Mercurial 1.9.3

First off download all of the files listed above to /usr/local/src:

cd /usr/local/src
wget http://www.python.org/ftp/python/2.7.2/Python-2.7.2.tgz
wget http://www.sqlite.org/sqlite-autoconf-3070800.tar.gz
wget http://pypi.python.org/packages/2.7/s/setuptools/setuptools-0.6c11-py2.7.egg
wget http://modwsgi.googlecode.com/files/mod_wsgi-3.3.tar.gz
wget http://prdownloads.sourceforge.net/docutils/docutils-0.8.1.tar.gz
wget http://mercurial.selenic.com/release/mercurial-1.9.3.tar.gz
tar zxf Python-2.7.2.tgz
tar zxf sqlite-autoconf-3070800.tar.gz
tar zxf mod_wsgi-3.3.tar.gz
tar zxf docutils-0.8.1.tar.gz
tar zxf mercurial-1.9.3.tar.gz

Install Dependencies
There are a few dependencies we’ll need, some might already be installed:

yum -y install gcc gdbm-devel readline-devel ncurses-devel zlib-devel bzip2-devel sqlite-devel db4-devel openssl-devel tk-devel bluez-libs-devel make

Build and install SQLite3:

cd sqlite-autoconf-3070800
make install
cd ..

CentOS 5.5 ships with Python 2.4 which is fine for Mercurial but I wanted to use Python 2.7 for some other stuff as well. This explains how to install Python 2.7 side by side with 2.4 (which would be a real bugger to evict from your system due to the huge number of dependencies there are – yum for example).

I installed Python 2.7 into /opt/python2.7.2, to do this:

cd Python-2.7.2
./configure --prefix=/opt/python2.7.2 --with-threads --enable-shared
make install
cd ..

When you execute make it’s possible you’ll get this message:

Python build finished, but the necessary bits to build these modules were not found:
bsddb185 dl imageop

Don’t worry, Python was built successfully, it just means these modules aren’t supported. For our purposes it won’t break anything not to have these modules, they are either deprecated or not relevant.

Next we need to tell ld where to find our Python 2.7 shared libraries:

touch /etc/ld.so.conf.d/opt-python2.7.2.conf
echo "/opt/python2.7.2/lib/" >> /etc/ld.so.conf.d/opt-python2.7.2.conf

Then we create a link to Python 2.7 in /usr/bin and fix up our bash profile to use 2.7:

ln -sf /opt/python2.7.2/bin/python /usr/bin/python2.7
echo "alias python=/opt/python2.7.2/bin/python" >> ~/.bash_profile
echo "alias python2.7=/opt/python2.7.2/bin/python" >> ~/.bash_profile
echo "PATH=$PATH:/opt/python2.7.2/bin" >> ~/.bash_profile
source ~/.bash_profile

If all is well you should see the following when you launch python:

Python 2.7.2 (default, Oct 21 2011, 10:46:56)
[GCC 4.1.2 20080704 (Red Hat 4.1.2-51)] on linux2
Type "help", "copyright", "credits" or "license" for more information.

Press Ctrl+D to exit back to bash.

PyPi, pip, virtualenv (optional)
Although not strictly necessary I also installed these to flesh out my Python dev environment:

cd /usr/local/src
sh setuptools-0.6c11-py2.7.egg --prefix=/opt/python2.7.2
/opt/python2.7.2/bin/easy_install pip
ln -sf /opt/python2.7.2/bin/pip /usr/bin/pip
pip install virtualenv
ln -sf /opt/python2.7.2/bin/virtualenv /usr/bin/virtualenv

Mercurial requires Docutils. When installed this for the first time I had to explicitly call python 2.7 to ensure that Docutils was installed to /opt/python2.7 and not the default Python 2.4 install (that said it’s no big deal if it does also end up being installed there):

cd docutils-0.8.1
python2.7 setup.py install
cd ..

Drop into the mercuria source directory:

cd mercurial-1.9.3

Build and install Mercurial:

make PYTHON=/opt/python2.7.2/bin/python PREFIX=/opt/python2.7.2 all
make PYTHON=/opt/python2.7.2/bin/python PREFIX=/opt/python2.7.2 install
cd ..

Pay particular attention to the PYTHON=/opt/python2.7.2/bin/python and PREFIX=/opt/python2.7.2 arguments, these ensure that mercurial is built using our Python 2.7 install and gets installed in the correct site-packages folder.

Enter hg version and hit return, if all is good you should see:

Mercurial Distributed SCM (version 1.9.3)
(see http://mercurial.selenic.com for more information)

Copyright (C) 2005-2011 Matt Mackall and others
This is free software; see the source for copying conditions. There is NO

To build mod_wsgi you need to ensure that the httpd-devel package is installed, I’m assuming you already have apache installed, so go ahead and do that now:

yum install httpd-devel

Next we do the build, and be sure to configure for Python 2.7:

cd mod_wsgi-3.3
./configure --with-python=/opt/python2.7.2/bin/python
make install

If all is good then you’ll find mod_wsgi.so in your apache modules folder:

$ ll /usr/lib/httpd/modules/mod_wsgi.so
-rwxr-xr-x 1 root root 303801 Oct 21 12:12 /usr/lib/httpd/modules/mod_wsgi.so

Continued in Part #2…

Posted in Uncategorized | 2 Comments


My old DasBlog based site got a bit tired and too hard to maintain so here’s a blog reboot.

Posted in Uncategorized | 1 Comment