I manage websites using separate development and production locations. Changes are communicated and revision control kept through Mercurial. In this post the method is documented.

Prerequisites

  • I use a setup with Mercurial and Apache. Installation and configuration is better covered elsewhere, and should not be much more than sudo aptitude install mercurial apache2, at both the production and the development side.
  • SSH is used.
  • To simplify, I also use the hgweb interface for the Mercurial repositories at the development side, at location ${WEBROOT}/hg. See Install Mercurial and hgweb on a Debian-based system for more info.
  • The web project folder is set to ${WEBROOT}/mywebproject.
  • The server root at the production server is set as ~/public for the production site user.
  • For illustrative purposes, I assume SSH aliases developmentserver-alias and productionserver-alias are set up at opposite sites.

Outline

A functional development server is setup where new functions, designs, etc., are tested, and wanted changes are committed into Mercurial. A commit trigger is written which automatically transfers the changes to the production server and applies them. A commit therefore yields live code, and the copies across the servers are kept identical.

Even in small projects, having two different places for code and keeping perfect sync with manual oversight is often difficult, and was my the primary reason for using a distributed version control system ("DVCS") in this case. To get revision control on top of that is a very nice bonus.

Hooks and hgrc

At the development server, ${WEBROOT}/.hg/hgrc contains:

[hooks]
commit = ssh productionserver-alias hg --cwd public pull -u

On commit, this runs the hg pull -u command at the remote server. The authentication is setup using keys and passwordless login, but it works even if a password is wanted.

At the production server, ${WEBROOT}/.hg/hgrc contains:

[paths]
default = http://developmentserver.com/hg/mywebproject
default-push = ssh://developmentserver-alias/hg/mywebproject

This makes the hg pull -u command look at the correct place for the code. This could also have been given as an explicit argument in the previous SSH command. The default-push line ensures that changes, against all best practices, made at the production server can be pushed back to the correct place at the development server (in this case assumed to be situated at ~/hg/mywebproject).

Apache setup

At both the production and the development servers, /etc/apache2/conf.d/hg.local contains:

RedirectMatch 404 /\\.hg(/|$|ignore$)

This hides .hg directories and .hgignore files from web users.

A small digression on this point is that on a larger site I would use a more elaborate solution that does not store the .hg directory in the production root. The above Apache setting "should" hide them perfectly, but it doesn't take more than a failed configuration, a bug in Apache or an unnoticed change in specifications to reveal the revision history. This can be highly unwanted, exposing sensitive information. On a smaller scale, I judge this risk as both negligible and without essential repercussions.

No specific extra configuration is needed.

What to ignore

The .hgignore file in the project root will enable you to exclude certain files from the revision control system. This is wanted for e.g.:

  • large binary files,
  • files containing passwords and
  • site-specific files.

If space is not an issue, large binary files might be convenient to check in to allow them to automatically transfer (even though it is not encouraged to fill revision control systems with large blobs of binary data), but in practice these are rarely changed and might be just as simple to handle manually. This depends on the site in question's nature.

Files containing passwords and such should not be checked in. I usually create and commit a sample corresponding file with credentials omitted to be able to document the file in question and give instructions on how to recreate it.

The site-specific files should mostly be of the above two types, but depending on your setup might be even more. Once again it depends on the site in question's nature.

By keeping .hgignore checked into the project, files will automatically be ignored on code commits.

Initial deployment

Get a working version at the development server ready. Thereafter, at the production site, run:

hg clone http://developmentserver.com/hg/mywebproject public/

to clone the project into the ${WEBROOT} that was set to ~/public. This should make the site live.

Now, modify the hgrc files as described. After that, all essential configuration is done.

Expansion

In theory I believe it would be even better to work with branches at the development side where new functions could be tested, and only later commit them into the default branch, when the production server is updated. I haven't looked into this since I don't use it because of the project's small size and currently well functioning setup, but it should not pose any problems. This can currently be solved by

  1. cloning the code,
  2. branching,
  3. commiting changes in the cloned directory,
  4. pulling to default directory and
  5. merging with default branch while discarding the temporary clone.

This might seem like a lot of steps, but it is a standard workflow with DVCS systems.

Using the hg archive command, one could do without risking exposure of the repository files at the production server. In small cases, though, it might be more important to get the benefit of the extra backup site.

This method is also easily seen to seamlessly integrate with e.g. ikiwiki.

Conclusion

This method has worked well for my projects, and it is a big improvement from the old manual scp method without revision control.

As ikiwiki and other things show, DVCS systems are versatile enough to find applications in diverse areas.

On an even broader note, the use of DVCS systems is so important that it in my opinion should be taught at the very latest among introduction courses at university educations. It promotes a structured workflow, encourages group work and documents the creation process.