gitgithooksgit-post-receive

Run git post-receive hook as user1, and checkout as user2


I'm setting up a deploy-on-git-push process on a remote Debian server. It is basically the common approach of having a bare repository with a post-receive hook which does a checkout to the web server's docroot, more or less.

I've used slightly simpler variations of this set up successfully for years, but this time around I'm complicating it by trying to separate ownership of the git repo and the site files between 2 different users. I don't want the user who can SSH in (the git push happens over SSH) to have write perms to the site, and vice-versa (the site user shouldn't have write perms to the git repository).

I push over SSH to the repo, and this means any post-receive hook runs as the user I SSH in as - git-user in this case. So the post-receive hook can't do the checkout itself, as the site files would end up being owned by git-user, not site-user.

So my post-receive simply touches a trigger file, /var/run/deploy. A separate script is run from site-user's cron frequently, eg every minute, looking for that file. If it sees it, it does a checkout to /var/www/site. The relevant part of that script looks something like:

# Running as site-user
mkdir $NEW \
  && cd $NEW \
  && git --work-tree=. --git-dir=/var/gitrepos/my_site.git checkout -f www

However this fails with:

fatal: Unable to create '/var/gitrepos/my_site.git/index.lock': Permission denied

This is true, site-user - intentionally - does not have write permission to /var/gitrepos/my_site.git. I am not sure why doing a checkout to a different directory needs to create a lockfile in the repo, but apparently it does, and I guess I shouldn't fight that.

So what are the options?

Any other neat options I am missing?

UPDATE

As suggested by @Matt below I tried setting GIT_INDEX_FILE to a writeable (by site-user) file outside the repository. This does seem to get past the first problem, but still fails with:

error: Unable to create '/var/gitrepos/my_site.git/HEAD.lock': Permission denied

I don't understand why a checkout to a new location needs to modify anything in the repository?


Solution

  • Another option would be for the git-user to create an archive that contains all the relevant files to be deployed (think of it as an artefact). This can be done in the post-receive hook before touching the trigger file. To create the ZIP/TAR, you can use the git archive command which facilitates this step.

    As soon as the cron-job runs and a deploy is triggered, the site-user extracts the contents of the archive into /var/www/site and removes the archive.

    This way, the git-user has no access to the webroot. At the same time, the site-user does not even need read-access to the repository.


    It is also possible to specify an alternative index-file with the environment variable GIT_INDEX_FILE to circumvent the default location ($GIT_DIR/index). But I don't know if Git needs write access to other files/folders as well.