server

Installing Subversion on Fedora

My server uses Fedora so I can't go into more detail about installing on other systems. I found the install straight forward, but there were a few things I wanted to change along the way.

Firstly, we can use yum to do the install:

yum install svn mod_dav_svn

mod_dav_svn is used to hook Subversion into Apache - it means we can access our repositories using URLs, so as long as you've got internet access you can access your code.

This will add the file /etc/httpd/conf.d/subversion.conf which hooks into apache. However, I run multiple subdomains and a secure subdomain, and I'd rather have my checkouts going via my secure subdomain. So, I commented out almost the entire file (by adding '#' at the start of each line) - apart from the two lines that start 'LoadModule' - without these it won't work at all!

I then edited /etc/httpd/conf.d/ssl.conf instead, and within the <VirtualHost> definition I added:

<Location /svn>
   DAV svn
   SVNParentPath /subversion/public
 
   AuthzSVNAccessFile /subversion/conf/authz.policy
   SVNPathAuthz off
 
   # Require SSL connection for password protection.
   # SSLRequireSSL
 
   AuthType Basic
   AuthName "Subversion at dJomp"
   AuthUserFile /subversion/conf/.htpasswd
   Require valid-user
</Location>

Here I tell mod_dav_svn that my repositories will be at /subversion/public/ and the conf files are under /subversion/conf/. I've not created repositories or the configuration files yet, but that's fine - just don't restart apache until you do!

Next, let's create our first Subversion repository.

cd /subversion/public
svnadmin create reponame
chown -R apache:apache reponame

Note the user that we chown the directory to. Since we're accessing through URLs, apache will be doing the donkey work for us, so it needs access to everything.

OK, now we need to set up the configuration files. Let's first define some users and passwords. For the first user we must create the .htpasswd file:

htpasswd -c /subversion/conf/.htpasswd djomp

Then we just add a new user each time by dropping the -c flag:
htpasswd /subversion/conf/.htpasswd djomp

Now we have users, we can define permissions on repositories in /subversion/conf/authz.policy. The content looks something like:

[repo1:/]
djomp = rw
* = r
 
[repo2:/]
bob = rw

This will give the user 'djomp' read/write access, and any other user read-only, for the repository repo1; and only 'bob' has access to repo2. We can define blocks like this for all our repositories.

The final step is to get apache to read in the configuration files again so it'll start working:

service httpd reload

That's it - we now have subversion installed and working, with a working repository!

Using Subversion for Website Code

This is a brilliant trick - and one that can help you develop code even without a local install of apache/etc for testing.

