Gitolite Public HTTP Access

:: tutorial, git

I’ve always liked self hosting as it gives you the most flexability. With projects like gitolite you can ditch the heavy database, the server side scripting languages and run a nicely configured and feature rich server on something as small as a Raspberry Pi.

Gitolite supports gitweb, but when running on Debian it required me to install apache2, and configure CGI. Other distros I’ve tried were even worse with dependencies. All I want to is give public, read-only access via http. Can’t be too difficult, right?

Every time the user interacts with their local and remote repos, a set of hooks are triggered. There are many local and remote hooks, which you can read about here. The hook we will be using is post-update. It is called whenever a push occurs from local to remote repos, and after all refs are updated.

If you’d like to play along you can access the code:

$ git clone https://commentedcode.org/git/blog/gitolite-public-access.git \
    /tmp/pub_access

Setup

The setup has two parts: Gitolite and your WebServer. Once its setup, you can easily enable this feature through your gitolite-admin repo. All the steps are assuming you’re running the latest gitlote (v3). I’ve had it running on v2 just fine and the config wasn’t much different.

Gitolite Configuration

The first file you’ll need to edit is gitolite.rc. If you followed the gitolite instructions for their basic setup, it will be in the home directory of your git user: $HOME/.gitolite.rc

You’ll need to enable site-local code by uncommenting the following line. The default location works fine.

LOCAL_CODE => "$ENV{HOME}/local",

Next you’ll need to enable Repo Specific Hooks. Uncomment the following line

# allow repo-specific hooks to be added
'repo-specific-hooks',

You can now create the directory to store the hooks. You can store any hooks you’d like to run, at any stage of the server side process. For this task we’ll be creating a hook for the post-update phase of a commit.

user$ sudo su - git
git$ mkdir -p $HOME/local/hooks/repo-specific

make-public Hook

The script is pretty simple. When a push is completed the hook will copy the server’s contents to a directory that will be hosted via http. Anonymous users will be able to pull the code but won’t be able to push anything.

#!/bin/sh
#
# Description: Creates HTTP R/O Repo when update is pushed
#

# Name of Repo
REPO=$(echo $PWD | sed "s|$HOME/repositories/||")

# Location of your repo on the server
GIT_DIR=$HOME/repositories/$REPO

# Location of your punblic version of the repo
HTTP_DIR_BASE=$HOME/http
HTTP_DIR=$HTTP_DIR_BASE/$REPO

# Update local repo info
git update-server-info

# Make sure a clean copy is moved
if [ -d $HTTP_DIR ]
then
  rm -rf $HTTP_DIR
fi
mkdir -p $HTTP_DIR
cp -rf $GIT_DIR/* $HTTP_DIR

# Update index.html
cd $HTTP_DIR_BASE
./gen-index.sh

# Directories must have Read and Execute Permissions
# for apache to be able to navigate them
find $HTTP_DIR_BASE -type d -exec chmod a+rx {} \;

# Files must have Read Permissions for apache
# to be able to read them.
find $HTTP_DIR_BASE -type f -exec chmod a+r {} \;

# Change owner to git and web
chown -R git:www-data $HTTP_DIR_BASE

# Display a message on the client side to show
# the action has been performed.
echo "Updated Public Access"

Modify the script to match your locations. The copy it to your repo-specific directory.

git$ cp /tmp/pub_access/git/local/hoosk/repo-specific/make-public \
         $HOME/local/hooks/repo-specific
git$ chmod 755 $HOME/local/hooks/repo-specific/make-public

Web Access

You’ll need to create a directory that the git user can copy the git server files to that your web server will have access. The simplest way of doing this is making a directory in the git users’s home and giving that user group access to web content. Since your git user can only be accessed via ssh keys, the security threshold is pretty low.

user$ sudo gpasswd -a git www-data
user$ sudo su - git
git$ mkdir $HOME/http

To give our service a little bit of a UI, we can automatically generate an index.html file every time the hook gets called. The file won’t be fancy, just list our license defaults, how to access the repo and a list of all projects. You can modify it to add other features if you’d like.

git$ cp /tmp/pub_access/git/http/gen-index.sh $HOME/http
git$ chown -R git:www-data $HOME/http
git$ chmod 755 $HOME/http/gen-index.sh
git$ $HOME/http/gen-index.sh
git$ ls $HOME/http/
gen-index.sh index.html

Running the script we’ve created the index.html file, but it will not list any repos yet.

Server Configuration

I have included a snippet for nginx, but the concept works for all web servers. You want to point http://example.com/git to /home/git/http. With nginx its as easy as

server {
  ...
  location /git {
    alias /home/git/http;
  }
}

The snippet included adds logging support and can be included in any virtual hosted setup.

server {
  ...
  include snippets/public-git-repo.conf;
}

Restart your webserver and you should be able to access the index.html at /git.

Usage

To use this hook you just need to add an option to any repo, or group of repos you want to be made public. Pull your gitolite-admin project and modify the gitolite.conf file.

repo blog/gitolite-public-access.git
  RW+ = @devs
  option hook.post-update = make-public

After every push (post update), your repo will run the make-public hook and generate the new static pages. The output from your push will actually printout the results from our hook, see the line “remote:”

user$ git push origin

Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 935 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
remote: Updated Public Access
To commentedcode.org:testing.git
   b813a96..e8b7e6b  master -> master
Done

Once the public repo has been updated you’ll be able to clone using http/https.

Private R/W Access: git clone git@example.com:project.git

Public R/O Access: git clone http://example.com/git/project.git

Special Case: Adding to pre-existing repos

If you are adding a new hook to a preexisting repo you’ll need to run gitolite setup to have the hooks propogate. Otherwise, if you are adding a new repo this will occur automatically. Then you can run the hook manually to generate the output rather than waiting for the next push.

git$ gitolite setup
git$ cd repositories/gitolite-public-access.git
git$ $HOME/local/hooks/repo-specific/make-public
Updated Public Access
git$