Fixing permissions in Subversion post-commit hooks
October 27, 2009,
Subversion is a great version control system and it allows for a lot of tweakability, but a mistake with permissions is easily made...and easily fixed.
The code we develop for customers and ourselves are kept in several Subversion repositories. The reasons we chose for Subversion are historical, since we grew up with CVS.
One of the things we do for some Subversion repositories is to do an automatic checkout, or an automatic update of a checked out repository. For us it works very well, but there is one thing which took us a bit longer to solve (mostly because of other more urgent things to fix): permissions in checkouts done with a post-commit hook.
In every Subversion repository there is a directory hooks, in which you can put extra programs to control what happens when there is a commit. Usually you don't want to do anything, but it can come in handy. The directory hooks contains a few example scripts. One post-commit script we use for some repositories is to do a fresh checkout or update of a checkout every time there is an update.
Problems with post-commit hooks
If you use the svn protocol over SSH to access repositories what happens when you do a checkout or commit is that you log in with SSH to the server and launch a temporary local Subversion server, which runs as the user (and with attached permissions) you log in with. The hooks from the Subversion repository will also be run as that user.
In our case this is not OK. There are two users, both have a different user id and main group, but are in a shared group (svn). The checkout and all contents of the checked out versions of repositories on our system are typically owned by group svn. An update done as one user interferes with the checkout from another user and writes files and directories using the main group for that other user, sometimes resulting in a not fully completed checkout.
Linux and groups
On a Linux system every user belongs to one or more groups. Using the groups command you can see which groups you currently belong to, for example:
$ groups armijn
This is not necessarily the same list as the set of groups you are in on the system, which you can check with the id command (but you have to add the username as a parameter):
$ id armijn uid=500(armijn) gid=500(armijn) groups=500(armijn),501(test)
Forcing the current group can be done using the newgrp command:
$ groups armijn $ newgrp test $ groups test armijn
The current group will have changed to test:
$ touch /tmp/foobar $ ls -l /tmp/foobar -rw-r--r-- 1 armijn test 0 2009-10-26 22:39 /tmp/foobar
Changing groups again will change the current group:
$ newgrp armijn $ groups armijn test $ touch /tmp/foobaz; ls -l /tmp/foobaz -rw-rw-r-- 1 armijn armijn 0 2009-10-26 22:40 /tmp/foobaz
newgrp and Subversion post-commit hooks
A typical Subversion post-commit hook could look like this:
#!/bin/sh BASE=/tmp/foosvn svn update $BASE
It updates the checkout in $BASE on the server (an initial checkout should be made by hand first in this case) every time there is a commit.
These checkouts are run as the user which logs in which might not be desirable, as explained above. The logical thing to do is to change the current user id, for example:
#!/bin/sh newgrp BASE=/tmp/foosvn svn update $BASE exit
Unfortunately this does not work and if you do a commit it will hang without the desired results. To work around this you can make use of so called 'here documents' available in bash:
BASE=/tmp/foosvn newgrp svn <<FOO /usr/bin/svn update $BASE exit FOO
With this little trick we got everything to work as expected: files and directories in the checked out version of the repository are owned by group svn.