Setting Up a FreeBSD SVN Mirror

According to discussions on the FreeBSD-stable list, CVS support for FreeBSD sources is not just dying, it’s practically dead. (I’ll skip over the jokes about Netcraft confirming it…) So I setup an SVN mirror of the FreeBSD base and ports repositories and documented the process a little. I did this mainly because there are a lot of servers in two locations that need frequent access to the base and ports repositories, and there are currently cvsup mirrors running (one at each place) to keep the load off the upstream servers and for faster local access.

Summary

Before getting too far into it, here is the most relevant post in the thread on the FreeBSD-stable list that gives a decent summary of what has changed.

Copying/Paraphrasing from that link:

  • RELENG_9 is still alive and will continue for the foreseeable future.
  • You can still track 9-STABLE via cvs/cvsup.
  • RELENG_9_1 does not exist (yet?).
  • There will be *no* RELENG_10* tags on cvs

Rather than being caught off guard for 10.x, switch now, and you might need to anyhow if you plan on tracking anything but 9-STABLE.

A couple links to more FreeBSD+SVN info
http://wiki.freebsd.org/PortsSubversionPrimer
http://www.freebsd.org/doc/en/articles/committers-guide/article.html#SUBVERSION-PRIMER
http://mebsd.com/configure-freebsd-servers/update-freebsd-source-tree-using-subversion-svn.html

Subversion can be installed from /usr/ports/devel/subversion – and if you plan on using Apache to serve your SVN repositories over HTTP, you need to check the option to build the DAV modules when configuring both Subversion and Apache. If you already have Subversion and/or Apache installed but didn’t select the DAV module options, you may need to rebuild the ports.

What are the “base”, “doc” and “ports” repositories exactly?

Base – This includes all of the various FreeBSD OS and related code. Basically if you were getting RELENG_<foo> it will come from base. All of the branches and tags are there, they are just worded slightly differently. A few random examples:

# The RELENG_9_0 errata/security branch (9.0-RELEASE-pX)
svn co svn://svn.freebsd.org/base/releng/9.0/
# The RELENG_8 stable branch (8-STABLE)
svn co svn://svn.freebsd.org/base/stable/8/
# FreeBSD 8.3-RELEASE exactly (no errata)
svn co svn://svn.freebsd.org/base/release/8.3.0/

Doc – FreeBSD documentation

Ports – The FreeBSD ports tree. Because the ports tree is not branched the way the base repository is, you almost always want ‘head‘ here:

svn co svn://svn.freebsd.org/ports/head/

However you can get the ports tree at a certain FreeBSD Release by using a tag such as:

svn co svn://svn.freebsd.org/ports/tags/RELEASE_9_0_0/

Creating a Subversion Mirror

Now, on to the mirroring part. It’s much faster to start your mirror from a seed file, rather than making it completely download and rebuild the entire repository from scratch.

You can fetch from the seed files as described below, and then bring the repository up-to-date the rest of the way with svnsync. The doc tree can be done the same way, but since I don’t build docs I did not mirror it. I used ftp2.freebsd.org in this example, feel free to substitute your favorite local mirror, provided it has copies of the files.

# mkdir -p /home/freebsd-svn/
# cd /home/freebsd-svn/
# fetch ftp://ftp2.freebsd.org/pub/FreeBSD/development/subversion/svnmirror-base-r238500.tar.xz
# tar xvzf svnmirror-base-r238500.tar.xz
# svnsync sync file:////home/freebsd-svn/base
# cd /home/freebsd-svn/
# fetch ftp://ftp2.freebsd.org/pub/FreeBSD/development/subversion/svnmirror-ports-r301235.tar.xz
# tar xvzf svnmirror-ports-r301235.tar.xz
# svnsync sync file:////home/freebsd-svn/ports

Now that you have updated copies of the repositories, you can setup a cron job to run a script to pull in new changes. The script is simple:

#!/bin/sh
/usr/local/bin/svnsync sync file:////home/freebsd-svn/base
/usr/local/bin/svnsync sync file:////home/freebsd-svn/ports

I added that to a cron job that runs once per hour, but depending on how often you actually update your own servers using it, you could probably dial that back a bit.

Serving Your Mirror

Now that you have a local mirror, but how to serve that up to your other machines? Well, one way is via ssh, and another is svnserve, but for various reasons I prefer to do this via HTTP, so I setup Apache with DAV to hand it out. I’ll also explain svnserve later in this post.

Serving with Apache

First, as I mentioned above, you’ll want to make sure you compiled both Subversion (/usr/ports/devel/subversion) and Apache (/usr/ports/www/apache22) with the relevant DAV module options. Then you can setup the Apache config.

In the main httpd.conf file (/usr/local/etc/apache22/httpd.conf), check that the DAV modules are loaded. This may already have been done automatically by the port:

LoadModule dav_svn_module     libexec/apache22/mod_dav_svn.so
LoadModule authz_svn_module   libexec/apache22/mod_authz_svn.so

