Permalink
Browse files

3 new VREFs plus doc

  - 'dupkeys' -- catch duplicate keys in keydir
  - 'email-check' -- "you can only push your own commits"

plus, 'merge-check' -- how we could have done the no-merges policy
  • Loading branch information...
sitaramc committed Feb 26, 2012
1 parent ed85bf3 commit 183952013476e296be00074715d3b3b1539b87cf
@@ -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...
@@ -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
+
@@ -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;
@@ -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];
-'
View
@@ -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;
Oops, something went wrong.

2 comments on commit 1839520

@juhasz

This comment has been minimized.

Show comment
Hide comment
@juhasz

juhasz Mar 17, 2012

Why are these files removed? update.secondary still works, but it was easier to use the sample file...

Why are these files removed? update.secondary still works, but it was easier to use the sample file...

@sitaramc

This comment has been minimized.

Show comment
Hide comment
@sitaramc

sitaramc Mar 17, 2012

Owner
Owner

sitaramc replied Mar 17, 2012

Please sign in to comment.