Let's say I've coded this site, and I want to be able to update it easily, while testing the code out as I go. The first step is to create a test subdomain - I may create test.djomp.co.uk in this case. (This may force you to write code that isn't dependent on your domain name!)

I can then check out my code to both www.djomp.co.uk and test.djomp.co.uk. Then, when I check in a change, I can check it out to the test subdomain first, verify it still works now it's on the server, and then check out the code to the main site.

There's quite a lot of manual steps in there though, so let's automate things so we don't have to log into the server every time.

Hooks to update the test site

Within your subversion repository stored on the server is a subdirectory 'hooks'. In here we can have scripts or programs to execute at certain points when using Subversion. The one we need is post-commit - that will be run after a successful commit.

What we want is some way of checking out the latest version to our test subdomain. Now, here's the issue - the post-commit script will execute as the user that Subversion runs as (in my case, "apache", as I use WebDAV to interact with Subversion). However, the svn update command must come from the user that checked out the repository. We could set this to be "apache", but I don't fancy giving all my scripts access to my repository. Bash scripts can't be run with setuid so we need to write a short C program to perform the update, and setuid the executable to the user that owns the local copy of the code. Still with me? If you are, I'm surprised!

Here's the code. In this example, user "djomp" owns the local copy:

#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>
 
int main(void)
{
        execl("/usr/bin/svn", 
                "svn",
                "update", 
                "/path-to-test-subdomain-checkout/",
                "--non-interactive",
                "--config-dir",
                "/home/djomp/.subversion",
                (const char*) NULL);
 
        return(EXIT_FAILURE);
}

So, the script executes svn update at the required directory, using the subversion configuration for the correct user.

Now we can compile and set the permissions on the program:

gcc -o djomp.co.uk djomp.c
chown djomp:apache
chmod 750 djomp.co.uk
chmod u+s djomp.co.uk

This will allow apache to execute the script, and it will run as user "djomp". This does mean that any web script on my server could check out the latest code here, but I'm less worried about that than giving full repository access!

We can now set up our post-commit script to execute this program:

#!/bin/sh
/path-to-update-scripts/djomp.co.uk

So now, crossing our fingers and toes, if we commit a change to the repository, it will automatically be checked out to the test site!

Updating the live site

But how to get a checkout going to the main site? We don't want to automate that - we need to test the new code first.

Sidenote - many people wouldn't want to commit a change before testing it, otherwise their repository could be full of old code that doesn't work. I agree with this, but I find this too useful to mind about the odd mistake here and there - especially if the follow up commit makes it obvious that it's a fix for the previous one!

We can easily set up a second update script to point at the live site. If we give it the same permissions - allowing "apache" to execute it - we could then set up a web page to call the script.

We could even make that web page a little more clever and it could check the versions of the test and live site before offering the link. Thankfully we can test the version number of a local copy without being the user that owns it, so we just need a simple script:

#!/bin/sh
 
cd /path-to-local-copy/
svnversion

This will return a number.

We can then set up a PHP script to handle all of this, as below. This will take details of multiple sites, should you require:

<html>
<head></head>
<?
  $pathToUpdateScripts = '/path-to-update-scripts/';
  $pathToVersionScripts = '/path-to-version-scripts/';
  $repos = array(
      array(
        'Name' => 'dJomp.co.uk',
        'UpdateScript' => 'djomp.co.uk',
        'LiveVersionScript' => 'djomp.co.uk',
        'TestVersionScript' => 'test.djomp.co.uk',
      ),
/*
      array(
        'Name' => '',
        'UpdateScript' => '',
        'LiveVersionScript' => '',
        'TestVersionScript' => '',
      ),
*/
    );
 
?>
<body>
<?
$output = array();
 
foreach($repos AS $ref => $repo)
{
  if (isset($_GET['do']) && ($_GET['do'] == $ref))
  {
    echo '<h2>Updating '.$repo['Name'].'</h2>';
    exec($pathToUpdateScripts.$repo['UpdateScript'], $output);
  }
}
 
if (count($output))
{
  echo '<pre>',implode("\n",$output),'</pre>';
}
 
?>
 
<hr>
<table border>
  <tr>
    <th>Repo</th>
    <th>Test Version</th>
    <th>Real Version</th>
    <th>Update</th>
  </tr>
<?
foreach($repos AS $ref => $repo)
{
  $testVersion = exec($pathToVersionScripts.$repo['TestVersionScript']);
  $realVersion = exec($pathToVersionScripts.$repo['LiveVersionScript']);
  echo '<tr>',
      '<td>',$repo['Name'],'</td>',
      '<td>',$testVersion,'</td>',
      '<td>',$realVersion,'</td>',
      '<td>',
        ($realVersion == $testVersion
          ? '-' 
          : '<a href="index.php?do='.$ref.'">Update</a>'),
      '</td>',
    '</tr>';
}
?>
</table>
</body>
</html>

Now this script is a little more sensitive so secure it however you wish - at least behind a password somewhere. The worst it can do is update your live site to your latest commit, but depending on what you commit that could be bad enough!

Conclusion

That's it. You can now update your live site in two steps:

  1. Commit your changes
  2. Click a link on a private webpage

I think that's pretty nifty. I do abuse a few of my subversion repositories, though; there are times when I need to submit a change but I don't have a local test area, so I make the changes and submit them to the test server to check they even work!

If you're using an editor that has subversion support built-in then it becomes even easier - you only need your editor and a web browser open to be developing your site remotely.

More techy stuff coming soon

Tags:

I've had plenty of experience setting things up just how I like them, and I feel it's long overdue that I share these with everyone. It may not be quite how you want it - but it might be enough inspiration to get you on your way to setting things up as you want.

Subversion

Version control is a must-have for any code. I never bothered in the past and there were certainly times when I regretted not being able to grab an old version of the code after something went hideously wrong. I would now use it for everything, and I recommend everyone does.

I personally use Subversion. With tools like TortoiseSVN for Windows making managing your working copy really easy, with a clean graphical interface, you have no excuses any more!

These articles are going to deal with the server-side of setting up Subversion, rather than day-to-day use of Subversion with your code. It's a few bits of cleverness I've come across that have really helped me out.

Hosting Websites

I've run my own server for a number of years now.

Syndicate content