Uncomment the line for the extra/httpd-dav.conf file

Include etc/apache22/extra/httpd-dav.conf

Then save/exit after you’ve made any other changes you want to that file – if you’ve never set Apache up on that server, you’ll probably want to set the server name, admin e-mail, hostname, and point it to a directory that won’t serve up the Apache default files. There are many Apache tutorials out there if you need help with this part.

Now edit /usr/local/etc/apache22/extra/httpd-dav.conf

The first thing I do here is comment out or remove any directives referring to uploading. Then at the end, I add a section that actually allows the SVN bits to be served up:

<Location /freebsd-svn>
    DAV svn
    SVNParentPath /home/freebsd-svn
    Order deny,allow
    <LimitExcept GET PROPFIND OPTIONS REPORT>
        Deny from all
    </LimitExcept>
</Location>

Because you can’t exactly commit to these repositories anyhow, that will deny anyone access that tries to write.

After making those changes, save/exit and restart apache. An apachectl configtest would be prudent to ensure the remainder of the config is proper.

Now you should be able to access the repositories over HTTP! Note that you can’t just browse to http://your-server/freebsd-svn – you’ll need to use the name of the SVN repo inside, such as http://your-server/freebsd-svn/base or http://your-server/freebsd-svn/ports

To checkout the ports tree to your current directory, you’d use:

svn co http://your-server/freebsd-svn/ports/head/ .

Serving with svnserve

If you don’t want to use HTTP, you can setup svnserve to use the svn protocol on port 3690. It’s pretty simple actually, just edit /etc/rc.conf and add:

svnserve_enable="YES"
svnserve_flags="-d -R --listen-host 0.0.0.0"
svnserve_data="/home/freebsd-svn"
svnserve_user="root"
svnserve_group="wheel"

The “-R” in the flags tells it to be read only. I would suggest adding an svn user and group to run the daemon under, but it will run as root if you configure it to do so manually as I did in the example.
NOTE: svnserve does not appear to properly handle dual stack IPv4/IPv6, so you may need to use the IPv4 IP or a DNS alias without a AAAA when accessing the repo such as:

svn so svn://x.x.x.x/ports/head/ .

If anyone knows a good way to have it listen on both, I’d love to hear it. I’ll update this post if I get that figured out. That’s another reason I prefer HTTP at the moment.

Other Ways to Serve

Accessing over ssh is left as an exercise for the reader. :-)

Handy Commands

Here are a couple other handy commands (will add more as I find them or they are suggested):

Overwriting local changes when updating

The default behavior of csup/cvsup was to overwrite your locally changed files. By default Subversion will leave your changes alone and try to merge them. To get the csup style behavior with Subversion, you must revert all local changes first and then update.

svn revert -R /usr/src
svn update

The csup style behavior is more useful as a consumer applying local patches and needing to revert to the newest upstream when it’s updated. The svn style behavior is more useful for developers so they don’t risk losing their work. This will NOT remove any distfiles or packages from ports, as those have svn:ignore set in the properties of the ports repository.

Adopting an existing tree

I was hoping to find a way to “adopt” an existing directory into Subversion rather than deleting and checking out a whole tree again, but so far I haven’t found a good way to do that cleanly — if anyone knows, I’m all ears. So far it’s easiest to rm -rf /usr/src or /usr/ports when checking out the SVN copy. I tried using –force when doing svn co and it seemed to want to work but I was left with a giant mess in the end.

Switching Branches

Say you want to move from tracking 9-STABLE to tracking 10-STABLE, that’s really easy:

svn sw svn://svn.freebsd.org/base/stable/10

Another practical example, say you start out by grabbing RELENG_9_0 (the security branch):

svn co svn://svn.freebsd.org/base/releng/9.0/

And then the time comes that you want to track 9.1 instead, then you switch like so:

svn sw svn://svn.freebsd.org/base/releng/9.1/

And then you end up with RELENG_9_1, it’s as easy as that. No need to redo the entire checkout.

That’s All…

I always welcome feedback – if you know of a better, easier, or more correct way to do something I’ve described here – I’m more than willing to make corrections.

Much of the information in this post was gleaned from the freebsd-stable thread linked earlier, along with some info from other sources and personal experience.

2 thoughts on “Setting Up a FreeBSD SVN Mirror

  1. there is no need to run svnserve under root:wheel
    my setup:
    * fetch tarballs
    * pw adduser svn -d /path/svn_user/home
    * under user svn extract tarball into ~
    *and finally:
    crontab -u svn -l
    0 */2 * * * /usr/local/bin/svnsync sync file:///usr/local/repositories/freebsd/ports/
    0 */2 * * * /usr/local/bin/svnsync sync file:///usr/local/repositories/freebsd/base/

  2. I mentioned that in the post but didn’t go into detail about it since I was using HTTP, thanks for the extra info for those who take that path!