gitolite cookbook

(a.k.a. "stop all that rambling and just tell me what I need to do!")


Documentation is meant to be as complete as possible, which means it attempts to cover all situations and scenarios. That makes it harder to read.

However, if you make some assumptions, remove the rationale, justification, exceptions and special cases, etc., and generally just say what is to be done rather than explain why, many tasks can be described very easily.

Or, if the main documentation already does that, a cookbook may help you find it faster, simply because it's organised differently.

Maybe this will help. If you run into problems, please check the main documentation before asking for help.

administration

separating "key admin" from "repo admin"

In gitolite, the person who controls the keys is the most critical in terms of security -- because he can always add his own key in your name :-)

Traditionally, the same person also administers repos and permissions. But sometimes you want to separate them.

To separate those roles, put the following in conf/gitolite.conf, and let the repo-manager manage everything through "actual.conf":

repo gitolite-admin
    RW+ = key-manager repo-manager

    RW+ VREF/NAME/                    = key-manager
    -   VREF/NAME/keydir/             = @all
    -   VREF/NAME/conf/gitolite.conf  = @all

include "actual.conf"

access

looking up repo access from external tools

There are two supported interfaces for this, one in perl and one in shell. Other languages should probably use the shell mode. (The shell mode has a very convenient "batch" mode if you need to check many repos at once).

Perl interface: A good intro to this, including a link to code, using gitweb as an example can be found by looking for 'repo-specific authorisation in gitweb' in the page on allowing access to gitweb and git-daemon. Some notes:

  • be sure to read the comments in the code to learn exactly how to adapt it to your needs
  • in place of the can_read function in that code, you can of course use can_write. In fact, reading the comments in "Easy.pm" (look for it in the source) shows you several other interesting tests you can make, like is_admin, in_group, and owns.

Shell interface: If you want to do this from shell, it's even easier. The same "Easy.pm" source contains comments that show shell equivalents for each of the functions it exports, but here's a sample:

if gitolite access -q reponame username W
then
    ...

You can even test for access to specific branches:

if gitolite access -q reponame username W master
then
    ...

If your gitolite is older than v3.6, you must use the full ref name; just 'master' won't do.

allowing access by other programs

Giving external tools (like apache) access to gitolite repositories involves making sure that the unix owner/group and permissions settings allow this. This is all described in the UMASK section in the page on the rc file, because that's the only setting that gitolite controls; every thing else is pure Unix.

commands

adding your own commands

