diff options
author | Christian Ruppert <idl0r@gentoo.org> | 2012-05-20 20:31:17 +0200 |
---|---|---|
committer | Christian Ruppert <idl0r@gentoo.org> | 2012-05-20 20:31:17 +0200 |
commit | c8a8268f4148161033d1c06bd8f2495ef811927c (patch) | |
tree | 2aecf2a6bee259879fc5917482d859e082ebb83c | |
parent | Add find_pubkey() function (diff) | |
parent | v2.3.1 (diff) | |
download | gitolite-gentoo-c8a8268f4148161033d1c06bd8f2495ef811927c.tar.gz gitolite-gentoo-c8a8268f4148161033d1c06bd8f2495ef811927c.tar.bz2 gitolite-gentoo-c8a8268f4148161033d1c06bd8f2495ef811927c.zip |
Merge branch 'upstream'v2.3.1-gentoogitolite-gentoo-2.3.1
46 files changed, 1666 insertions, 524 deletions
@@ -4,8 +4,6 @@ # the "git describe" output for that refname to the tar. This lets you say # "cat .GITOLITE-VERSION" to find out which ref produced this tar -# Note: I'm not sure if that "-r" is a GNU tar extension... - branch := $(shell git rev-parse --abbrev-ref HEAD) $(branch): $(branch).tar @@ -1,5 +1,23 @@ # Gitolite README +**2012-03-25: there is a completely re-written version of gitolite in the "g3" +branch. The old version will now be supported only for existing users, and +for people who need the features that have not yet made it to the new one. To +reflect this, the `pu` branch has been merged into master and deleted.** + +The steps to use the new version are: + + git clone git://github.com/sitaramc/gitolite + cd gitolite + git checkout g3 + + +Documentation is currently available at [g3di] + +[g3di]: http://sitaramc.github.com/gitolite/g3/ + +---- + **Github users: please read the "wiki" link at the top of the page before submitting issues or pull requests**. @@ -16,3 +34,8 @@ it, features, contact/mailing list info, and so on. For convenience, here is a link to the [master table of contents](http://sitaramc.github.com/gitolite/master-toc.html), which is very useful to search using your browser's search function. + +---- + +License information for code and documentation is at the end of doc/index.mkd +(or you can read it online [here][license]). diff --git a/conf/example.gitolite.rc b/conf/example.gitolite.rc index 38dd2cc..8d528e6 100644 --- a/conf/example.gitolite.rc +++ b/conf/example.gitolite.rc @@ -19,8 +19,8 @@ $GL_CONF_COMPILED="$GL_ADMINDIR/conf/gitolite.conf-compiled.pm"; # DO NOT CHANGE THE NEXT FOUR LINES UNLESS YOU REALLY KNOW WHAT YOU'RE DOING. # These variables are set automatically by the install method you choose. # (PACKAGE MAINTAINERS: PLEASE READ doc/packaging.mkd) -# $GL_PACKAGE_CONF = ""; -# $GL_PACKAGE_HOOKS = ""; +$GL_PACKAGE_CONF = "/tmp/share/gitolite/conf"; +$GL_PACKAGE_HOOKS = "/tmp/share/gitolite/hooks"; # ------------------------------------------------------------------------------ # most often used/changed variables diff --git a/contrib/VREF/gl-VREF-COUNT b/contrib/VREF/gl-VREF-COUNT new file mode 100755 index 0000000..f61ab57 --- /dev/null +++ b/contrib/VREF/gl-VREF-COUNT @@ -0,0 +1,51 @@ +#!/bin/bash + +# gitolite VREF to count number of changed/new files in a push + +# see gitolite docs for what the first 7 arguments mean + +# inputs: +# arg-8 is a number +# arg-9 is optional, and can be "NEWFILES" +# outputs (STDOUT) +# arg-7 if the number of changed (or new, if arg-9 supplied) files is > arg-8 +# otherwise nothing +# exit status: +# always 0 + +die() { echo "$@" >&2; exit 1; } +[ -z "$8" ] && die "not meant to be run manually" + +newsha=$3 +oldtree=$4 +newtree=$5 +refex=$7 + +max=$8 + +nf= +[ "$9" = "NEWFILES" ] && nf='--diff-filter=A' +# NO_SIGNOFF implies NEWFILES +[ "$9" = "NO_SIGNOFF" ] && nf='--diff-filter=A' + +# count files against all the other commits in the system not just $oldsha +# (why? consider what is $oldtree when you create a new branch, or what is +# $oldsha when you update an old feature branch from master and then push it +count=`git log --name-only $nf --format=%n $newtree --not --all | grep . | sort -u | wc -l` + +[[ $count -gt $max ]] && { + # count has been exceeded. If $9 was NO_SIGNOFF there's still a chance + # for redemption -- if the top commit has a proper signed-off by line + [ "$9" = "NO_SIGNOFF" ] && { + author_email=$(git log --format=%ae -1 $newsha) + git cat-file -p $newsha | + egrep -i >/dev/null "^ *$count +new +files +signed-off by: *$author_email *$" && exit 0 + echo $refex top commit message should include the text \'$count new files signed-off by: $author_email\' + exit 0 + } + echo -n $refex "(too many " + [ -n "$nf" ] && echo -n "new " || echo -n "changed " + echo "files in this push)" +} + +exit 0 diff --git a/contrib/VREF/gl-VREF-DUPKEYS b/contrib/VREF/gl-VREF-DUPKEYS new file mode 100755 index 0000000..c7b7cf4 --- /dev/null +++ b/contrib/VREF/gl-VREF-DUPKEYS @@ -0,0 +1,45 @@ +#!/bin/bash + +# gitolite VREF to detect duplicate public keys + +# see gitolite doc/virtual-refs.mkd for what the arguments are +sha=$3 + +# git sets this; and we don't want it at this point... +unset GIT_DIR + +# paranoia +set -e + +# setup the temp area +export TMPDIR=$GL_REPO_BASE_ABS +export tmp=$(mktemp -d -t gl-internal-temp-repo.XXXXXXXXXX); +trap "rm -rf $tmp" EXIT; + +git archive $sha keydir | tar -C $tmp -xf - + # DO NOT try, say, 'GIT_WORK_TREE=$tmp git checkout $sha'. It'll screw up + # both the 'index' and 'HEAD' of the repo.git. Screwing up the index is + # BAD because now it goes out of sync with $GL_ADMINDIR. Think of a push + # that had a deleted pubkey but failed a hooklet for some reason. A + # subsequent push that fixes the error will now result in a $GL_ADMINDIR + # that still *has* that deleted pubkey!! + + # And this is equally applicable to cases where you're using a + # post-receive or similar hook to live update a web site or something, + # which is a pretty common usage, I am given to understand. + +cd $tmp + +for f in `find keydir -name "*.pub"` +do + ssh-keygen -l -f "$f" +done | perl -ane ' + die "FATAL: $F[2] is a duplicate of $seen{$F[1]}\n" if $seen{$F[1]}; + $seen{$F[1]} = $F[2]; +' + +# as you can see, a vref can also 'die' if it wishes to, and it'll take the +# whole update with it if it does. No messing around with sending back a +# vref, having it run through the matches, and printing the DENIED message, +# etc. However, if your push is running from a script, and that script is +# looking for the word "DENIED" or something, then this won't work... diff --git a/contrib/VREF/gl-VREF-EMAIL_CHECK b/contrib/VREF/gl-VREF-EMAIL_CHECK new file mode 100755 index 0000000..34c66f5 --- /dev/null +++ b/contrib/VREF/gl-VREF-EMAIL_CHECK @@ -0,0 +1,66 @@ +#!/usr/bin/perl + +# gitolite VREF to check if all *new* commits have author == pusher + +# THIS IS NOT READY TO USE AS IS +# ------------------------------ +# you MUST change the 'email_ok()' sub to suit *YOUR* site's +# gitolite username -> author email mapping! + +# See bottom of the program for important philosophical notes. + +use strict; +use warnings; + +# mapping between gitolite userid and correct email address is encapsulated in +# this subroutine; change as you like +sub email_ok { + my ($author_email) = shift; + my $expected_email = "$ENV{GL_USER}\@atc.tcs.com"; + return $author_email eq $expected_email; +} + +my ( $ref, $old, $new ) = @ARGV; +for my $rev (`git log --format="%ae\t%h\t%s" $new --not --all`) { + chomp($rev); + my ( $author_email, $hash, $subject ) = split /\t/, $rev; + + # again, we use the trick that a vref can just choose to die instead of + # passing back a vref, having it checked, etc., if it's more convenient + die "$ENV{GL_USER}, you can't push $hash authored by $author_email\n" . "\t(subject of commit was $subject)\n" + unless email_ok($author_email); +} + +exit 0; + +__END__ + +The following discussion is for people who want to enforce this check on ALL +their developers (i.e., not just the newbies). + +Doing this breaks the "D" in "DVCS", forcing all your developers to work to a +centralised model as far as pushes are concerned. It prevents amending +someone else's commit and pushing (this includes rebasing, cherry-picking, and +so on, which are all impossible now). It also makes *any* off-line +collabaration between two developers useless, because neither of them can push +the result to the server. + +PHBs should note that validating the committer ID is NOT the same as reviewing +the code and running QA/tests on it. If you're not reviewing/QA-ing the code, +it's probably worthless anyway. Conversely, if you *are* going to review the +code and run QA/tests anyway, then you don't really need to validate the +author email! + +In a DVCS, if you *pushed* a series of commits, you have -- in some sense -- +signed off on them. The most formal way to "sign" a series is to tack on and +push a gpg-signed tag, although most people don't go that far. Gitolite's log +files are designed to preserve that accountability to *some* extent, though; +see contrib/adc/who-pushed for an admin defined command that quickly and +easily tells you who *pushed* a particular commit. + +Anyway, the point is that the only purpose of this script is to + + * pander to someone who still has not grokked *D*VCS + OR + * tick off an item in some stupid PHB's checklist + diff --git a/contrib/VREF/gl-VREF-FILETYPE b/contrib/VREF/gl-VREF-FILETYPE new file mode 100755 index 0000000..e61acc6 --- /dev/null +++ b/contrib/VREF/gl-VREF-FILETYPE @@ -0,0 +1,45 @@ +#!/bin/bash + +# gitolite VREF to find autogenerated files + +# *completely* site specific; use it as an illustration of what can be done +# with gitolite VREFs if you wish + +# see gitolite docs for what the first 7 arguments mean + +# inputs: +# arg-8 is currently only one possible value: AUTOGENERATED +# outputs (STDOUT) +# arg-7 if any files changed in the push look like they were autogenerated +# otherwise nothing +# exit status: +# always 0 + +die() { echo "$@" >&2; exit 1; } +[ -z "$8" ] && die "not meant to be run manually" + +newsha=$3 +oldtree=$4 +newtree=$5 +refex=$7 + +option=$8 + +[ "$option" = "AUTOGENERATED" ] && { + # currently we only look for ".java" programs with the string "Generated + # by the protocol buffer compiler. DO NOT EDIT" in them. + + git log --name-only $nf --format=%n $newtree --not --all | + grep . | + sort -u | + grep '\.java$' | + while read fn + do + git show "$newtree:$fn" | egrep >/dev/null \ + 'Generated by the protocol buffer compiler. +DO NOT EDIT' || + continue + + echo $refex + exit 0 + done +} diff --git a/contrib/VREF/gl-VREF-MERGE_CHECK b/contrib/VREF/gl-VREF-MERGE_CHECK new file mode 100644 index 0000000..0542a87 --- /dev/null +++ b/contrib/VREF/gl-VREF-MERGE_CHECK @@ -0,0 +1,49 @@ +#!/usr/bin/perl +use strict; +use warnings; + +# gitolite VREF to check if there are any merge commits in the current push. + +# THIS IS DEMO CODE; please read all comments below as well as +# doc/virtual-refs.mkd before trying to use this. + +# usage in conf/gitolite.conf goes like this: + +# - VREF/MERGE_CHECK/master = @all +# # reject only if the merge commit is being pushed to the master branch +# - VREF/MERGE_CHECK = @all +# # reject merge commits to any branch + +my $ref = $ARGV[0]; +my $oldsha = $ARGV[1]; +my $newsha = $ARGV[2]; +my $refex = $ARGV[6]; + +# The following code duplicates some code from parse_conf_line() and some from +# check_ref(). This duplication is the only thing that is preventing me from +# removing the "M" permission code from 'core' gitolite and using this +# instead. However, it does demonstrate how you would do this if you had to +# create any other similar features, for example someone wanted "no non-merge +# first-parent", which is far too specific for me to add to 'core'. + +# -- begin duplication -- +my $branch_refex = $ARGV[7] || ''; +if ($branch_refex) { + $branch_refex =~ m(^refs/) or $branch_refex =~ s(^)(refs/heads/); +} else { + $branch_refex = 'refs/.*'; +} +exit 0 unless $ref =~ /^$branch_refex/; +# -- end duplication -- + +# we can't run this check for tag creation or new branch creation, because +# 'git log' does not deal well with $oldsha = '0' x 40. +if ( $oldsha eq "0" x 40 or $newsha eq "0" x 40 ) { + print STDERR "ref create/delete ignored for purposes of merge-check\n"; + exit 0; +} + +my $ret = `git rev-list -n 1 --merges $oldsha..$newsha`; +print "$refex FATAL: merge commits not allowed\n" if $ret =~ /./; + +exit 0; diff --git a/contrib/adc/fork b/contrib/adc/fork index 81a0f3e..f144c91 100755 --- a/contrib/adc/fork +++ b/contrib/adc/fork @@ -24,7 +24,15 @@ echo $GL_USER > gl-creater git config gitweb.owner "$GL_USER" ( $GL_BINDIR/gl-query-rc GL_WILDREPOS_DEFPERMS ) | SSH_ORIGINAL_COMMAND="setperms $to" $GL_BINDIR/gl-auth-command $GL_USER -cp -R $GL_REPO_BASE_ABS/$from.git/hooks/* $GL_REPO_BASE_ABS/$to.git/hooks + +# symlink hooks +shopt -s nullglob +# the order is important; "package" hooks must override same-named "user" hooks +for i in `$GL_BINDIR/gl-query-rc GL_ADMINDIR`/hooks/common/* \ + `$GL_BINDIR/gl-query-rc GL_PACKAGE_HOOKS `/common/* +do + ln -sf $i $GL_REPO_BASE_ABS/$to.git/hooks +done if [ -n "$GL_WILDREPOS_DEFPERMS" ]; then echo "$GL_WILDREPOS_DEFPERMS" > gl-perms diff --git a/contrib/adc/help b/contrib/adc/help index 9c2289e..82912e0 100755 --- a/contrib/adc/help +++ b/contrib/adc/help @@ -37,11 +37,11 @@ [ -f $HOME/gl-adc-pre-help.txt ] && cat $HOME/gl-adc-pre-help.txt # default help text -cat <<EOF +echo " The following adc's (admin-defined commands) are available at this site. -creating a "fork" of a repo: +creating a 'fork' of a repo: the 'fork' adc forks a repo that you have read access to, to a repo that you have create rights to @@ -51,13 +51,13 @@ deleting/trashing repos: repo before you can 'rm' it. A different scheme of handling this is to use 'trash' to move the repo to - a "trashcan" area. You can then 'list-trash' to see what you have, and + a 'trashcan' area. You can then 'list-trash' to see what you have, and you can then 'restore' whichever repo you need to bring back. - More details can be found at: - http://sitaramc.github.com/gitolite/contrib/adc/repo-deletion.html + More details can be found in contrib/adc/repo-deletion.mkd (or online at + http://sitaramc.github.com/gitolite/wild_repodel.html) -EOF +" # post [ -f $HOME/gl-adc-post-help.txt ] && cat $HOME/gl-adc-post-help.txt diff --git a/contrib/update.detect-dup-pubkeys b/contrib/update.detect-dup-pubkeys deleted file mode 100755 index a32db7e..0000000 --- a/contrib/update.detect-dup-pubkeys +++ /dev/null @@ -1,74 +0,0 @@ -#!/bin/bash - -# update "hooklet" to detect duplicate public keys - -# This particular hooklet also serves as an example for people writing others. -# [It should be quite easy to figure out what parts apply to any hooklet and -# what parts are specific to *this* hooklet and its function.] - -# see hooks/common/update.secondary.sample for instructions on *enabling* -# hooklets - -# a hooklet is called as follows: -# git-receive-pack --> 'update' --> 'update.secondary' --> this script -# note: the same three arguments that git passes to the update hook are passed -# along to each hooklet. - -# the update hook, and therefore the hooklets, are called for *every* repo out -# there. If you want this hooklet to run only for certain repos, here's how: -[ "$GL_REPO" = "gitolite-admin" ] || exit 0 - -# superfluous, since update.secondary already did it, but I'd like to -# emphasise that all output MUST go to STDERR -exec >&2 - -# ---- - -# the main functionality of the hooklet starts here. In this one (and I -# suspect many others) we want to examine the actual files from the commit -# that was pushed. - -# get the tip commit being pushed -sha=$3 - -# git sets this; and we don't want it at this point... -unset GIT_DIR - -# paranoia -set -e - -# setup the temp area -export TMPDIR=$GL_REPO_BASE_ABS -export tmp=$(mktemp -d -t gl-internal-temp-repo.XXXXXXXXXX); -trap "rm -rf $tmp" EXIT; - -# now get the files into $tmp. - # (note: if your task does not require the actual files, and you can - # manage with "git cat-file -s" and so on, then you may not even need a - # $tmp; you may be able to do it all right in the repo.git directory) - -git archive $sha keydir | tar -C $tmp -xf - - # DO NOT try, say, 'GIT_WORK_TREE=$tmp git checkout $sha'. It'll screw up - # both the 'index' and 'HEAD' of the repo.git. Screwing up the index is - # BAD because now it goes out of sync with $GL_ADMINDIR. Think of a push - # that had a deleted pubkey but failed a hooklet for some reason. A - # subsequent push that fixes the error will now result in a $GL_ADMINDIR - # that still *has* that deleted pubkey!! - - # And this is equally applicable to cases where you're using a - # post-receive or similar hook to live update a web site or something, - # which is a pretty common usage, I am given to understand. - -cd $tmp - -# ---- - -# *finally*, the actual check you need to do in this hook: look for duplicate -# pubkeys and exit 1 if dups are found -for f in `find keydir -name "*.pub"` -do - ssh-keygen -l -f "$f" -done | perl -ane ' - die "$F[2] is a duplicate of $seen{$F[1]}\n" if $seen{$F[1]}; - $seen{$F[1]} = $F[2]; -' diff --git a/contrib/update.email-check b/contrib/update.email-check deleted file mode 100755 index 7610444..0000000 --- a/contrib/update.email-check +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/perl - -# Technical notes: - - # Gitolite specific script to check "author email" field of every commit - # pushed and to disallow if this email does not match the email that the - # user pushing is expected to have. - - # Use without gitolite is also possible; just substitute your access - # control system's notion of "user" for the env var GL_USER in the code - # below and probably call it "update" if you dont already have an update - # hook. - - # Mapping between "username" and "email address" is encapsulated in a - # subroutine for ease of changing; see code below. - -# Philosophical notes: - - # Doing this breaks the "D" in "DVCS", forcing all your developers to work - # to a centralised model as far as pushes are concerned. It prevents - # amending someone else's commit and pushing (this includes rebasing, - # cherry-picking, and so on, which are all impossible now). It also makes - # *any* off-line collabaration between two developers useless, because - # neither of them can push the result to the server. - - # PHBs should note that validating the committer ID is NOT the same as - # reviewing the code and running QA/tests on it. If you're not - # reviewing/QA-ing the code, it's probably worthless anyway. Conversely, - # if you *are* going to review the code and run QA/tests anyway, then you - # don't really need to validate the author email! - - # In a DVCS, if you *pushed* a series of commits, you have -- in some - # sense -- signed off on them. The most formal way to "sign" a series is - # to tack on and push a gpg-signed tag, although most people don't go that - # far. Gitolite's log files are designed to preserve that accountability - # to *some* extent, though; see contrib/adc/who-pushed for an admin - # defined command that quickly and easily tells you who *pushed* a - # particular commit. - - # Anyway, the point is that the only purpose of this script is to - # - pander to someone who still has not grokked *D*VCS - # OR - # - tick off an item in some stupid PHB's checklist - -use strict; -use warnings; - -# mapping between gitolite userid and correct email address is encapsulated in -# this subroutine; change as you like -sub email_ok -{ - my ($author_email) = shift; - my $expected_email = "$ENV{GL_USER}\@atc.tcs.com"; - return $author_email eq $expected_email; -} - -# print STDERR "SECONDARY HOOK:\n" . join(",", @ARGV, "\n"); - -my ($ref, $old, $new) = @ARGV; -for my $rev ( `git log --format="%ae\t%h\t%s" $new --not --all` ) { - chomp($rev); - my ($author_email, $hash, $subject) = split /\t/, $rev; - - die "$ENV{GL_USER}, you can't push $hash authored by $author_email\n" . - "\t(subject of commit was $subject)\n" - unless email_ok($author_email); -} - -exit 0; diff --git a/doc/1000-words.mkd b/doc/1000-words.mkd index 9fec921..321cc2e 100644 --- a/doc/1000-words.mkd +++ b/doc/1000-words.mkd @@ -147,7 +147,7 @@ lines for whatever repo you want too add. 3. `git add conf/gitolite.conf`, then `git commit`, then `git push` You do NOT add the repos directly anywhere on the server; you do it by -cloning, adding keys, and pushing. +cloning, adding repo and access lines, and pushing. ## #flow gitolite flow diff --git a/doc/CHANGELOG b/doc/CHANGELOG index e478ad4..30b3824 100644 --- a/doc/CHANGELOG +++ b/doc/CHANGELOG @@ -2,6 +2,11 @@ Major changes to gitolite, master branch only, most recent first, no dates but the tags can help you position stuff approximately [NYD = not yet documented due to lack of time...] + - v2.3.1 + + - fix bug where config statements would get ignored if you had a description + set. The biggest problem was that this would affect mirroring. + - v2.3 - added "M" permission to allow enforcing a "no-merges" policy. diff --git a/doc/admin.mkd b/doc/admin.mkd index f7738ac..df8906f 100644 --- a/doc/admin.mkd +++ b/doc/admin.mkd @@ -33,8 +33,8 @@ server by cloning the special 'gitolite-admin' repo on your workstation (`git clone git@server:gitolite-admin`), making changes, and pushing them. This section tells you how to add users and repos. - * ask each user who will get access to send you a public key. See other - sources (for example [here][genpub]) for how to do this + * ask each user who will get access to send you a public key. (Generating a + keypair is described somewhere in [this][gl_ssh] page). * rename each public key according to the user's name, with a `.pub` extension, like `sitaram.pub` or `john-smith.pub`. You can also use @@ -106,9 +106,8 @@ received from git. <font color="gray"> -> In addition, gitolite now contains the basic infrastructure to support -> multiple 'update' hooks without having to remember to chain them yourself. -> See `hooks/common/update.secondary.sample` for instructions. +> Also see the document on [virtual refs][vref] for a way to add additional +> checks that you might need. </font> @@ -278,84 +277,47 @@ owner name for wild repos. The rest of the setup is in the conf file. [**NOTE**: I would appreciate help testing these instructions] -Just copying everything won't work unless everything on the new server is -exactly the same. I suggest you don't try it unless you know what you're -doing. +Here's the simplest set of instructions, assuming the destination is a recent +gitolite (has the 'gl-admin-push' command). Unless specified, all steps are +on the *new* server. -**Assumptions** + * **install** gitolite. Don't worry about the pubkey used in the gl-setup + step -- for example this will do fine: - * you have not changed `$REPO_BASE` on either of the servers; if you did, - substitute accordingly - * the admin's name is "YourName" -- again, substitute accordingly! - * the "hosting user" on both servers is "git". Substitute whatever you're - actually using (for example, if you're installing using RPM/DEB, this - would be "gitolite") + ssh-keygen -q -N '' -f dummy + gl-setup -q dummy.pub -There are many ways of doing this, but the most *generic* set of steps are -given below. Please follow all the steps; do not skip or improvise! Ask me -if things are not clear -- you can help me fine tune this document :-) + * **edit** the rc file to have similar settings to the old one. - * (workstation, old server) **pull** the latest changes to the - `gitolite-admin` repo to your workstation, if you don't have them - already. You'll need them later on. - - * (old server) **disable** the old server so your users will not push any - changes to it. There are several ways to do this, but the simplest is to - insert this line at the top of `~/.gitolite.rc` on the old server: + Do not copy the entire file outright -- some of the variables (notably + `GL_PACKAGE_CONF` and `GL_PACKAGE_HOOKS`) are installation dependent and + should not be touched! Do a diff or a vimdiff and copy across only what + you know *you* changed on the old server. - exit 1; - - * (new server) **copy** the repos to the new server, **except** the - `gitolite-admin` repo and files called `gitolite-hooked` in the `hooks` - directory of each repo. - - That sounds complicated but it's not. It's just: - - cd $HOME - rsync -a olduser@oldhost:repositories . - mv repositories/gitolite-admin.git $HOME/old-gitolite-admin.git - find repositories -name gitolite-hooked | xargs rm - - Don't forget to chown repositories if git's UID changed. Gitolite expects - the hosting user to own all the files and directories it manages. + * **disable** the old server so your users will not push any changes to it. + There are several ways to do this, but the simplest is to insert this line + at the top of `~/.gitolite.rc` on the old server: - * (workstation, new server) **install** gitolite normally on your new - server. Use whatever install method suits you, but you must use the - **same** name for the admin ("YourName" in the install instructions). You - may use a different keypair if you need to, or use the same one that - currently gets access to the old server. - - * (new server) **edit** the `~/.gitolite.rc` file to match the settings on - the old server, if needed. Do not copy the entire file outright -- some - of the variables (notably `GL_PACKAGE_CONF` and `GL_PACKAGE_HOOKS`) are - installation dependent and should not be touched! Do a diff or a vimdiff - and copy across only what you know *you* changed on the old server. - - * (workstation) **push** the config to the new server. To do this, go to - your admin clone, and: - - * if you used a different keypair when installing to the new server, - copy that pubkey to this clone into `keydir/Yourname.pub`, then add - and commit the change to the pubkey - - cd gitolite-admin - cp path/to/new/YourName.pub keydir/YourName.pub - git add keydir - git commit -m "new server, new key" + exit 1; - * if you did *not* use a different keypair, just make a dummy commit + * **copy** the contents of `$REPO_BASE` in the old server to `$REPO_BASE` on + the new server. By default, as you know, these are both + `$HOME/repositories`. - git commit -m "new server" --allow-empty + * **`chown -R`** the files to the correct user if you copied using root. - * set the URL for the new server + * **fix up** the hooks - git remote set-url origin git@newserver:gitolite-admin + gl-setup - * push the config, including past history + * **trigger** a push to the admin repo - git push -f + git clone repositories/gitolite-admin.git /tmp/gitolite-admin + cd /tmp/gitolite-admin + git commit --allow-empty -m 'trigger compile on new server' + gl-admin-push -f -And that should be that! +Done. ### custom git config diff --git a/doc/delegation.mkd b/doc/delegation.mkd index 243e247..e62c990 100644 --- a/doc/delegation.mkd +++ b/doc/delegation.mkd @@ -42,9 +42,8 @@ well as *any* repo inside the `browsers` subdirectory, as members of the Each of these groups is called a **subconf** from here on. Then you designate a **sub-admin** to manage each subconf, and you ensure -(using the standard gitolite feature of restricting pushes by names of changed -files) that a sub-admin can make changes only to her subconf file and nothing -else. +(using [this][NAME] gitolite feature) that a sub-admin can make changes only +to her subconf file and nothing else. For example, Alice is in charge of all web browser development projects. Similarly, Bob takes care of web servers, and Mallory, as [tradition][abe] diff --git a/doc/developer-notes.mkd b/doc/developer-notes.mkd index 54c9665..29ae96f 100644 --- a/doc/developer-notes.mkd +++ b/doc/developer-notes.mkd @@ -79,7 +79,10 @@ Fedora has a very special setup, as follows: they want to * actual git repos are under "git" (or some such), and include the chmod g+s - (git init --shared) unix perms tricks for shared access + (git init --shared) unix perms tricks for shared access. (<font + color="gray">Starting with git 1.7.something, you would also need to + explicitly delete the new receive.denyNonFastForwards setting that git + seems to default to when you use --shared</font>). * but since they're coming through gl-auth, branch-level acls are in effect diff --git a/doc/gitolite.conf.mkd b/doc/gitolite.conf.mkd index 1e83da1..eaefde9 100644 --- a/doc/gitolite.conf.mkd +++ b/doc/gitolite.conf.mkd @@ -290,7 +290,7 @@ allowing the secret branches into it). The previous section is sufficient for most common needs, but gitolite can go a lot further than that. -### restricting pushes by dir/file name using NAME/ +### #NAME restricting pushes by dir/file name using NAME/ Here's a hopefully self-explanatory example. Assume the project has the following contents at the top level: a README, a "doc/" directory, and an @@ -475,15 +475,15 @@ Some usage hints: ### #mergecheck enforcing a no-merges policy -Some people want to enforce a no-merges policy for various reasons. This -behaviour can be enabled by suffixing an "M" to the end of any permission -starting with `RW` (i.e., all of them except `R`). So for instance, `RW` -becomes `RWM`, and `RW+` becomes `RW+M`, etc. +Some people want to enforce a no-merges policy for various reasons. The new +"M" qualifier can be used to specify who is allowed to push merge commits. +This works just like "C" and "D" in the previous section, so **please read +that for a more detailed description** and apply the same ideas, (including +the `@all` trick!) to "M" for "allow merge commits". -The rules are exactly the same as for "C" and "D": once a repo has an "M" -qualifier tied to any access rule, all rules for that repo are subject to -merge checking, and merge commits will only be allowed when the rule has the -"M" qualifier. +The only other thing to remember is that this qualifier, if used, goes at the +end of any permission starting with `RW` (i.e., all of them except `R`). For +example, `RW` becomes `RWM`, `RW+` becomes `RW+M`, `RW+CD` becomes `RW+CDM`. ## summary: permissions diff --git a/doc/gitolite.rc.mkd b/doc/gitolite.rc.mkd index 8fab3dc..2395af4 100644 --- a/doc/gitolite.rc.mkd +++ b/doc/gitolite.rc.mkd @@ -131,19 +131,28 @@ on feedback from my users to find or fix issues. The second choice is to give it a space separated list of settings you consider safe. (These are actually treated as a set of perl regular - expression patterns, and any one of them must match). For example: - `$GL_GITCONFIG_KEYS = "core\\.logAllRefUpdates core\\..*compression";` - allows repo admins to set one of those 3 config keys (yes, that second - pattern matches two settings from "man git-config", if you look). + expression patterns, and any one of them must match). + + For example, this allows repo admins to set one of those 3 config + keys (yes, that second pattern matches two settings from "man + git-config", if you look): + + $GL_GITCONFIG_KEYS = 'core\.logAllRefUpdates core\..*compression'; + + Each pattern should match the *whole* key (in other words, there + is an implicit `^` at the start of each pattern, and a `$` at the + end). + + Note: if you don't know regex syntax, please learn, but briefly, a + literal period must be escaped with a backslash, as you can see in the + example above. In addition, if you use double quotes instead of single + quotes, you will need to escape the backslash itself, like `"foo\\..*"`. + + It's probably simplest to stick to single quotes. The third choice (which you may have guessed already if you're familiar with regular expressions) is to allow anything and everything: - `$GL_GITCONFIG_KEYS = ".*";` - - NOTE that due to some quoting and interpolation issues I have not been - able to look at, a literal "." needs to be specified in this string as - `\\.` (two backslashes and a dot). So this is how you'd allow any keys in - the "foo" section: `$GL_GITCONFIG_KEYS = "foo\\..*";` + `$GL_GITCONFIG_KEYS = '.*';` * `$GL_NO_CREATE_REPOS`, boolean, default 0 @@ -303,7 +312,9 @@ on feedback from my users to find or fix issues. The default produces files like `~/.gitolite/logs/gitolite-2009-09.log`. If you make up your own templates, **PLEASE MAKE SURE** the directory exists and is writable; gitolite won't do that for you unless it is the - default, ("$GL_ADMINDIR/logs") + default, ("$GL_ADMINDIR/logs"). + + Note that `%d` is also available if you want. * `$GL_PERFLOGT`, string, default undef @@ -312,6 +323,8 @@ on feedback from my users to find or fix issues. kept separate from access log files because they store different, usually much shorter term, information. + See the previous variable (`GL_LOGT`) for template related info. + * `$GL_SITE_INFO`, string, default undef Some installations would like to give their users customised information diff --git a/doc/hook-propagation.mkd b/doc/hook-propagation.mkd index 0b89539..3e261c4 100644 --- a/doc/hook-propagation.mkd +++ b/doc/hook-propagation.mkd @@ -132,3 +132,32 @@ By default, the only reason you need to touch the "system" location is if you want to modify the 'update' hook, but why would you fiddle with the most important part of gitolite, huh? You're a good admin, and will use [hook chaining][hookchaining] properly, right? + +## why not just push a hook? + +A question I often get is, why can't we simply push the hooks using the admin +repo, just like we push config changes. + +To understand why, realise that in many sites, the "right to push the +gitolite-admin repo" is **not** the same as "right to get a command line on +the server and run arbitrary commands". + +This means, gitolite tries its best to keep these two rights separated, and to +prevent someone who has the former right from trivially acquiring the latter. + +And so we don't allow adding hooks by admin push. + +That doesn't mean you can't do it yourself. Here's one possible way. + +Using the simple instructions [here][customhooks], add a +`post-update.secondary` hook containing this code: + + #!/bin/bash + cp $GL_ADMINDIR/local-hooks/* $GL_ADMINDIR/hooks/common + gl-setup + +Now create a directory in your gitolite-admin clone called "local-hooks", put +all your hooks there, and add/commit/push. + +That *should* do it. Test it and send me a patch for this document when you +do :-) diff --git a/doc/index.mkd b/doc/index.mkd index bfc8739..4f5212a 100644 --- a/doc/index.mkd +++ b/doc/index.mkd @@ -177,9 +177,13 @@ INR. The hole should not require enabling any of the options listed as having a [security impact][rcsecurity] in the rc file, nor obvious things like setting the umask too loose, etc. -## contact and license +## F=license contact and license -Gitolite is released under GPL v2. See COPYING for details. +The gitolite software is released under GPL v2. See COPYING for details. + +The gitolite documentation is provided under a [Creative Commons +Attribution-NonCommercial-ShareAlike 3.0 Unported +License](http://creativecommons.org/licenses/by-nc-sa/3.0/). * author: sitaramc@gmail.com, sitaram@atc.tcs.com * mailing list: gitolite@googlegroups.com diff --git a/doc/install.mkd b/doc/install.mkd index c4220bd..827f61b 100644 --- a/doc/install.mkd +++ b/doc/install.mkd @@ -52,8 +52,7 @@ On your *server*, as *root*: # (now as gitolite) gl-setup /tmp/YourName.pub -Note: if you're running non-interactively (i.e., cannot tolerate an editor -popping up), insert a "-q" before the argument to gl-setup. +Note: please see appendix d for command line options for [gl-setup][]. On your *workstation*: @@ -94,8 +93,7 @@ similar files and add it somehow. Then: gl-setup /tmp/YourName.pub -Note: if you're running non-interactively (i.e., cannot tolerate an editor -popping up), insert a "-q" before the argument to gl-setup. +Note: please see appendix d for command line options for [gl-setup][]. On your *workstation*: @@ -161,8 +159,7 @@ On your *server*, as *root*: # (now as git) gl-setup /tmp/YourName.pub -Note: if you're running non-interactively (i.e., cannot tolerate an editor -popping up), insert a "-q" before the argument to gl-setup. +Note: please see appendix d for command line options for [gl-setup][]. On your *workstation*: @@ -431,3 +428,28 @@ The easiest way is: find ~/repositories -wholename "*.git/hooks/update" | xargs rm -f but you can do it manually if you want to be careful. + +### #gl-setup appendix d: command line options for gl-setup + +After gl-system-install (or the RPM/DEB) have installed the *code*, gl-setup +sets up the actual gitolite instance. (Gitolite in [pictures][] may help +explain this better.) + +In ssh mode, gl-setup expects a pubkey filename the first time it is run, and +will complain if you don't supply it. On subsequent runs it is optional; you +only need to supply it if you want to quickly and easily change the admin's +(or indeed anyone's!) pubkey without going through all the steps that +[gl-admin-push][adminpush] requires. + +In http mode, gl-setup expects an "admin name" the first time it is run. On +subsequent runs, arguments are ignored. + +gl-setup accepts the following command line options, which must appear +*before* the pubkey filename/admin name: + + * `-q` -- quiet mode; suppress the editor that pops up to allow you to + change the rc file the first time. Meaningless/ignored on subseqent runs. + * `-q -q` -- extra quiet mode; suppress the editor as well as the + sshkeys-lint check at the end of the run. Old-timers who know ssh so well + that they still use protocol 1 keys *must* use this mode, because + sshkeys-lint will barf on them. Equivalent to `-q` in http mode. diff --git a/doc/virtual-refs.mkd b/doc/virtual-refs.mkd new file mode 100644 index 0000000..17cd607 --- /dev/null +++ b/doc/virtual-refs.mkd @@ -0,0 +1,282 @@ +# F=vref virtual refs + +The traditional method of adding additional checks to gitolite was to use [hook +chaining][hookchaining], which basically means you put your code in a new +hook called `update.secondary`. + +But this is not ideal -- it runs for all repos and all users, which you may +not want. + +Here's a teaser example for a better way. Copy `contrib/VREF/gl-VREF-COUNT` +to `src`, install/upgrade gitolite, then add this to your admin conf: + + repo r1 + RW+ = lead_dev dev2 dev3 + - VREF/COUNT/9 = dev2 dev3 + - VREF/COUNT/3/NEWFILES = dev2 dev3 + +Now dev2 and dev3 cannot push changes that affect more than 9 files at a time, +nor those that have more than 3 new files. It doesn't affect any other repo, +nor does it affect the lead developer. + +And here's one you can use right away to catch duplicate pubkeys in the admin +repo; just copy `contrib/VREF/gl-VREF-DUPKEYS` to `src` and upgrade gitolite, +then add this to conf/gitolite.conf: + + repo gitolite-admin + # ... normal rules ... + - VREF/DUPKEYS = @all + +(If you've been using the existing `contrib/update.detect-dup-pubkeys` as a +secondary update hook till now, you may want to switch to this method.) + +---- + +[[TOC]] + +---- + +## rule matching recap + +You won't get any joy out of this if you don't understand at least +[refex][]es, [deny][] rules, and [NAME][]-based restrictions. + +And here's an important **warning**. Recall the "summary" from [this][aac] +document: + +> The first matching refex that has the permission you're looking for (`W` +> or `+`) **or a minus (`-`)**, results in success **or failure, +> respectively**. A fallthrough **also** results in failure. + +Note the last sentence in that summary. **That sentence does NOT apply** to +virtual refs; a fallthru results in success here. You'll see why this is more +convenient as you read on. + +---- + +## what is a virtual ref + +A ref like `refs/heads/master` is the main property of a push that gitolite +uses to make its yes/no decision. I call this a "real" ref. + +Any *other* property of the push that you want to use to help in the decision +is therefore a *virtual* ref. This could be a property that git knows about, +like in the example above, or comes from outside git like, say, the current +time; see examples section later for some ideas. + +(To be honest, [NAME][]-based restrictions should also be called virtual refs, +but they've been in gitolite forever so they're grandfathered in, and the +material in this document does not apply to them). + +## fallthru is success here + +Notice that you didn't need to add an `RW+ VREF/...` rule for user `lead_dev` +in our example. This is the opposite of what we do in a similar situation +[here][NAME]. This section explains why. + +**Virtual refs are best used as additional "deny" rules**, using extra checks +that core gitolite cannot perform. + +Making fallthru be a "fail" forces you to add rules for all users, instead of +just the ones who should have those extra checks. Worse, since every virtual +ref involves calling an external program, many of these calls may be wasted. + +There's another advantage to doing it this way: a VREF can choose to simply +die if things look bad, and it will have the same effect, assuming you used +the VREF only in [deny][] rules. + +This in turn means any existing update hook can be used as a VREF *as-is*, as +long as it (a) prints nothing on success and (b) dies on failure. See the +email-check and dupkeys examples later. + +(Yes, I should have used this logic for [NAME][] also. What can I say -- I am +older and wiser now. Sadly, we can't change [NAME][] without breaking a lot +of existing configs, so it stays like a real ref -- fallthru is failure). + +## how it works -- overview + +Briefly, a refex starting with `VREF/FOO` triggers a call to a program called +`gl-VREF-FOO` in `$GL_BINDIR`. + +That program is expected to print zero or more lines to its STDOUT; each line +is taken by gitolite as a new "ref" to be matched against all the refexes for +this user in the config. Including, the refex that caused the vref call, of +course. + +Normally, you send back the refex itself, if the test determines that the rule +should be matched, otherwise nothing. So, in our example, we print +`VREF/COUNT/9` if the count was indeed greater than 9. Otherwise we just +exit. + +## how it works -- details + + * the VREF code is only called if there are any VREF rules for the user, + which means when the lead developer pushes, the VREF is not called at all. + + * when dev2 or dev3 push, gitolite first checks the real ref + (`ref/heads/master` or whatever), then any [NAME][] rules. So far this is + normal processing. + + After this it looks at VREF rules, and calls an external program for every + one it finds. Specifically, in a line like + + - VREF/COUNT/3/NEWFILES = user + + COUNT is the vref name, so the program called is + `$GL_BINDIR/gl-VREF-COUNT`. + + The program is passed **nine arguments** in this case (see next section + for details). + + * the script can print anything it wants to STDOUT; the first word in each + such line will be treated as a virtual ref to be matched against all the + rules, while the rest, if any, is a message to be added to the standard + "...DENIED..." message that gitolite prints if that refex matches. + + Usually it only makes sense to either + + * print nothing -- if you don't want the rule that triggered it to match + (ie., whatever condition being tested was not violated; like if the + count of changed files did not exceed 9, in our earlier example) + * print the refex itself (plus an optional message), so that it matches + the line which invoked it + +### arguments passed to the vref code + + * arguments **1, 2, 3**: the 'ref', 'oldsha', and 'newsha' that git passed + to the update hook (see 'man githooks') + + * arguments **4 and 5**: the 'oldtree' and 'newtree' SHAs. These are the + same as the oldsha and newsha values, except if one of them is all-0. + (indicating a ref creation or deletion). In that case the corresponding + 'tree' SHA is set (by gitolite, as a courtesy) to the special SHA + `4b825dc642cb6eb9a060e54bf8d69288fbee4904`, which is the hash of an empty + tree. + + (None of these shenanigans would have been needed if `git diff $oldsha + $newsha` would not error out when passed an all-0 SHA.) + + * argument **6**: the attempted access flag. Typically `W` or `+`, but + could also be `C`, `D`, or any of these 4 followed by `M`. If you have to + ask what they mean, you haven't read enough gitolite documentation to be + able to make virtual refs work. + + * argument **7**: is the entire refex; in our example + `VREF/COUNT/3/NEWFILES`. + + * arguments **8 onward**: are the split out (by `/`) portions of the refex, + excluding the first two components. In our example they would be `3` + followed by `NEWFILES`. + +Yes, argument 7 is redundant if you have 8 and 9. It's meant to make it easy +to write vref scripts in any language. See script examples in source. + +## what (else) can the vref code pass back + +Actually, the vref code can pass anything back; each line in its output will +be matched against all the rules as usual (with the exception that fallthru is +not failure). + +For example, you could have a ruleset like this: + + repo r1 + # ... normal rules ... + + - VREF/TIME/WEEKEND = @interns + - VREF/TIME/WEEKNIGHT = @interns + - VREF/TIME/HOLIDAY = @interns + +and you could write the TIME vref code (gl-VREF-TIME) to passback any or all +of the times that match. Then if an intern tried to access the system, each +rule would trigger a call to gl-VREF-TIME. + +The script should send back any of the applicable times (even more than one, +or none at all, as the case may be). So even if it was invoked using the +first rule, it might pass back (to gitolite) a virtual ref saying +'VREF/TIME/HOLIDAY', which would promptly cause the request to be denied. + +## VREFs shipped in contrib + +### number of new files + +If a dev pushes more than 2 *new* files, the top commit needs to have a +signed-off by line in its commit message. For example if he has 4 new files +this text should be: + + 4 new files signed-off by: <top commit author's email> + +The config entry for this is below (`NO_SIGNOFF` applies only to, and thus +implies, `NEWFILES`): + + RW+ VREF/COUNT/2/NO_SIGNOFF = sitaram + - VREF/COUNT/2/NO_SIGNOFF = @all + +Notice how the refex in both cases is *exactly* the same. If you make it +different (even change the number on my access line), things won't work. + +Junior devs can't push more than 10 new files, even with a signed-off by line: + + - VREF/COUNT/10/NEWFILES = @junior_devs + +### advanced filetype detection + +Note: this is more for illustration than use; it's rather specific to one of +the projects I manage but the idea is the important thing. + +Sometimes a file has a standard extension (that cannot be 'gitignore'd), but +it is actually automatically generated. Here's one way to catch it: + + - VREF/FILETYPE/AUTOGENERATED = @all + +You can look at `contrib/VREF/gl-VREF-FILETYPE` to see how it handles the +'AUTOGENERATED' option. You could also have a more generic option, like +perhaps BINARY, and handle that in the FILETYPE vref too. + +### checking author email + +Some people want to ensure that "you can only push your own commits". + +If you force it on everyone, this is a very silly idea (see "Philosophical +Notes" section of `contrib/VREF/gl-VREF-EMAIL_CHECK`). + +But there may be value in enforcing it just for the junior developers. + +The neat thing is that the existing `contrib/update.email-check` was just +copied to `contrib/VREF/gl-VREF-EMAIL_CHECK` and it works, because VREFs get +the same first 3 arguments and those are all that it cares about. (Note: you +have to change one subroutine in that script if you want to use it) + +### catching duplicate pubkeys + +We covered this as a teaser example at the start. + +## other ideas -- code welcome! + +### "no non-merge first-parents" + +Shruggar on #gitolite wanted this. Possible code to implement it would be +something like this (untested) + + [ -z "$(git rev-list --first-parent --no-merges $2..$3)" ] + +This can be implemented using `contrib/VREF/gl-VREF-MERGE_CHECK` as a model. +That script does what the 'in core' feature called [merge check][mergecheck] +does, although the syntax to be used in conf/gitolite will be quite different. + +### other ideas for VREFs + +Here are some more ideas: + + * number of commits (`git rev-list --count $old $new`) + * number of binary files in commit (currently I only know to count + occurrences of ` Bin ` in the output of `git diff --stat` + * number of *new* binary files (count ` Bin 0 ->` in `git diff --stat` + output) + * time of day/day of week (see example snippet somewhere above) + * IP address + * phase of the moon + +Note that pretty much anything that involves `$oldsha..$newsha` will have to +deal with the issue that when you push a new tag or branch, the "old" part +is all 0's, and unless you consider `--all` existing branches and tags it +becomes meaningless in terms of "number of new files" etc. diff --git a/doc/who-uses-it.mkd b/doc/who-uses-it.mkd index b719c0a..59e15fb 100644 --- a/doc/who-uses-it.mkd +++ b/doc/who-uses-it.mkd @@ -59,7 +59,8 @@ specific code (see [contrib/ldap][ldap] directory for details). **kernel.org**, the official distribution point for the Linux kernel, is the latest (as of 2011-10) high-visibility installation. According to [this email][ko-ann] to the lkml, kernel.org decided to use gitolite for access -controlling their git repos. +controlling their git repos. Their [FAQ entry][ko-why] describes at a high +level why they chose gitolite. This move also prompted the first ever security audit of gitolite by an outside party. Gitolite did great; see [here][audit] for details. @@ -69,6 +70,7 @@ edges in gitolite, and smoothing them out was fun (the "playing with gitolite" stuff, making the test suite simpler, "deny" rules for the entire repo). [ko-ann]: http://lkml.org/lkml/2011/9/23/357 +[ko-why]: http://www.kernel.org/faq/#whygitolite [audit]: http://groups.google.com/group/gitolite/browse_thread/thread/8dc5242052b16d0f ---- diff --git a/hooks/common/update b/hooks/common/update index 1048208..f31f108 100755 --- a/hooks/common/update +++ b/hooks/common/update @@ -75,32 +75,12 @@ push @allowed_refs, @ { $repos{'@all'} {$ENV{GL_USER}} || [] }; push @allowed_refs, @ { $repos{$ENV{GL_REPO}}{'@all'} || [] }; push @allowed_refs, @ { $repos{'@all'} {'@all'} || [] }; -# prepare the list of refs to be checked - -# previously, we just checked $ref -- the ref being updated, which is passed -# to us by git (see man githooks). Now we also have to treat each NAME being -# updated as a potential "ref" and check that, if NAME-based restrictions have -# been specified - -my @refs = ($ref); # the first ref to check is the real one -# because making it work screws up efficiency like no tomorrow... -if (exists $repos{$ENV{GL_REPO}}{NAME_LIMITS}) { - # this is special to git -- the hash of an empty tree - my $empty='4b825dc642cb6eb9a060e54bf8d69288fbee4904'; - # well they're not really "trees" but $empty is indeed the empty tree so - # we can just pretend $oldsha/$newsha are also trees, and anyway 'git - # diff' only wants trees - my $oldtree = $oldsha eq '0' x 40 ? $empty : $oldsha; - my $newtree = $newsha eq '0' x 40 ? $empty : $newsha; - push @refs, map { chomp; s/^/NAME\//; $_; } `git diff --name-only $oldtree $newtree`; -} +# first check the main ref (this is a *real* ref, like refs/heads/master, and +# is what we print in the log) +my $log_refex = check_ref(\@allowed_refs, $ENV{GL_REPO}, $ref, $att_acc); -# we potentially have many "refs" to check. The one we print in the log is -# the *first* one (which is a *real* ref, like refs/heads/master), while all -# the rest (if they exist) are like NAME/something. So we do the first one -# separately to capture it, then run the rest (if any) -my $log_refex = check_ref(\@allowed_refs, $ENV{GL_REPO}, (shift @refs), $att_acc); -check_ref (\@allowed_refs, $ENV{GL_REPO}, $_ , $att_acc) for @refs; +# then we check all virtual refs +check_vrefs(\@allowed_refs, $ENV{GL_REPO}, $att_acc); # if we returned at all, all the checks succeeded. Check secondary hooks now $UPDATE_CHAINS_TO ||= 'hooks/update.secondary'; @@ -111,3 +91,50 @@ log_it("", "$att_acc\t" . substr($oldsha, 0, 14) . "\t" . substr($newsha, 0, 14 "\t$reported_repo\t$ref\t$log_refex"); exit 0; + +# ---------------------------------------------------------------------- + +sub check_vrefs { + my ( $ar_ref, $repo, $aa ) = @_; + # these will be passed on as args 1, 2, 4 to check_ref. Arg-3 will be + # the new ref being checked. + + # this sub uses $ref, $oldsha, and $newsha globals; they're pretty much + # 'constant' once you enter an update hook anyway + + my $empty = '4b825dc642cb6eb9a060e54bf8d69288fbee4904'; # this is special to git -- the hash of an empty tree + my $oldtree = $oldsha eq '0' x 40 ? $empty : $oldsha; + my $newtree = $newsha eq '0' x 40 ? $empty : $newsha; + my @first6 = ( $ref, $oldsha, $newsha, $oldtree, $newtree, $aa ); + + # first, handle the VREF thats's been in "core" forever... + if ( $repos{ $ENV{GL_REPO} }{NAME_LIMITS} ) { + for my $ref ( map { chomp; s/^/NAME\//; $_; } `git diff --name-only $oldtree $newtree` ) { + check_ref( $ar_ref, $repo, $ref, $aa ); + } + } + + my %vref_seen; + + for my $vr ( grep { m(^VREF/) } map { $_->[1] } sort { $a->[0] <=> $b->[0] } @{$ar_ref} ) { + next if $vref_seen{$vr}++; + + # find code pertaining to $vr and run it; see docs for details + my ( $dummy, $pgm, @args ) = split '/', $vr; + $pgm = "$ENV{GL_BINDIR}/gl-VREF-$pgm"; + -x $pgm or die "can't find helper program for $vr\n"; + + open( my $fh, "-|", $pgm, @first6, $vr, @args ) or die "can't spawn helper program for $vr: $!\n"; + while (<$fh>) { + my ( $vref, $deny_message ) = split( ' ', $_, 2 ); + my $ret = check_ref( $ar_ref, $repo, $vref, $aa, 1 ); + die "$ret\n" . ( $deny_message || '' ) if $ret =~ /DENIED/ and $ret !~ /by fallthru/; + # I could change check_ref to take one more argument, say + # 'fallthru_is_pass', but I want to keep these changes as + # isolated as possible for now + } + close($fh) or die $! + ? "Error closing sort pipe: $!" + : "Exit status $? from VREF helper program for $vr"; + } +} diff --git a/hooks/common/update.secondary.sample b/hooks/common/update.secondary.sample deleted file mode 100644 index cc6ac98..0000000 --- a/hooks/common/update.secondary.sample +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/sh - -# driver script to run multiple update "hooklets". Each "hooklet" performs a -# specific (possibly site-local) check, and they *all* have to succeed for the -# push to succeed. - -# HOW TO USE: - -# (1) rename this file to remove the .sample extension -# (2) make the renamed file executable (chmod +x) -# (3) put it in ~/.gitolite/hooks/common on the server -# (4) create a directory called update.secondary.d in the same place -# (5) copy all the update "hooklets" you want (like update.detect-dup-pubkeys) -# from contrib or wherever, to that directory -# (6) make them also executable (chmod +x) -# (7) run gl-setup - -# rules for writing a hooklet are in the sample hooklet called -# "update.detect-dup-pubkeys" in contrib - -# ---- - -# NOTE: a hooklet runs under the same assumptions as the 'update' hook, so the -# starting directory must be maintained and arguments must be passed on. - -[ -d hooks/update.secondary.d ] || exit 0 - -# all output from these "hooklets" must go to STDERR to avoid confusing the client -exec >&2 - -for i in hooks/update.secondary.d/* -do - [ -x "$i" ] || continue - # call the hooklet with the same arguments we got - "$i" "$@" || { - # hooklet failed; we need to log it... - echo hooklet $i failed - perl -I$GL_BINDIR -Mgitolite -e "log_it('hooklet $i failed')" - # ...and send back some non-zero exit code ;-) - exit 1 - } -done - -exit 0 diff --git a/src/gitolite.pm b/src/gitolite.pm index ca66107..37f3977 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -27,6 +27,7 @@ use Exporter 'import'; slurp special_cmd try_adc + wrap_mkdir wrap_chdir wrap_open wrap_print @@ -105,6 +106,17 @@ our %one_git_config; # ditto for %git_configs # convenience subs # ---------------------------------------------------------------------------- +sub wrap_mkdir +{ + # it's not an error if the directory exists, but it is an error if it + # doesn't exist and we can't create it + my $dir = shift; + my $perm = shift; # optional + return if -d $dir; + mkdir($dir) or die "mkdir $dir failed: $!\n"; + chmod $perm, $dir if $perm; +} + sub wrap_chdir { chdir($_[0]) or die "$ABRT chdir $_[0] failed: $! at ", (caller)[1], " line ", (caller)[2], "\n"; } @@ -125,6 +137,19 @@ sub wrap_print { chmod $oldmode, $file if $oldmode; } +sub wrap_system { + system(@_); + + # straight from 'perldoc -f system' (sans the coredump part) + if ( $? == -1 ) { + print STDERR "failed to execute: $!\n"; + } elsif ( $? & 127 ) { + printf STDERR "child died with signal %d\n", ( $? & 127 ); + } else { + printf STDERR "child exited with value %d\n", $? >> 8; + } +} + sub slurp { local $/ = undef; my $fh = wrap_open("<", $_[0]); @@ -169,7 +194,9 @@ sub dos2unix { sub log_it { my ($ip, $logmsg); - open my $log_fh, ">>", $ENV{GL_LOG} or die "open log failed: $!\n"; + open my $log_fh, ">>", $ENV{GL_LOG} or die + "open log failed: $!\n" . + "attempting to log: " . ( $_[0] || '(nothing)' ) . "\n"; # first space sep field is client ip, per "man ssh" ($ip = $ENV{SSH_CONNECTION} || '(no-IP)') =~ s/ .*//; # the first part of logmsg is the actual command used; it's either passed @@ -467,6 +494,7 @@ sub setup_git_configs next if $key =~ /^gitolite-options\./; if ($value ne "") { $value =~ s/^['"](.*)["']$/$1/; + $value =~ s/%GL_REPO/$repo/; system("git", "config", $key, $value); } else { system("git", "config", "--unset-all", $key); @@ -562,7 +590,7 @@ sub setup_gitweb_access system("git config --remove-section gitweb 2>/dev/null"); } - return ($desc or can_read($repo, 'gitweb')); + return (can_read($repo, 'gitweb') or $desc); # this return value is used by the caller to write to projects.list } diff --git a/src/gl-admin-push b/src/gl-admin-push index 3fd0214..cd71137 100755 --- a/src/gl-admin-push +++ b/src/gl-admin-push @@ -1,6 +1,15 @@ #!/bin/sh die() { echo "$@" >&2; exit 1; } +if [ "$1" = "-h" ] +then + echo Usage: + echo " gl-admin-push [any options applicable to 'git push' command]" + echo + echo "Please see the 'gl-admin-push: ...' section in doc/tips-notes.mkd (Online at" + echo " http://sitaramc.github.com/gitolite/adminpush.html)" + exit 1 +fi # ---------- @@ -9,7 +18,7 @@ die() { echo "$@" >&2; exit 1; } GL_BINDIR=` perl -ne 'print($1), exit if /^command="(.+?)\/gl-(time|auth-command) /' < $HOME/.ssh/authorized_keys` # GL_BINDIR still not known? we have a problem... [ -z "$GL_BINDIR" ] && { - cat <<EOF2 + echo " Unable to determine correct path for gitolite scripts from the authkeys file. @@ -21,7 +30,7 @@ this command. For example (if you followed doc/http-backend.mkd precisely): GL_BINDIR=/var/www/gitolite-home/bin $0 $@ -EOF2 +" exit 1 } diff --git a/src/gl-compile-conf b/src/gl-compile-conf index 594d54c..4c296e6 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -175,9 +175,9 @@ sub parse_conf_line @refs = qw(refs/.*) unless @refs; # deprecation warning map { print STDERR "WARNING: old syntax 'PATH/' found; please use new syntax 'NAME/'\n" if s(^PATH/)(NAME/) } @refs; - # fully qualify refs that dont start with "refs/" or "NAME/"; + # fully qualify refs that dont start with "refs/" or "NAME/" or "VREF/"; # prefix them with "refs/heads/" - @refs = map { m(^(refs|NAME)/) or s(^)(refs/heads/); $_ } @refs; + @refs = map { m(^(refs|NAME|VREF)/) or s(^)(refs/heads/); $_ } @refs; @refs = map { s(/USER/)(/\$gl_user/); $_ } @refs; # expand the user list, unless it is just "@all" diff --git a/src/gl-conf-convert b/src/gl-conf-convert index 1faca4f..7b0bfee 100755 --- a/src/gl-conf-convert +++ b/src/gl-conf-convert @@ -9,6 +9,10 @@ use strict; use warnings; +if (not @ARGV and -t or $ARGV[0] eq '-h') { + print "Usage:\n gl-conf-convert < gitosis.conf > gitolite.conf\n(please the documentation for more)\n"; + exit 1; +} my @comments = (); my $groupname; diff --git a/src/gl-dryrun b/src/gl-dryrun index 2ebd198..5943433 100755 --- a/src/gl-dryrun +++ b/src/gl-dryrun @@ -11,7 +11,7 @@ cat <<EOFU This is a quick hack. It is not "production quality". Resist the temptation to turn this into an update.secondary hook and put it on the server. I WILL NOT BE RESPONSIBLE FOR ANY PROBLEMS IF YOU DO THAT. (Even more so if you use -'git checkout $3' *without* setting GIT_INDEX_FILE to something temporary, and +'git checkout' *without* setting GIT_INDEX_FILE to something temporary, and eventually realise that *deleted* files don't stay deleted...! And if you didn't understand that, all the more reason not to do it). @@ -35,6 +35,8 @@ EOFU exit 1; } +[ "$1" = "-h" ] && usage + [ -n "$1" ] || die "need an admin username" admin="$1"; shift @@ -69,7 +71,7 @@ $GL_CONF_COMPILED="$GL_ADMINDIR/conf/gitolite.conf-compiled.pm"; $GL_WILDREPOS = 1; $PROJECTS_LIST = $ENV{PWD} . "/projects.list"; $REPO_UMASK = 0077; -$GL_BIG_CONFIG = 0; +$GL_BIG_CONFIG = 1; $GL_NO_DAEMON_NO_GITWEB = 1; $GIT_PATH=""; $GL_GITCONFIG_KEYS = ".*"; @@ -86,7 +88,7 @@ EOF # now compile it echo compiling... -$GL_BINDIR/gl-compile-conf +$GL_BINDIR/gl-compile-conf || die "compile failed; aborting rest of run" echo echo "checking if $admin has push rights..." diff --git a/src/gl-install b/src/gl-install index de7aefd..ac9e84e 100755 --- a/src/gl-install +++ b/src/gl-install @@ -2,6 +2,8 @@ # INTERNAL COMMAND. NOT MEANT TO BE RUN BY THE USER DIRECTLY. +# sets up REPO_BASE, GL_ADMINDIR. Symlinks hooks + use strict; use warnings; @@ -10,108 +12,52 @@ use warnings; # ---------------------------------------------------------------------------- BEGIN { - die "ENV GL_RC not set\n" unless $ENV{GL_RC}; + die "ENV GL_RC not set\n" unless $ENV{GL_RC}; die "ENV GL_BINDIR not set\n" unless $ENV{GL_BINDIR}; } use lib $ENV{GL_BINDIR}; + use gitolite_rc; +use gitolite_env; use gitolite; -# ---------------------------------------------------------------------------- -# start... -# ---------------------------------------------------------------------------- - -# setup quiet mode if asked; please do not use this when running manually -open STDOUT, ">", "/dev/null" if (@ARGV and shift eq '-q'); - -# wrapper around mkdir; it's not an error if the directory exists, but it is -# an error if it doesn't exist and we can't create it -sub wrap_mkdir -{ - my $dir = shift; - my $perm = shift; # optional - if ( -d $dir ) { - print "$dir already exists\n"; - return; - } - mkdir($dir) or die "mkdir $dir failed: $!\n"; - chmod $perm, $dir if $perm; - print "created $dir\n"; -} - -unless ($ENV{GL_RC}) { - # doesn't exist. Copy it across, tell user to edit it and come back - my $glrc = $ENV{HOME} . "/.gitolite.rc"; - if ($GL_PACKAGE_CONF) { - system("cp $GL_PACKAGE_CONF/example.gitolite.rc $glrc"); - } else { - system("cp $ENV{GL_BINDIR}/../conf/example.gitolite.rc $glrc"); - } - print "created $glrc\n"; - print "please edit it, change the paths if you wish to, and RERUN THIS SCRIPT\n"; - exit; -} - -# add a custom path for git binaries, if specified -$ENV{PATH} .= ":$GIT_PATH" if $GIT_PATH; - -# set the umask before creating any files/directories -umask($REPO_UMASK); +setup_environment(); # mkdir $REPO_BASE, $GL_ADMINDIR if they don't already exist wrap_mkdir($REPO_BASE); -wrap_mkdir($GL_ADMINDIR, 0700); +wrap_mkdir( $GL_ADMINDIR, 0700 ); + # mkdir $GL_ADMINDIR's subdirs -for my $dir (qw(conf doc keydir logs src hooks hooks/common hooks/gitolite-admin)) { - # some of them will stay empty; too lazy to fix right now ;-) - wrap_mkdir("$GL_ADMINDIR/$dir", 0700); +for my $dir (qw(conf keydir logs hooks hooks/common hooks/gitolite-admin)) { + wrap_mkdir( "$GL_ADMINDIR/$dir", 0700 ); } -# "src" and "doc" will be overwritten on each install, but not conf -if ($GL_PACKAGE_HOOKS) { - system("cp -R -p $GL_PACKAGE_HOOKS $GL_ADMINDIR"); -} else { - system("cp -R -p $ENV{GL_BINDIR}/../src $ENV{GL_BINDIR}/../doc $ENV{GL_BINDIR}/../hooks $GL_ADMINDIR"); - system("cp $ENV{GL_BINDIR}/../conf/VERSION $GL_ADMINDIR/conf"); -} +# hooks must be propagated to all the repos in case they changed -unless (-f $GL_CONF or $GL_PACKAGE_CONF) { - print <<EOF; - please do the following: - 1. create and edit $GL_CONF to contain something like this: - repo gitolite-admin - RW+ = yourname - 2. copy "yourname.pub" to $GL_ADMINDIR/keydir - 3. run this command - $GL_ADMINDIR/src/gl-compile-conf -EOF -} +# See http://sitaramc.github.com/gitolite/hook_prop.html if you're not sure +# what is happening here, especially the picture toward the end. -# finally, hooks must be propagated to all the repos in case they changed +# all repos, all hooks chdir($REPO_BASE) or die "chdir $REPO_BASE failed: $!\n"; for my $repo (`find . -type d -name "*.git" -prune`) { - chomp ($repo); - # propagate our own, plus any local admin-defined, hooks - ln_sf("$GL_ADMINDIR/hooks/common", "*", "$repo/hooks"); - # in case of package install, GL_ADMINDIR is no longer the top cop; - # override with the package hooks - ln_sf("$GL_PACKAGE_HOOKS/common", "*", "$repo/hooks") if $GL_PACKAGE_HOOKS; + chomp($repo); + # propagate user hooks + ln_sf( "$GL_ADMINDIR/hooks/common", "*", "$repo/hooks" ); + # propagate package hooks, overriding user hooks + ln_sf( "$GL_PACKAGE_HOOKS/common", "*", "$repo/hooks" ); chmod 0755, "$repo/hooks/update"; } -# oh and one of those repos is a bit more special and has an extra hook :) +# (special!) gitolite-admin repo, post-update hook, package hook only if ( -d "gitolite-admin.git/hooks" ) { - print "copying post-update hook to gitolite-admin repo...\n"; - unlink "gitolite-admin.git/hooks/post-update"; - symlink "$GL_ADMINDIR/hooks/gitolite-admin/post-update", "gitolite-admin.git/hooks/post-update" - or die "could not symlink post-update hook\n"; - # ditto... (see previous block) - ln_sf("$GL_PACKAGE_HOOKS/gitolite-admin", "post-update", "gitolite-admin.git/hooks") if $GL_PACKAGE_HOOKS; + ln_sf( "$GL_PACKAGE_HOOKS/gitolite-admin", "post-update", "gitolite-admin.git/hooks" ); chmod 0755, "gitolite-admin.git/hooks/post-update"; } -# fixup program renames +# deal with old program names... +# not needed for RPM/DEB type systems since they take care of it themselves +# but people upgrading might appreciate this; not sure how useful it is though for my $oldname (qw(pta-hook.sh conf-convert.pl 00-easy-install.sh gl-easy-install 99-emergency-addkey.sh gl-emergency-addkey install.pl update-hook.pl hooks/update ga-post-update-hook VERSION)) { unlink "$GL_ADMINDIR/src/$oldname"; unlink "$ENV{HOME}/gitolite-install/src/$oldname"; diff --git a/src/gl-mirror-push b/src/gl-mirror-push index 70514cb..e7054e2 100755 --- a/src/gl-mirror-push +++ b/src/gl-mirror-push @@ -25,6 +25,7 @@ hn=`get_rc_val GL_HOSTNAME` # get repo name then check if it's a local or slave (ie we're not the master) [ -z "$1" ] && die fatal: missing reponame argument repo=$1; shift +repo=${repo%.git} REPO_BASE=`get_rc_val REPO_BASE` cd $REPO_BASE/$repo.git 2>/dev/null || die fatal: could not change directory to "$repo" diff --git a/src/gl-mirror-shell b/src/gl-mirror-shell index d2b6489..80534dc 100755 --- a/src/gl-mirror-shell +++ b/src/gl-mirror-shell @@ -39,6 +39,11 @@ die "fatal: GL_HOSTNAME not set in rc; mirroring disabled\n" unless $GL_HOSTNAME # ---------------------------------------------------------------------------- +die "please read the gitolite mirroring documentation; this program is too +critical for you to just run it based on a 'usage' message.\n" if not @ARGV or $ARGV[0] eq '-h'; + +# ---------------------------------------------------------------------------- + # deal with local invocations first # on the "master", run from a shell, for one specific repo, with an optional @@ -47,6 +52,7 @@ die "fatal: GL_HOSTNAME not set in rc; mirroring disabled\n" unless $GL_HOSTNAME if ( ($ARGV[0] || '') eq 'request-push' and not $ENV{SSH_ORIGINAL_COMMAND} ) { shift; my $repo = shift or die "fatal: missing reponame\n"; + $repo =~ s/\.git$//; -d "$REPO_BASE/$repo.git" or die "fatal: no such repo?\n"; # this is the default argument if no slave list or key is supplied @@ -77,8 +83,6 @@ if ( ($ARGV[0] || '') eq 'request-push' and not $ENV{SSH_ORIGINAL_COMMAND} ) { exit 0; } -unless (@ARGV) { print STDERR "fatal: missing command\n"; exit 1; } - # ---------- # now the remote invocations; log it, then get the sender name @@ -107,6 +111,7 @@ if ($soc eq 'info') { if ($soc =~ /^git-receive-pack '(\S+)'$/) { my $repo = $1; + $repo =~ s/\.git$//; die "fatal: invalid characters in $repo\n" unless $repo =~ $REPONAME_PATT; my $mm = mirror_mode($repo); @@ -134,11 +139,12 @@ if ($soc =~ /^git-receive-pack '(\S+)'$/) { if ($soc =~ /^request-push (\S+)$/) { my $repo = $1; + $repo =~ s/\.git$//; die "fatal: invalid characters in $repo\n" unless $repo =~ $REPONAME_PATT; die "$ABRT fatal: $GL_HOSTNAME ==//==> $sender refused: not in slave list\n" unless mirror_listslaves($repo) =~ /(^|\s)$sender(\s|$)/; print STDERR "$GL_HOSTNAME ==== ($repo) ===> $sender\n"; # just one sender, and we've checked that he is "on the list". Foreground... - system("$ENV{GL_BINDIR}/gl-mirror-push", $repo, "-fg", $sender); + wrap_system("$ENV{GL_BINDIR}/gl-mirror-push", $repo, "-fg", $sender); exit; } @@ -156,6 +162,8 @@ if ($soc =~ /^USER=(\S+) SOC=(git-receive-pack '(\S+)')$/) { my $user = $1; $ENV{SSH_ORIGINAL_COMMAND} = $2; my $repo = $3; + $repo =~ s/\.git$//; + $repo =~ s(^/)(); die "fatal: invalid characters in $user\n" unless $user =~ $USERNAME_PATT; die "fatal: invalid characters in $repo\n" unless $repo =~ $REPONAME_PATT; die "$ABRT fatal: $GL_HOSTNAME <==//== $sender redirected push rejected\n" unless mirror_redirectOK($repo, $sender); diff --git a/src/gl-setup b/src/gl-setup index 7bf8881..1970d93 100755 --- a/src/gl-setup +++ b/src/gl-setup @@ -18,16 +18,42 @@ GL_PACKAGE_CONF=/tmp/share/gitolite/conf # pubkey file if you happen to have lost all gitolite-access to the repos (but # do have shell access via some other means) +# ---------------------------------------------------------------------- +# local functions +# ---------------------------------------------------------------------- + die() { echo "$@" >&2; exit 1; } get_rc_val() { `dirname $0`/gl-query-rc $1 } -TEMPDIR=`mktemp -d -t tmp.XXXXXXXXXX` +# ---------------------------------------------------------------------- +# tempdir setup +# ---------------------------------------------------------------------- + +TEMPDIR=`perl -MFile::Temp -l -e 'print File::Temp::tempdir("tmp.XXXXXXXXXX", TMPDIR => 1);'` export TEMPDIR trap "/bin/rm -rf $TEMPDIR" 0 +# ---------------------------------------------------------------------- +# argument handling +# ---------------------------------------------------------------------- + +# save arguments for use in commit message later +args="$*" + +if [ "$1" = "-h" ] +then + echo Usage: + echo " gl-setup [-q] [-q] [YourName.pub] # ssh mode" + echo " gl-setup [-q] [-q] [YourName] # http mode" + echo + echo "Please see 'appendix d' in doc/install.mkd for more. (Online at" + echo " http://sitaramc.github.com/gitolite/install.html#gl-setup)" + exit 1 +fi + # quiet mode; only used to suppress popping up an editor on a new rc file if [ "$1" = "-q" ] then @@ -35,6 +61,17 @@ then quiet=1 fi +# extra quiet mode (second '-q'); suppress the lint check at the end +if [ "$1" = "-q" ] +then + shift + nolint=1 +fi + +# ---------------------------------------------------------------------- +# get the admin_name and (usually) the pubkey file name +# ---------------------------------------------------------------------- + if [ -n "$GITOLITE_HTTP_HOME" ] then HOME=$GITOLITE_HTTP_HOME @@ -51,6 +88,10 @@ else fi fi +# ---------------------------------------------------------------------- +# report changes to rc file (for manual fixing) or setup a new rc file +# ---------------------------------------------------------------------- + export GL_RC GL_RC=`get_rc_val GL_RC 2>/dev/null` [ -z "$GL_RC" ] && GL_RC=$HOME/.gitolite.rc @@ -62,7 +103,7 @@ then } print_rc_vars $GL_PACKAGE_CONF/example.gitolite.rc > $TEMPDIR/.newvars print_rc_vars $GL_RC > $TEMPDIR/.oldvars - comm -23 $TEMPDIR/.newvars $TEMPDIR/.oldvars > $TEMPDIR/.diffvars + grep -f $TEMPDIR/.oldvars -v $TEMPDIR/.newvars > $TEMPDIR/.diffvars if [ -s $TEMPDIR/.diffvars ] then cp $GL_PACKAGE_CONF/example.gitolite.rc $HOME/.gitolite.rc.new @@ -88,6 +129,10 @@ else fi fi +# ---------------------------------------------------------------------- +# setup ~/.ssh +# ---------------------------------------------------------------------- + # setup ssh stuff. We break our normal rule that we will not fiddle with # authkeys etc., because in this case it seems appropriate ( @@ -98,6 +143,10 @@ fi chmod go-w . .ssh .ssh/authorized_keys ) +# ---------------------------------------------------------------------- +# setup gitolite's env vars +# ---------------------------------------------------------------------- + export GL_BINDIR export REPO_BASE export GL_ADMINDIR @@ -105,42 +154,50 @@ GL_BINDIR=` get_rc_val GL_BINDIR ` REPO_BASE=` get_rc_val REPO_BASE ` GL_ADMINDIR=`get_rc_val GL_ADMINDIR` -# now we get to gitolite itself +# ---------------------------------------------------------------------- +# setup hooks, admindir, the admin repo +# ---------------------------------------------------------------------- gl-install -q [ -f $GL_ADMINDIR/conf/gitolite.conf ] || { - cat <<EOF | cut -c9- > $GL_ADMINDIR/conf/gitolite.conf + echo " repo gitolite-admin RW+ = $admin_name repo testing RW+ = @all -EOF + " | cut -c9- > $GL_ADMINDIR/conf/gitolite.conf } [ -n "$pubkey_file" ] && cp $pubkey_file $GL_ADMINDIR/keydir touch $HOME/.ssh/authorized_keys gl-compile-conf -q -# setup push-to-admin -[ -n "$pubkey_file" ] && ( +# setup the admin repo +[ -n "$pubkey_file" ] || [ -n "$GITOLITE_HTTP_HOME" ] && ( cd $HOME; cd $REPO_BASE/gitolite-admin.git GIT_WORK_TREE=$GL_ADMINDIR; export GIT_WORK_TREE git add conf/gitolite.conf keydir git config --get user.email >/dev/null || git config user.email $USER@`hostname` git config --get user.name >/dev/null || git config user.name "$USER on `hostname`" - git diff --cached --quiet 2>/dev/null || git commit -am start + git diff --cached --quiet 2>/dev/null || git commit -am "gl-setup $args" ) # now that the admin repo is created, you have to set the hooks properly; best # do it by running install again gl-install -q -# ---- +# ---------------------------------------------------------------------- +# lint check on ssh keys +# ---------------------------------------------------------------------- + +[ -z "$nolint" ] && { + # the never-ending quest to help with bloody ssh issues... + cd $GL_ADMINDIR/keydir + [ -n "$pubkey_file" ] && $GL_BINDIR/sshkeys-lint -q -a $admin_name < $HOME/.ssh/authorized_keys +} -# the never-ending quest to help with bloody ssh issues... -cd $GL_ADMINDIR/keydir -[ -n "$pubkey_file" ] && $GL_BINDIR/sshkeys-lint -q -a $admin_name < $HOME/.ssh/authorized_keys +# ---------------------------------------------------------------------- exit 0 diff --git a/src/gl-system-install b/src/gl-system-install index d7df7e4..b8b6377 100755 --- a/src/gl-system-install +++ b/src/gl-system-install @@ -1,117 +1,144 @@ -#!/bin/sh +#!/usr/bin/perl +use strict; +use warnings; +use English; # so I can say $EUID instead of $> +use File::Path qw(mkpath); -# install the gitolite software *system wide*. Not too robust, fancy, etc., -# but does have a usage message and catches simple problems. +# install the gitolite software *system wide*. -usage() { echo " - Usage: - $0 [shared-bin-dir shared-conf-dir shared-hooks-dir] +# This program does for a manual install (root or non-root method) what +# doc/packaging.mkd describes is needed to be done by a packager. - Installs the gitolite software (just the software, not keys or repos) - within \$HOME when invoked by a user, or system-wide when invoked by root. +# Packagers can also use this program to do the same thing if they wish. - Takes 0 or 3 absolute paths. The first one must be part of \$PATH. +my ( $bin_dir, $conf_dir, $hooks_dir ); - Examples: - # as root - $0 +check_args(); +argv_or_defaults(); +check_dirs(); - # this defaults to: - $0 /usr/local/bin /var/gitolite/conf /var/gitolite/hooks +use FindBin; +# we assume the standard gitolite source tree is here! +chdir( $FindBin::Bin . "/.." ) or die "can't cd: $!\n"; - # as a normal user - $0 +# notice the 'and' for failure after system() calls, because they return +# "shell truth" not "perl truth" - # this defaults to: - $0 \$HOME/bin \$HOME/share/gitolite/conf \$HOME/share/gitolite/hooks +# copy src +system("cp src/* $bin_dir") and die "cp src/* to $bin_dir failed"; - [RPM packagers: you can supply a 4th argument to specify a 'buildroot' - directory. DEB folks would call this a 'DESTDIR', I believe. In this - usage the first 3 arguments are NOT optional] -" - exit 1; -} +# fixup GL_PACKAGE_CONF in gl-setup +replace( "/tmp/share/gitolite/conf", $conf_dir, "$bin_dir/gl-setup" ); -die() { echo >&2; echo "$@" >&2; echo >&2; echo Run "$0 -h" for a detailed usage message. >&2; exit 1; } - -[ "$1" = "-h" ] && usage +# record which version is being sent across; we assume it's HEAD +record_version(); -validate_dir() { - echo $1 | grep '^/' >/dev/null || die "$1 should be an absolute path" - [ -d $1 ] || mkdir -p $1 || die "$1 does not exist and could not be created" -} +# copy conf +system("cp -R conf/* $conf_dir") and die "cp conf/* to $conf_dir failed"; -# if we have a buildroot, set that up first -buildroot=$4; -[ -n "$buildroot" ] && validate_dir $buildroot -[ -n "$buildroot" ] && buildroot=$buildroot/ - -# either all 3 args must be supplied or none at all -[ -n "$1" ] && [ -z "$3" ] && die "I need all 3 directories or none at all" -# supply default values to args 1, 2, and 3 if not provided -[ -z "$1" ] && { - euid=`perl -e 'print $>'` - if [ "$euid" = "0" ] - then - set /usr/local/bin /var/gitolite/conf /var/gitolite/hooks - else - set $HOME/bin $HOME/share/gitolite/conf $HOME/share/gitolite/hooks - fi - echo "using default values for EUID=$euid:" >&2 - echo "$@" >&2 -} +# fixup GL_PACKAGE_CONF and GL_PACKAGE_HOOKS in the example rc +replace( "/tmp/share/gitolite/conf", $conf_dir, "$conf_dir/example.gitolite.rc" ); +replace( "/tmp/share/gitolite/hooks", $hooks_dir, "$conf_dir/example.gitolite.rc" ); -gl_bin_dir=$1; validate_dir $buildroot$gl_bin_dir -gl_conf_dir=$2; validate_dir $buildroot$gl_conf_dir -gl_hooks_dir=$3; validate_dir $buildroot$gl_hooks_dir +# copy hooks +system("cp -R hooks/* $hooks_dir") and die "cp hooks/* to $hooks_dir failed"; -bindir=`echo $0 | perl -lpe 's/^/$ENV{PWD}\// unless /^\//; s/\/[^\/]+$//;'` -cd $bindir/.. # we assume the standard gitolite source tree is here! +# this is some extra gunk for people with crap setups +path_advice(); -cp src/* $buildroot$gl_bin_dir || die "cp src/* to $buildroot$gl_bin_dir failed" -perl -lpi -e "s(^GL_PACKAGE_CONF=.*)(GL_PACKAGE_CONF=$gl_conf_dir)" $buildroot$gl_bin_dir/gl-setup +exit 0; -# record which version is being sent across; we assume it's HEAD -if git rev-parse --is-inside-work-tree >/dev/null 2>&1 -then - git describe --tags --long --dirty=-dt 2>/dev/null > conf/VERSION || die "git describe failed -- your git is probably too old" -else - [ -f conf/VERSION ] || echo '(unknown)' > conf/VERSION -fi +# ---------------------------------------------------------------------- -cp -R conf/* $buildroot$gl_conf_dir || die "cp conf/* to $buildroot$gl_conf_dir failed" -perl -lpi \ - -e "s(^#\s*\\\$GL_PACKAGE_CONF\s*=.*)(\\\$GL_PACKAGE_CONF = '$gl_conf_dir';)" \ - $buildroot$gl_conf_dir/example.gitolite.rc -perl -lpi \ - -e "s(^#\s*\\\$GL_PACKAGE_HOOKS\s*=.*)(\\\$GL_PACKAGE_HOOKS = '$gl_hooks_dir';)" \ - $buildroot$gl_conf_dir/example.gitolite.rc +sub check_args { + return unless @ARGV; + return if @ARGV == 3; + usage(); +} -cp -R hooks/* $buildroot$gl_hooks_dir || die "cp hooks/* to $buildroot$gl_hooks_dir failed" +sub argv_or_defaults { + ( $bin_dir, $conf_dir, $hooks_dir ) = @ARGV; + return if @ARGV; + + unless (@ARGV) { + my $HOME = $ENV{HOME}; + if ( $EUID eq "0" ) { + ( $bin_dir, $conf_dir, $hooks_dir ) = qw(/usr/local/bin /var/gitolite/conf /var/gitolite/hooks); + } else { + ( $bin_dir, $conf_dir, $hooks_dir ) = ( "$HOME/bin", "$HOME/share/gitolite/conf", "$HOME/share/gitolite/hooks" ); + } + print STDERR "using default values for EUID=$EUID:\n"; + print STDERR join( ", ", $bin_dir, $conf_dir, $hooks_dir ), "\n"; + } +} -# ---- +sub check_dirs { + for my $dir ( $bin_dir, $conf_dir, $hooks_dir ) { + die "$dir should be an absolute path\n" unless $dir =~ m(^/); + mkpath($dir); + -d $dir or die "$dir does not exist and could not be created\n"; + } +} -# check if $gl_bin_dir is in $PATH and advise the user if needed -which=`which gl-setup 2>/dev/null` +sub replace { + my ( $old, $new, $file ) = @_; + system("perl -lpi -e 's($old)($new)' $file"); +} -path_advice=" -Since gl-setup MUST be run from the PATH (and not as src/gl-setup or such), -you must fix this before running gl-setup. The simplest way is to add +sub record_version { + # this is really much easier in plain shell :( + if ( system("git rev-parse --is-inside-work-tree >/dev/null 2>&1") ) { + # for system() calls, perl 'true/success' is shell 'false/fail', which + # means the command failed; we are not in a git work tree + -f "conf/VERSION" or system("echo '(unknown)' > conf/VERSION"); + } else { + system("git describe --tags --long --dirty=-dt 2>/dev/null > conf/VERSION") + and die "git describe failed -- your git is probably too old"; + } +} - PATH=$gl_bin_dir:\$PATH +sub path_advice { + my $path_advice = " + Since gl-setup MUST be run from the PATH (and not as src/gl-setup or + such), you must fix this before running gl-setup. Just add + + PATH=$bin_dir:\$PATH + + to the end of your bashrc or similar file. You can even simply do that + manually each time you log in and want to run a gitolite command.\n"; + + for my $p ( split( ':', $ENV{PATH} ) ) { + return if $p eq $bin_dir; # all is well + + if ( -x "$p/gl-setup" ) { + #<<< + die " ***** WARNING *****\n" . + " you have installed the sources into $bin_dir, but\n" . + " $p in your \$PATH *also* contains gl-setup.\n" . + " This is almost certainly going to confuse you or me later.\n" . + $path_advice; + #>>> + } + } + + #<<< + die " ***** WARNING *****\n" . + " gl-setup is not in your \$PATH.\n" . + $path_advice; + #>>> +} -to the end of your bashrc or similar file. You can even simply run that -command manually each time you log in and want to run a gitolite command." +sub usage { + print " +Usage: + gl-system-install [bin-dir conf-dir hooks-dir] -[ -z "$which" ] && die " ***** WARNING ***** -gl-setup is not in your \$PATH. -$path_advice" +Requires all 3 arguments or none. All arguments supplied must be absolute +paths. The following defaults are used if arguments are not supplied: -which=`dirname $which` -[ "$which" = "$gl_bin_dir" ] || die " ***** WARNING ***** -$which precedes $gl_bin_dir in your \$PATH, -and it *also* contains gl-setup. This is almost certainly going to confuse -you or me later. -$path_advice" + as normal user: \$HOME/bin, \$HOME/share/gitolite/conf, \$HOME/share/gitolite/hooks + as root: /usr/local/bin, /var/gitolite/conf, /var/gitolite/hooks +"; + exit 1; +} -exit 0 diff --git a/src/gl-tool b/src/gl-tool index 256a47a..d95a3bc 100755 --- a/src/gl-tool +++ b/src/gl-tool @@ -13,7 +13,7 @@ use gitolite_rc; use gitolite; sub usage { print <DATA>; exit 1; } -usage() unless (@ARGV); +usage() if (not @ARGV) or $ARGV[0] eq '-h'; my $cmd = shift; my $pub = shift; diff --git a/src/sshkeys-lint b/src/sshkeys-lint index 96c027f..a18a306 100755 --- a/src/sshkeys-lint +++ b/src/sshkeys-lint @@ -8,7 +8,8 @@ use warnings; use Getopt::Long; my $admin = 0; my $quiet = 0; -GetOptions('admin|a=s' => \$admin, 'quiet|q' => \$quiet); +my $help = 0; +GetOptions('admin|a=s' => \$admin, 'quiet|q' => \$quiet, 'help|h' => $help); use Data::Dumper; $Data::Dumper::Deepcopy = 1; @@ -31,7 +32,7 @@ sub msg { print "sshkeys-lint: " . ( $warning ? "WARNING: " : "" ) . $_ for @_; } -@ARGV or not -t or usage(); +usage() if not @ARGV and -t or $help; our @pubkeyfiles = @ARGV; @ARGV = (); # ------------------------------------------------------------------------ @@ -42,6 +42,7 @@ git config --global user.email "tester@example.com" # install it cd gitolite +cp contrib/VREF/* src git describe --tags --long HEAD > conf/VERSION # in case it is dirty src/gl-system-install diff --git a/t/smart-http-smoke-test/README b/t/smart-http-smoke-test/README new file mode 100644 index 0000000..09d2178 --- /dev/null +++ b/t/smart-http-smoke-test/README @@ -0,0 +1,12 @@ +quick smoke test for smart http mode. + + - tested on fedora 16; YDMV + - tests are designed for "just installed" case; running them twice will fail + +step 1: as root, run + + t/smart-http-smoke-test/install + +step 2: as any normal user, run + + prove t/smart-http-smoke-test/t01 diff --git a/t/smart-http-smoke-test/install b/t/smart-http-smoke-test/install new file mode 100755 index 0000000..7fa3d0a --- /dev/null +++ b/t/smart-http-smoke-test/install @@ -0,0 +1,49 @@ +#!/bin/bash + +die() { echo "$@"; exit 1; } + +id | grep '=0(root)' || die "you must run this as root" +umask 0022 + +cd ~apache +rm -rf gitolite-home +mkdir gitolite-home +export GITOLITE_HTTP_HOME +GITOLITE_HTTP_HOME=/var/www/gitolite-home +PATH=$PATH:$GITOLITE_HTTP_HOME/bin + +cd gitolite-home +git clone /tmp/gitolite.git gitolite-source + +cd gitolite-source +src/gl-system-install $GITOLITE_HTTP_HOME/bin $GITOLITE_HTTP_HOME/share/gitolite/conf $GITOLITE_HTTP_HOME/share/gitolite/hooks + +echo '$ENV{GIT_HTTP_BACKEND} = "/usr/libexec/git-core/git-http-backend";' > 1 +echo '$ENV{PATH} .= ":$ENV{GITOLITE_HTTP_HOME}/bin";' >> 1 +cat /var/www/gitolite-home/share/gitolite/conf/example.gitolite.rc >> 1 +\mv 1 /var/www/gitolite-home/share/gitolite/conf/example.gitolite.rc + +gl-setup -q tester + +chown -R apache.apache $GITOLITE_HTTP_HOME + +cat <<EOF1 > /etc/httpd/conf.d/gitolite.conf +SetEnv GIT_PROJECT_ROOT /var/www/gitolite-home/repositories +ScriptAlias /git/ /var/www/gitolite-home/bin/gl-auth-command/ +ScriptAlias /gitmob/ /var/www/gitolite-home/bin/gl-auth-command/ +SetEnv GITOLITE_HTTP_HOME /var/www/gitolite-home +SetEnv GIT_HTTP_EXPORT_ALL + +<Location /git> + AuthType Basic + AuthName "Private Git Access" + Require valid-user + AuthUserFile /tmp/gitolite-http-authuserfile +</Location> +EOF1 + +htpasswd -bc /tmp/gitolite-http-authuserfile tester tester +map "htpasswd -b /tmp/gitolite-http-authuserfile % %" u{1..6} + +service httpd restart + diff --git a/t/smart-http-smoke-test/t01 b/t/smart-http-smoke-test/t01 new file mode 100755 index 0000000..d5f5d82 --- /dev/null +++ b/t/smart-http-smoke-test/t01 @@ -0,0 +1,91 @@ +#!/bin/bash + +die() { echo "$@"; exit 1; } + +# git clone `url u1 r1` +url() { + echo http://$1:$1@localhost/git/$2.git +} + +# `cmd sitaram info` +cmd() { + c="curl http://$1:$1@localhost/git" + shift + c="$c/$1" + shift + + if [ -n "$1" ] + then + c="$c?$1" + shift + fi + while [ -n "$1" ] + do + c="$c+$1" + shift + done + + echo $c +} + +export tmp=$(mktemp -d); +trap "rm -rf $tmp" 0; +cd $tmp + +tsh "plan 29" + +tsh " + ## ls-remote tester admin + git ls-remote `url tester gitolite-admin` + ok + /HEAD/ + /refs.heads.master/ + ## clone + git clone `url tester gitolite-admin` + ok + /Cloning into/ + ls -al gitolite-admin/conf + /gitolite.conf/ +" || die "step 1" + +cd gitolite-admin +echo repo t2 >> conf/gitolite.conf +echo 'RW+ = u1 u2' >> conf/gitolite.conf + +tsh " + ## add, commit, push + git add conf/gitolite.conf + ok + !/./ + git commit -m t2 + ok + /1 file.*changed/ + git push + ok + /creating t2/ or die run this on a fresh install please... + /Initialized.*var.www.gitolite-home.repositories.t2.git/ + /To http:..tester:tester.localhost.git.gitolite-admin.git/ + /master -. master/ + ## various ls-remotes + git ls-remote `url u1 gitolite-admin` + !ok + /DENIED to u1/ + git ls-remote `url u1 t2` + ok + !/./ + git ls-remote `url u2 t2` + ok + !/./ + git ls-remote `url u3 t2` + !ok + /DENIED to u3/ + ## push to u1:t2 + git push `url u1 t2` master:master + ok + /To http:..u1:u1.localhost.git.t2.git/ + /master -. master/ + git ls-remote `url u2 t2` + ok + /HEAD/ + /refs.heads.master/ +" || die "step 2" diff --git a/t/standalone/gl-system-install b/t/standalone/gl-system-install new file mode 100755 index 0000000..4175b47 --- /dev/null +++ b/t/standalone/gl-system-install @@ -0,0 +1,177 @@ +#!/usr/bin/perl + +# test script for install side of gitolite (not part of normal test suite) + +use 5.10.0; +use strict; +use warnings; + +use lib "$ENV{HOME}/._s/bin"; +use Tsh; + +my $version_string = get_version(); + +my $euid=$>; +try 'cd'; + +# invalid arguments + +clean(); +try " + ## invalid arguments + gitolite/src/gl-system-install bin2 + !ok + /I need all 3.../ + gitolite/src/gl-system-install bin2 share2/gitolite/conf share2/gitolite/hooks + !ok + /should be an absolute path/ + "; + +# no arguments + +clean(); +try " + ## no arguments + gitolite/src/gl-system-install + ok + /using default values for EUID=$euid/ + //home/gl-test/bin, /home/gl-test/share/gitolite/conf, /home/gl-test/share/gitolite/hooks/ + "; + +static_checks( + "/home/gl-test/bin", + "/home/gl-test/share/gitolite/conf", + "/home/gl-test/share/gitolite/hooks", + $version_string +); + +# non-default arguments + +clean(); +try " + ## non-default arguments + gitolite/src/gl-system-install \$PWD/bin2 \$PWD/share2/gitolite/conf \$PWD/share2/gitolite/hooks + !ok + /WARNING/ + /gl-setup is not in your .PATH/ + "; + +static_checks( + "/home/gl-test/bin2", + "/home/gl-test/share2/gitolite/conf", + "/home/gl-test/share2/gitolite/hooks", + $version_string +); + +# occurs earlier in PATH + +clean(); +try " + ## occurs earlier in PATH + gitolite/src/gl-system-install + gitolite/src/gl-system-install \$PWD/bin2 \$PWD/share2/gitolite/conf \$PWD/share2/gitolite/hooks + !ok + /WARNING/ + /in your .PATH .also. contains gl-setup/ + " or die; + +static_checks( + "/home/gl-test/bin2", + "/home/gl-test/share2/gitolite/conf", + "/home/gl-test/share2/gitolite/hooks", + $version_string +); + +# ---------------------------------------------------------------------- + +sub static_checks { + # 3 directory names, version string + my ($bin_dir, $conf_dir, $hooks_dir, $verstr) = @_; + + try " + # $bin_dir/gl-setup contains $conf_dir + cat $bin_dir/gl-setup + ok + /\Q$conf_dir\E/ + !/\Q/tmp/share\E/ + " or die; + try " + # $conf_dir/example.gitolite.rc has $conf_dir and $hooks_dir + cat $conf_dir/example.gitolite.rc + ok + /\Q$conf_dir\E/ + /\Q$hooks_dir\E/ + !/\Q/tmp/share\E/ + " or die; + try " + # check $bin_dir got most of the sources + ls -al $bin_dir + ok + /gitolite_env.pm/ + /gitolite.pm/ + /gitolite_rc.pm/ + /gl-auth-command/ + /gl-compile-conf/ + /gl-install/ + /gl-query-rc/ + /gl-setup/ + /gl-system-install/ + " or die; + try " + # check $conf_dir got the example files + ls -al $conf_dir + ok + /example.gitolite.rc/ + /example.conf/ + /VERSION/ + " or die; + try " + # check $hooks_dir got the hooks + find $hooks_dir -type f + ok + /\Q$hooks_dir/common/gitolite-hooked\E/ + /\Q$hooks_dir/common/update\E/ + /\Q$hooks_dir/common/post-receive.mirrorpush\E/ + /\Q$hooks_dir/common/gl-pre-git.hub-sample\E/ + /\Q$hooks_dir/gitolite-admin/post-update\E/ + " or die; + try " + # check version string + cat $conf_dir/VERSION + ok + /\Q$verstr\E/ + " or die; +} + +sub clean { + chdir($ENV{HOME}); + system("rm -rf .ssh*"); + system("rm -rf .gito* gitolite-admin projects.list repositories *back.tar share share2 bin bin2 glt-adc td"); + mkdir "td"; + + system("mkdir -p .ssh; chmod go-w .ssh"); + + system("ssh-keygen -q -N '' -f .ssh/id_rsa"); + + # set up our identity (impacts some tests; don't change this!) + system('git config --global user.name "gitolite tester"'); + system('git config --global user.email "tester@example.com"'); +} + +sub cd_g { + chdir($ENV{HOME}); + chdir("gitolite"); +} + +sub setup { + chdir($ENV{HOME}); + try "cp .ssh/id_rsa.pub tester.pub"; + try "gl-setup -q tester.pub"; +} + +sub get_version { + chdir($ENV{HOME}); + chdir("gitolite"); + try "git describe --tags --long --dirty=-dt 2>/dev/null"; + return ((lines())[0]); +} diff --git a/t/t14-vrefs-1 b/t/t14-vrefs-1 new file mode 100644 index 0000000..ce36f45 --- /dev/null +++ b/t/t14-vrefs-1 @@ -0,0 +1,152 @@ +# vim: ft=sh: +tsh pwd || die '## tsh not installed?' + +# note 'tc' is an abbreviation for 'test-commit' in tsh lingo + +for wr in 0 1 +do + for bc in 0 1 + do + cd $TESTDIR + $TESTDIR/rollback || die "rollback failed" + editrc GL_WILDREPOS $wr + editrc GL_BIG_CONFIG $bc + + # ---------- + + name "INTERNAL" + echo " + @gfoo = foo + @lead = u1 + @dev2 = u2 + @dev4 = u4 + @devs = @dev2 @dev4 u6 + repo @gfoo + RW+ = @lead @devs + - VREF/COUNT/2 = @dev2 + - VREF/COUNT/4 = @dev4 + - VREF/COUNT/3/NEWFILES = u6 + - VREF/COUNT/6 = u6 + " | ugc + + name "setup" + cd ~/td + tsh " + ls -al foo; !ok; /cannot access foo: No such file or directory/ + clone u1:foo; ok; /Cloning into/ + /You appear to have cloned an empty/ + " + cd foo + tsh " + ls -Al; ok; /\.git/ + " + + name "check VREF is not called for u1" + GL_BINDIR=`gl-query-rc GL_BINDIR` + mv $GL_BINDIR/gl-VREF-COUNT ~ + tsh " + tc a1 a2 a3 a4 a5; ok; /aaf9e8e/ + push-om; ok; /new branch.*master -. master/ + !/can\'t find helper program for VREF/COUNT/ + !/hook declined/ + !/remote rejected/ + " + + name "check VREF is called for u2" + tsh " + tc b1; ok; /1f440d3/ + git push u2:foo; !ok; /can\'t find helper program for VREF/COUNT/2/ + /hook declined/ + /remote rejected/ + " + + mv ~/gl-VREF-COUNT $GL_BINDIR + name "u2 adds 1 file" + tsh " + git push u2:foo; ok; /aaf9e8e..1f440d3.*master -. master/ + " + + name "u2 adds 2 files" + tsh " + tc b2 b3; ok; /c3397f7/ + git push u2:foo; ok; /1f440d3..c3397f7.*master -. master/ + " + + name "u2 adds 3 files" + tsh " + tc c1 c2 c3; ok; /be242d7/ + git push u2:foo; !ok; /W VREF/COUNT/2 foo u2 DENIED by VREF/COUNT/2/ + /too many changed files in this push/ + /hook declined/ + /remote rejected/ + " + + name "u4 adds 3 files" + tsh " + git push u4:foo; ok; /c3397f7..be242d7.*master -. master/ + " + + name "u4 adds 4 files" + tsh " + tc d1 d2 d3 d4; ok; /88d80e2/ + git push u4:foo; ok; /be242d7..88d80e2.*master -. master/ + " + + name "u4 adds 5 files" + tsh " + tc d5 d6 d7 d8 d9; ok; /e9c60b0/ + git push u4:foo; !ok; /W VREF/COUNT/4 foo u4 DENIED by VREF/COUNT/4/ + /too many changed files in this push/ + /hook declined/ + /remote rejected/ + " + + name "u1 pushes it all" + tsh " + git push; ok; /88d80e2..e9c60b0.*master -. master/ + " + + name "u6 updates 6 old files" + tsh " + test-tick + tc d1 d2 d3 d4 d5 d6 + ok; /2773f0a/ + git push u6:foo; ok; /e9c60b0..2773f0a.*master -. master/ + tag six + " + + name "u6 updates 7 old files" + tsh " + test-tick; test-tick + tc d1 d2 d3 d4 d5 d6 d7 + ok; /98e01c5/ + git push u6:foo; !ok; /W VREF/COUNT/6 foo u6 DENIED by VREF/COUNT/6/ + /too many changed files in this push/ + /hook declined/ + /remote rejected/ + reset-h six; ok; /HEAD is now at 2773f0a/ + " + + name "u6 creates 4 new and 2 old files" + tsh " + test-tick; test-tick + tc d1 d2 n1 n2 n3 n4 + ok; /cf1e284/ + git push u6:foo; !ok; /W VREF/COUNT/3/NEWFILES foo u6 DENIED by VREF/COUNT/3/NEWFILES/ + /too many new files in this push/ + /hook declined/ + /remote rejected/ + reset-h six; ok; /HEAD is now at 2773f0a/ + " + + name "u6 creates 3 new and 3 old files" + tsh " + test-tick; test-tick + tc d1 d2 d3 n1 n2 n3 + ok; /7447dfe/ + git push u6:foo; ok; /2773f0a..7447dfe.*master -. master/ + " + + name INTERNAL + done +done diff --git a/t/t14-vrefs-2 b/t/t14-vrefs-2 new file mode 100644 index 0000000..ff6e3e3 --- /dev/null +++ b/t/t14-vrefs-2 @@ -0,0 +1,131 @@ +# vim: ft=sh: +tsh pwd || die '## tsh not installed?' + +# note 'tc' is an abbreviation for 'test-commit' in tsh lingo + +for wr in 0 1 +do + for bc in 0 1 + do + cd $TESTDIR + $TESTDIR/rollback || die "rollback failed" + editrc GL_WILDREPOS $wr + editrc GL_BIG_CONFIG $bc + + # ---------- + + name "INTERNAL" + echo " + @gfoo = foo + @lead = u1 + @senior_devs = u2 u3 + @junior_devs = u4 u5 u6 + repo @gfoo + + RW+ = @all + + RW+ VREF/COUNT/2/NO_SIGNOFF = @lead + - VREF/COUNT/2/NO_SIGNOFF = @all + + - VREF/COUNT/10/NEWFILES = @junior_devs + + - VREF/FILETYPE/AUTOGENERATED = @all + " | ugc + + name "setup" + cd ~/td + tsh " + ls -al foo; !ok; /cannot access foo: No such file or directory/ + clone u1:foo; ok; /Cloning into/ + /You appear to have cloned an empty/ + " + cd foo + tsh " + ls -Al; ok; /\.git/ + " + + name "u1 can push 15 new files" + tsh " + tc a b c d e f g h i j k l m n o + ok; /d8c0392/ + push-om; ok; /new branch.*master -. master/ + " + + name "u2 can push 2 new and 10 old files without signoff" + tsh " + tc a b c d e f g h i j u2a u2b + ok; /6787ac9/ + push u2:foo; ok; /d8c0392..6787ac9.*master -. master/ + " + name "u2 cannot push 3 new files without signoff" + tsh " + tc u2c u2d u2e; ok; /a74562b/ + push u2:foo; !ok; /W VREF/COUNT/2/NO_SIGNOFF foo u2 DENIED by VREF/COUNT/2/NO_SIGNOFF/ + /top commit message should include the text .3 new files signed-off by: tester.example.com./ + /hook declined/ + /remote rejected/ + " + name "u2 can push 15 new files with signoff" + tsh " + tc u2f u2g u2h u2i u2j u2k u2l u2m u2n u2o u2p u2q + ok; /8dd31aa/ + git commit --allow-empty -m '15 new files signed-off by: tester@example.com' + ok; /.master 6126489. 15 new files signed-off by: tester.example.com/ + push u2:foo; ok; /6787ac9..6126489.*master -. master/ + " + + name "u4 can push 2 new and 10 old files without signoff" + tsh " + tc u4a u4b a b c d e f g h i j + ok; /76c5593/ + push u4:foo; ok; /6126489..76c5593.*master -. master/ + " + name "u4 cannot push 3 new files without signoff" + tsh " + tc u4c u4d u4e; ok; /2a84398/ + push u4:foo; !ok; /W VREF/COUNT/2/NO_SIGNOFF foo u4 DENIED by VREF/COUNT/2/NO_SIGNOFF/ + /top commit message should include the text .3 new files signed-off by: tester.example.com./ + /hook declined/ + /remote rejected/ + " + name "u4 can push 10 new and 5 old files with signoff" + tsh " + tc u4f u4g u4h u4i u4j u4k u4l a b c d e + ok; /09b646a/ + git commit --allow-empty -m '10 new files signed-off by: tester@example.com' + ok; /.master 47f84b0. 10 new files signed-off by: tester.example.com/ + push u4:foo; ok; /76c5593..47f84b0.*master -. master/ + " + name "u4 cannot push 11 new files even with signoff" + tsh " + tc u4ab u4ac u4ad u4ae u4af u4ag u4ah u4ai u4aj u4ak u4al + ok; /90e7344/ + git commit --allow-empty -m '11 new files signed-off by: tester@example.com' + ok; /.master 1f36537. 11 new files signed-off by: tester.example.com/ + push u4:foo; !ok; /W VREF/COUNT/10/NEWFILES foo u4 DENIED by VREF/COUNT/10/NEWFILES/ + /too many new files in this push/ + /hook declined/ + /remote rejected/ + " + + name "test AUTOGENERATED vref" + tsh " + fetch; ok; + reset-h origin/master; + ok; + " + tsh " + tc not-really.java; ok; /0f88b2e/ + push u4:foo; ok; /47f84b0..0f88b2e.*master -. master/ + " + echo "Generated by the protocol buffer compiler. DO NOT EDIT" >> not-really.java + tsh " + commit -am pbc; ok; /aa3d8da/ + push u4:foo; !ok; /W VREF/FILETYPE/AUTOGENERATED foo u4 DENIED by VREF/FILETYPE/AUTOGENERATED/ + /hook declined/ + /remote rejected/ + " + + name INTERNAL + done +done diff --git a/t/test-driver.sh b/t/test-driver.sh index 926ba2f..7ab67f0 100755 --- a/t/test-driver.sh +++ b/t/test-driver.sh @@ -162,4 +162,4 @@ do done # this keeps changing as we add tests -echo 1..3163 +echo 1..3723 |