From 4373c5c74c5150b8f9619da7d9e9f52e6d99014f Mon Sep 17 00:00:00 2001 From: Sitaram Chamarty Date: Wed, 13 Jun 2012 14:43:19 +0530 Subject: [PATCH] GL_BINDIR2 becomes LOCAL_CODE, allows hook propagation also... plus a bunch of doc changes --- doc/cust.mkd | 107 ++++++++++++++++++++++----- doc/dev-notes.mkd | 34 ++++----- doc/master-toc.mkd | 2 +- doc/rc.mkd | 23 +----- doc/special.mkd | 38 ---------- src/lib/Gitolite/Conf/Store.pm | 11 ++- src/lib/Gitolite/Hooks/PostUpdate.pm | 9 +++ src/lib/Gitolite/Rc.pm | 8 +- 8 files changed, 126 insertions(+), 106 deletions(-) diff --git a/doc/cust.mkd b/doc/cust.mkd index 1cdf07b..8c7607b 100644 --- a/doc/cust.mkd +++ b/doc/cust.mkd @@ -5,7 +5,9 @@ not considered "core". This keeps the core simpler, and allows you to enhance gitolite for your own purposes without too much fuss. (As an extreme example, even mirroring is not in core now!) -(Also, please see the [developer notes][dev-notes] page). +This document will tell you about the types of non-core programs, and +how/where to install your own. (Actually *writing* the code is described in +the [developer notes][dev-notes] page). ---- @@ -13,13 +15,13 @@ even mirroring is not in core now!) ---- -## types of non-core programs +## introduction There are 5 basic types of non-core programs. * *Commands* can be run from the shell command line. Among those, the ones listed in the COMMANDS hash of the rc file can also be run remotely. - * *Hooks* are standard git hooks; see below. + * *Hooks* are standard git hooks. * *Sugar scripts* change the conf language for your convenience. The word sugar comes from "syntactic sugar". * *Triggers* are to gitolite what hooks are to git. I just chose a @@ -29,9 +31,76 @@ There are 5 basic types of non-core programs. [Here][non-core] is a list of non-core programs shipped with gitolite, with some description of each. -## #commands gitolite "commands" +## locations -Gitolite comes with several commands that users can run. Remote users run the +### default/primary location of non-core programs + +Regardless of how you installed gitolite, `gitolite query-rc GL_BINDIR` will +tell you where the programs reside. Within that directory, the locations of +non-core programs are: + + * `commands` for commands. + * `syntactic-sugar` for sugar scripts. + * `triggers` and `lib/Gitolite/Triggers` for triggers ([this][triggers] will + explain the difference). + * `VREF` for [VREFs][vref]. + +### #localcode alternate location -- the `LOCAL_CODE` rc variable + +If you want to add new non-core programs to your installation, or override the +shipped non-core programs with your own versions, it's easy enough to simply +copy your programs to the appropriate directory above, but then they'd get +wiped out on the next upgrade. + +A simple, "git-ish", method is to maintain a "local" branch in your clone of +the gitolite source repo and make your changes there. Maintain them using +rebase or merge when you 'git pull' gitolite itself, then use the rebased or +merged "local" as the source for your gitolite upgrades. Works very nicely, +and uses nothing but your git knowledge. + +Sadly, it doesn't work for people installing from RPMs/DEBs; their "primary +location" has already been setup, so any site-local customisations have to be +done elsewhere. + +This is where `LOCAL_CODE` comes in. If you define the `LOCAL_CODE` rc +variable, then its value (**please use a FULL path**) describes a location +where you can have any or all of these subdirectories: + + * `commands` + * `hooks/common` + * `syntactic-sugar` + * `triggers` and `lib/Gitolite/Triggers` + * `VREF` + +You might have noticed there's a new `hooks/common` directory here so you can +add hooks also using this mechanism. Unlike the rest of the directories, +adding new hooks to `hooks/common` requires that you follow up with `gitolite +setup`, or at least `gitolite setup --hooks-only`. + +### #pushcode managing custom code via the gitolite-admin repo + +The location given in `LOCAL_CODE` could be anywhere on disk. + +However, if you point it to someplace inside `$GL_ADMIN_BASE` (i.e., +`$HOME/.gitolite`), then you can version those programs using the +gitolite-admin repo. + +I suggest using a directory called "local-code" within the gitolite-admin repo +that contains as much of the above directory structure you need. If you do +that, then this is what you'd have in the rc file: + + LOCAL_CODE => "$ENV{HOME}/.gitolite/local-code", + +When you do this, gitolite takes care of everything automatically, including +running `gitolite setup --hooks-only` when you change any hooks and push. +**However, if you do this, anyone who can push changes to the admin repo will +effectively be able to run any arbitrary command on the server.** + +## types of non-core programs + +### #commands gitolite "commands" + +Gitolite comes with several commands that users can run. Remote users run commands by saying: ssh git@host command-name [args...] @@ -46,23 +115,25 @@ checking for the presence of env var `GL_USER`. You can get a **list of available commands** by using the `help` command. Naturally, a remote user will see a much smaller list than the server user. -You add commands to the "allowed from remote" list by adding its name (or -uncommenting it if it's already added but commented out) to the COMMANDS hash -in the [rc][] file. +You allow a command to be run from remote clients by adding its name to (or +uncommenting it if it's already added but commented out) the COMMANDS hash in +the [rc][] file. -If you write your own commands, put them in src/commands. +### #hooks hooks and gitolite -## #hooks hooks and gitolite +You can install any hooks except these: -Gitolite uses the `update` hook for all repos. In addition, it uses the -`post-update` hook for the gitolite-admin repo. + * (all repos) gitolite reserves the `update` hook. See the "update hook" + section in [dev-notes][] if you want additional update hook functionality. -If you want to add your own hook, it's easy as long as it's not the 'update' -hook. Just add it to `$HOME/.gitolite/hooks/common` and run `gitolite setup`. + * (gitolite-admin repo only) gitolite reserves the `post-update` hook. -The rest is between you and 'man githooks' :-) +How/where to install them is described in detail in the "locations" section +above, especially [this][localcode] and [this][pushcode]. The summary is that +you put them in the "hooks/common" sub-directory within the directory whose +name is given in the `LOCAL_CODE` rc variable. -## #sugar syntactic sugar +### #sugar syntactic sugar Sugar scripts help you change the perceived syntax of the conf language. The base syntax of the language is very simple, so sugar scripts take something @@ -74,10 +145,10 @@ lines), while the parser in the core gitolite engine does not change. If you want to write your own sugar scripts, please read the "your own sugar" section in [dev-notes][] first then email me. -## triggers +### triggers Triggers have their own [document][triggers]. -## VREFs +### VREFs VREFs also have their own [document][vref]. diff --git a/doc/dev-notes.mkd b/doc/dev-notes.mkd index 501d44f..18cd005 100644 --- a/doc/dev-notes.mkd +++ b/doc/dev-notes.mkd @@ -7,8 +7,9 @@ Gitolite has a huge bunch of existing features that gradually need to moved over. Plus you may want to write your own programs to interact with it. -Here are some random notes on developing hooks, commands, triggers, and sugar -scripts. +**This document is about *writing* hooks, commands, triggers, VREFS, and sugar +scripts. *Installing* them, including "where and how", is described +[here][localcode]**. ## environment variables and other inputs @@ -56,14 +57,19 @@ serve as documentation. ## writing your own... +### ...commands + +Commands are standalone programs, in any language you like. They simply +receive the arguments you append. In addition, the env var `GL_USER` is +available if it is being run remotely. src/commands/desc is the best example +at present. + ### ...hooks #### anything but the update hook -If you want to add your own hook, it's easy as long as it's not the 'update' -hook. Just add it to `$HOME/.gitolite/hooks/common` and run `gitolite setup`. - -The rest is between you and 'man githooks' :-) +If you want to add any hook other than the update hook, 'man githooks' is all +you need. #### update hook @@ -71,7 +77,8 @@ If you want to add additional `update` hook functionality, do this: * Write and test your update hook separately from gitolite. - * Now add the code to src/VREF. Let's say it is called "foo". + * Now add the code as a VREF (see [here][localcode] for details). Let's say + you called it "foo". * To call your new update hook to all accesses for all repos, add this to the end of your conf file: @@ -79,24 +86,13 @@ If you want to add additional `update` hook functionality, do this: repo @all - VREF/foo = @all -As you probably guessed, you can now make your additional update hooks more +As you probably guessed, you can make your additional update hooks more selective, applying them only to some repos / users / combinations. Note: a normal update hook expects 3 arguments (ref, old SHA, new SHA). A VREF will get those three, followed by at least 4 more. Your VREF should just ignore the extra args. -### ...commands - -You can add your own commands. You can run them on the server (example, -`gitolite access`). Then you can enable certain commands to be allowed to run -by a remote user by adding them to the "COMMANDS" hash of the [rc][] file. - -Commands are standalone programs, in any language you like. They simply -receive the arguments you append. In addition, the env var `GL_USER` is -available if it is being run remotely. src/commands/desc is the best example -at present. - ### ...trigger programs Trigger programs run at specific points in gitolite's execution, with specific diff --git a/doc/master-toc.mkd b/doc/master-toc.mkd index 9da5224..41a8d74 100644 --- a/doc/master-toc.mkd +++ b/doc/master-toc.mkd @@ -101,7 +101,7 @@ * the [subconf][] command * ([link][partial-copy]: faking selective READ control) * using pubkeys obtained [from elsewhere][keysonly] - * [updating hooks][pushhook] via the admin repo + * ([link][pushcode]: updating code via the admin repo) ## interfacing with [external][] tools diff --git a/doc/rc.mkd b/doc/rc.mkd index 17f2616..98ad1d7 100644 --- a/doc/rc.mkd +++ b/doc/rc.mkd @@ -98,24 +98,7 @@ information. DEFAULT_ROLE_PERMS => "READERS \@all\nWRITERS \@senior_devs", - * `GL_BINDIR2`, string + * `LOCAL_CODE`, string - This is useful when you install gitolite system-wide, but want to add or - override commands, VREFs, triggers, etc., by putting them somewhere in the - hosting user's home directory (i.e., without requiring root privileges). - - Add a new variable `GL_BINDIR2` to the the rc file. Example: - - GL_BINDIR2 => "$ENV{HOME}/gitolite/src2", - - In that directory, create as much of the following directory structure as - you need to add your programs: - - . - |-- commands - |-- lib - | `-- Gitolite - | `-- Triggers - |-- syntactic-sugar - |-- triggers - `-- VREF + This is described in more detail [here][localcode]. Please be aware + **this must be a FULL path**, not a relative path. diff --git a/doc/special.mkd b/doc/special.mkd index 3530920..37cfe08 100644 --- a/doc/special.mkd +++ b/doc/special.mkd @@ -68,41 +68,3 @@ Then write a script that Run this from cron or however you want. -## #pushhook updating hooks via the admin repo - -Gitolite by default maintains the distinction between someone who can push to -the admin repo and someone who has shell access to the server. As a result, -you cannot change any hooks merely by pushing the admin repo. - -But some people don't care about this and want to do it anyway. Besides, -there *is* one advantage: your hooks can also be versioned now. - -So here's how to add/change hooks when you push the gitolite-admin repo: - -1. Put all common hooks in a new directory called 'common-hooks'. - -2. Create a trigger program called 'propagate-hooks' in 'src/triggers' that - contains this: - - #!/bin/sh - - cd $GL_ADMIN_BASE - cp -a common-hooks/* hooks/common - - gitolite setup --hooks-only - -3. Add this to the `POST_COMPILE` trigger list in the rc file. - -And that should be it, pretty much. - -If you have lots of repos this is inefficient -- because the hook fixup will -run on *any* change to the admin repo, even if the change has nothing to do -with hooks. - -If you're concerned about this, put the script in 'src/commands' instead of -'src/triggers'. (You might want to add some access control; see other -commands for inspiration, but it's not really needed in this case). - -Then, whenever any hooks have changed, the admin can run - - ssh git@host propagate-hooks diff --git a/src/lib/Gitolite/Conf/Store.pm b/src/lib/Gitolite/Conf/Store.pm index f4f4cf2..8fd9f47 100644 --- a/src/lib/Gitolite/Conf/Store.pm +++ b/src/lib/Gitolite/Conf/Store.pm @@ -323,14 +323,13 @@ sub store_common { $hook_reset++; } - # propagate user hooks + # propagate user-defined (custom) hooks to all repos + ln_sf( "$rc{LOCAL_CODE}/hooks/common", "*", "$repo.git/hooks" ) if $rc{LOCAL_CODE}; + + # override/propagate gitolite defined hooks for all repos ln_sf( "$rc{GL_ADMIN_BASE}/hooks/common", "*", "$repo.git/hooks" ); - - # propagate admin hook + # override/propagate gitolite defined hooks for the admin repo ln_sf( "$rc{GL_ADMIN_BASE}/hooks/gitolite-admin", "*", "$repo.git/hooks" ) if $repo eq 'gitolite-admin'; - - # g2 diff: no "site-wide" hooks (the stuff in between gitolite hooks - # and user hooks) anymore. I don't think anyone used them anyway... } } diff --git a/src/lib/Gitolite/Hooks/PostUpdate.pm b/src/lib/Gitolite/Hooks/PostUpdate.pm index 3605de0..961db1a 100644 --- a/src/lib/Gitolite/Hooks/PostUpdate.pm +++ b/src/lib/Gitolite/Hooks/PostUpdate.pm @@ -25,11 +25,20 @@ sub post_update { tsh_try("git ls-tree --name-only master"); _die "no files/dirs called 'hooks' or 'logs' are allowed" if tsh_text() =~ /^(hooks|logs)$/m; + my $hooks_changed = 0; { local $ENV{GIT_WORK_TREE} = $rc{GL_ADMIN_BASE}; + + tsh_try("git diff --name-only master"); + $hooks_changed++ if tsh_text() =~ m(/hooks/common/); + # the leading slash ensure that this hooks/common directory is below + # some top level directory, not *at* the top. That's LOCAL_CODE, and + # it's actual name could be anything but it doesn't matter to us. + tsh_try("git checkout -f --quiet master"); } _system("gitolite compile"); + _system("gitolite setup --hooks-only") if $hooks_changed; _system("gitolite trigger POST_COMPILE"); exit 0; diff --git a/src/lib/Gitolite/Rc.pm b/src/lib/Gitolite/Rc.pm index 81479fe..8f8fe7b 100644 --- a/src/lib/Gitolite/Rc.pm +++ b/src/lib/Gitolite/Rc.pm @@ -87,7 +87,7 @@ do $ENV{G3T_RC} if exists $ENV{G3T_RC} and -r $ENV{G3T_RC}; # setup some perl/rc/env vars # ---------------------------------------------------------------------- -unshift @INC, "$rc{GL_BINDIR2}/lib" if $rc{GL_BINDIR2}; +unshift @INC, "$rc{LOCAL_CODE}/lib" if $rc{LOCAL_CODE}; $ENV{PATH} = "$ENV{GL_BINDIR}:$ENV{PATH}"; @@ -232,13 +232,13 @@ sub trigger { } sub _which { - # looks for a file in GL_BINDIR2 or GL_BINDIR. Returns whichever exists - # (GL_BINDIR2 preferred if defined) or 0 if not found. + # looks for a file in LOCAL_CODE or GL_BINDIR. Returns whichever exists + # (LOCAL_CODE preferred if defined) or 0 if not found. my $file = shift; my $mode = shift; # could be 'x' or 'r' my @files = ("$rc{GL_BINDIR}/$file"); - unshift @files, ("$rc{GL_BINDIR2}/$file") if $rc{GL_BINDIR2}; + unshift @files, ("$rc{LOCAL_CODE}/$file") if $rc{LOCAL_CODE}; for my $f ( @files ) { return $f if -x $f;