To add a command, say foo, do this:

  1. add this line in the rc file, within the %RC block, if it's not already present, or uncomment it if it's already present and commented out:

    LOCAL_CODE => "$ENV{HOME}/local",
    
  2. copy the program foo into $HOME/local/commands. (Don't forget the chmod +x!)

making commands available to remote users

Once you do the above, foo is available as gitolite foo. To make it available to remote users (as ssh git@host foo), add the line:

`foo`,

(including the comma at the end) to the ENABLE list in the rc file.

hooks

Note: the main documentation for this feature starts here.

adding your own update hooks

You have some update hooks (for example crlf checking) that you want to include in gitolite. Assuming the hook itself is tested and works as a normal git update hook does (i.e., conforms to what man githooks says an update hook should do), here's how to do this:

  1. add this line in the rc file, within the %RC block, if it's not already present, or uncomment it if it's already present and commented out:

    LOCAL_CODE => "$ENV{HOME}/local",
    
  2. copy your update hook to a subdirectory called VREF under this directory, giving it a suitable name (let's say "crlf"):

    # log on to gitolite hosting user on the server, then:
    cd $HOME
    mkdir -p local/VREF
    cp your-crlf-update-hook local/VREF/crlf
    chmod +x local/VREF/crlf
    
  3. in your gitolite-admin clone, edit conf/gitolite.conf and add lines like this:

        -   VREF/crlf       =   @all
    

    to each repo that should have that "update" hook.

    Alternatively, you can simply add this at the end of the gitolite.conf file:

    repo @all
        -   VREF/crlf       =   @all
    

    Either way, add/commit/push the change to the gitolite-admin repo.

adding other (non-update) hooks

Say you want other hooks, like a post-receive hook. Here's how:

  1. add this line in the rc file, within the %RC block, if it's not already present, or uncomment it if it's already present and commented out:

    LOCAL_CODE => "$ENV{HOME}/local",
    
  2. put your hooks into that directory, in a sub-sub-directory called "hooks/common":

    # log on to gitolite hosting user on the server, then:
    cd $HOME
    mkdir -p local/hooks/common
    cp your-post-receive-hook local/hooks/common/post-receive
    chmod +x local/hooks/common/post-receive
    
  3. run gitolite setup to have the hooks propagate to existing repos (repos created after this will get them anyway).

variation: maintain these hooks in the gitolite-admin repo

Important security note:

If you enable this, anyone who can push changes to the admin repo will effectively be able to run any arbitrary command on the server. See gitolite admin and shell access for more background.

If you want to maintain these update hooks (VREFs) or non-update hooks (post-update, pre-receive, post-receive) in the gitolite-admin repo, instead of having to log on to the server and make changes, the procedure is almost the same except for the following differences:

  1. add this line in the rc file, within the %RC block, if it's not already present, or uncomment it if it's already present and commented out:

    LOCAL_CODE => "$rc{GL_ADMIN_BASE}/local",
    

    Notice "local" is not in $HOME like in the previous examples!

  2. the hooks/scripts are to be added inside your gitolite-admin clone, not on the server. That is, whereever the above two sections say cd $HOME, you should read it as "cd /path/to/your/gitolite-admin-clone".

    (The directory local will be within this clone of course, not in $HOME.)

  3. add/commit/push as usual.

For update hooks, you will of course need to add VREF rule lines to appropriate repos in the conf file. For non-update hooks, you don't need to run 'gitolite setup' on the server; the push takes care of that.

(v3.6+) variation: repo-specific hooks

Until now, the non-update hooks you specified apply to all repos. Here's how to apply them only to certain repos:

  1. add this line in the rc file, within the %RC block, if it's not already present, or uncomment it if it's already present and commented out:

    LOCAL_CODE => "$rc{GL_ADMIN_BASE}/local",
    
  2. uncomment the 'repo-specific-hooks' line in the rc file or add it to the ENABLE list if it doesn't exist.

    If your rc file does not have an ENABLE list, you need to add this to the POST_COMPILE and the POST_CREATE lists. Click here for more on all this.

  3. put your hooks into your gitolite-admin clone, as follows:

    # on your workstation
    cd /path/to/your/gitolite-admin-clone
    mkdir -p local/hooks/repo-specific
    

    Now add your hooks to that directory, but instead of using the git "standard" names (pre-receive, post-receive, post-update), you use descriptive names (e.g. "deploy", "RSS-post", etc).

    For pre-receive or pre-auto-gc you should not use more than one hook. If you really need more than one, ask on the mailing list.

  4. add them to the repos you want them to be active in, in your conf file. For example:

    repo foo
        option hook.post-update     =   jenkins
    repo bar @baz
        option hook.post-update     =   deploy RSS-post
    
  5. add, commit, and push the admin repo.

triggers

adding your own triggers

First, write your trigger code, using the documentation here. Note especially the sections "common arguments" and "trigger-specific arguments". Look in the shipped triggers for ideas.

Note:

If your trigger is a perl module, as opposed to a standalone script or executable, please see the section on adding a perl module as a trigger.

Let's say your trigger is called foo, and it will be a PRE_GIT trigger.

  1. add this line in the rc file, within the %RC block, if it's not already present, or uncomment it if it's already present and commented out:

    LOCAL_CODE => "$ENV{HOME}/local",
    
  2. copy the program foo into $HOME/local/triggers. (Don't forget the chmod +x!)

  3. edit ~/.gitolite.rc again, and look for a PRE_GIT section. If it exists, add 'foo', (note the trailing comma!) to it. If it does not exist, add this block just before the ENABLE section:

    PRE_GIT =>
    [
        'foo'
    ],
    

    (again, note the trailing comma at the end of the block!)

    After saving the file, test that this worked by running gitolite query-rc PRE_GIT; your foo should be in the output list. If it's not, back up and double check your work.

That should be it.

VREFs

adding your own VREFs

Adding VREFs is the same as adding the update hook, so please see the section on "adding your own update hooks" above.

example VREFs

However, writing a proper VREF is not the same as writing a normal git "update hook". A proper VREF does more than just take 3 arguments, do something, and exit with a non-zero exit code for failure.

A VREF is called with at least 4 more arguments after the 3 that git itself defines for an update hook, plus usually at least one or two more. It also returns a lot more meaningful results via STDOUT.

Here are some examples. I also advise you to keep a browser tab open to the doc on VREF arguments as you look at these.

Important:

VREFs only come into play for push operations. When we say "access" in this section, we mean only write access. If you want to restrict it even for reads, you can do this in a PRE_GIT trigger; there's an example in contrib/triggers/IP-check in the gitolite source tree that may be useful as a template.

example 1: restricting by day of week

Here's the conf extract to say "junior devs can only push on weekdays":

repo foo
    -   VREF/DAY/Sat    =   @junior-devs
    -   VREF/DAY/Sun    =   @junior-devs

The code for this VREF is ridiculously simple:

#!/bin/bash
echo VREF/DAY/`date +%a`

On encountering the VREF rules (i.e., when a junior dev tries to push to this repo), gitolite calls the "DAY" VREF-maker. The code within the VREF maker simply echoes something like "VREF/DAY/Mon", where the last part is whatever the actual day of the week happens to be.

This output is then treated as a virtual ref and matched against the rules. On a Monday to Friday, nothing happens, because the VREFs generated do not match any deny rules (or indeed any rules at all). On weekends, they do match, and the push is denied.

example 2: restricting by source IP address

This one restricts junior developers to push to this repo only from a specific network block. The conf file here is slightly different. We know that the VREF-maker will return with some network address (as you'll see in the code later), so we set it up so that the correct network block is allowed and anything else is disallowed:

repo foo
    RW+ VREF/FROM/192.168.48.0/23   =   @junior-devs
    -   VREF/FROM                   =   @junior-devs

The code is not that complex. We take the user's actual IP address (this is available as the first word in $SSH_CONNECTION for ssh users, and for smart-http users, gitolite fakes it and creates that variable!). We then treat the "23" in the VREF rule, which appears to the code as $9, as the number of bits in the network address, then compute the network address for the users IP with that number of bits.

This network address is then sent back. (The $9 is added back at the end, but this is only to enable it to match the VREF rule).

#!/bin/bash

# exit if no arguments were supplied to the VREF.  This covers the
# second VREF rule in the conf above
[ -n "$8" ] || exit 0

from=${SSH_CONNECTION%% *}

eval `ipcalc -n $from/$9`       # sets env var "NETWORK"

echo VREF/FROM/$NETWORK/$9

For a source IP of 192.168.49.97, this runs ipcalc -n 192.168.49.97/23, which gives the network address 192.168.48.0. The echo then just sends back VREF/FROM/192.168.48.0/23. This VREF matches the RW+ line.

But if the IP is, say, 192.168.45.67, running ipcalc -n 192.168.45.67/23 gives you 192.168.44.0. The echo then send back VREF/FROM/192.18.44.0/23, which won't match the RW+ line, but will match the next one and thus deny the push.

(One thing that may not be obvious in this specific example is that you have to be careful when constructing the VREF rule. For any VREF/FROM/A/B, the result of running ipcalc -n A/B must be A, otherwise this won't work. That is, the bits of the network address after the network bits must be zero).

wild repos

making exceptions for specific instances of a wild repo

Sometimes you want to specify rules or config for specific instances of a wild repo, while still leaving it to be created by a user in the normal way.

This will not work:

repo foo/..*
    C                   =   blah blah
    RW+                 =   CREATOR
    RW                  =   WRITERS
    R                   =   READERS

# this does NOT work
repo foo/special-1
    RW+                 =   sitaram
    option  foo.bar     =   baz

The repo will be created as a normal (not wild) repo as soon as you push, which means you can't run the perms command on it to add people to the READERS and WRITERS roles, or do other things that wild repos allow.

The mental nudge you need to deal with this is to think what you would do if you had to write the same rule for more than one repo, say, any repo starting with "foo/special-" followed by a number. You'd use a pattern. And a pattern prevents an ordinary repo from being created.

So do this:

# this will work
repo foo/special-[1]
    RW+                 =   sitaram
    option  foo.bar     =   baz

Using a pattern for just one repo might sound like a kludge, but it's perfectly valid and supported.

Note that you do NOT need a "C" rule in there, since the pattern is a subset of the previous one (foo/..*), everything there applies to this repo also. If you're not sure why that is, you may need to read up on rule accumulation.

moving stuff around

moving a gitolite install from one machine to another

See moving servers.