aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSitaram Chamarty <sitaram@atc.tcs.com>2012-02-25 19:59:49 +0530
committerSitaram Chamarty <sitaram@atc.tcs.com>2012-02-26 09:04:30 +0530
commited85bf3c0856fb6ae1166799817ddf0db3ed0517 (patch)
tree02b0815719eaae368394de18aefc90edf7fb599d
parent'filetype' VREF (diff)
downloadgitolite-gentoo-ed85bf3c0856fb6ae1166799817ddf0db3ed0517.tar.gz
gitolite-gentoo-ed85bf3c0856fb6ae1166799817ddf0db3ed0517.tar.bz2
gitolite-gentoo-ed85bf3c0856fb6ae1166799817ddf0db3ed0517.zip
vref: docs
-rw-r--r--doc/virtual-refs.mkd222
1 files changed, 222 insertions, 0 deletions
diff --git a/doc/virtual-refs.mkd b/doc/virtual-refs.mkd
new file mode 100644
index 0000000..44f618f
--- /dev/null
+++ b/doc/virtual-refs.mkd
@@ -0,0 +1,222 @@
+# 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.
+
+----
+
+[[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.
+
+(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.
+
+## other possible examples
+
+I use these. Don't analyse the numbers -- I fully expect to tune them as time
+passes; the idea is the main thing.
+
+ * 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 this (NO_SIGNOFF applies only to, and thus
+ implies, NEWFILES). This applies to everyone except me ;-)
+
+ 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
+
+ * we also need to catch auto-generated files that have filename extensions
+ that cannot be ".ignore"
+
+ - VREF/FILETYPE/AUTOGENERATED = @all
+
+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
+
+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.