gitweb/daemon now work for wild repos also

(thanks to Kevin Fleming for the need/use case)

TODO: tests
TODO: proper documentation; meanwhile, just read this:

  - you can give gitweb and daemon read rights to wild card repos also,
    and it'll all just work -- when a new repo is 'C'reated, it'll pick
    up those rights etc

  - you can assign descriptions (and owners) to individual repos as
    before, except now you can assign them to repos that actually were
    created from wild card patterns.  So for example, you can define
    rules for

        repo foo/..*

    and then assign descriptions like

        foo/repo1 = "repo one"
        foo/repo2 = "repo two"
        foo/dil "scott" = "scott's dilbert repo"

    However, this only works for repos that already exist, and only when
    you push the admin repo.

    Thumb rule: have the user create his wild repo, *then* add and push
    the admin config file with the description.  Not the other way
    around.

implementation notes:

  - wildcard support for git config revamped, refactored...

    it's not just git config that needs wildcard support.  daemon and
    gitweb access also will be needing it soon, so we start by factoring
    out the part that finds the "pattern" given a "real" repo name.

  - GL_NO_DAEMON_NO_GITWEB now gates more than just those two things;
    see doc/big-config.mkd for details

  - we trawl through $GL_REPO_BASE_ABS *once* only, collecting repo
    names and tying them to either the same name or to a wild pattern
    that the repo name was created from

  - nice little subs to setup gitweb, daemon, and git config

  - god bless $GL_REPOPATT and the day I decided to set that env var
    whenever a user hits a wild repo in any way :-)

  - the code in gl-compile-conf is very simple now.  Much nicer than
    before
This commit is contained in:
Sitaram Chamarty 2010-07-16 23:01:34 +05:30
parent 61802045d9
commit 509c73b888
3 changed files with 165 additions and 107 deletions

View file

@ -115,6 +115,43 @@ sub ln_sf
}
}
# collect repo patterns for all %repos
# for each repo passed (actual repos only please!), use either its own name if
# it exists as is in the repos hash, or find and use the pattern that matches
sub collect_repo_patts
{
my $repos_p = shift;
my %repo_patts = ();
wrap_chdir("$ENV{GL_REPO_BASE_ABS}");
for my $repo (`find . -type d -name "*.git"`) {
chomp ($repo);
$repo =~ s(\./(.*)\.git$)($1);
# if its non-wild that's all you need
if ($repos_p->{$repo}) {
$repo_patts{$repo} = $repo;
} else {
# otherwise it gets a wee bit complicated ;-)
chomp (my $creator = `cat $repo.git/gl-creater`);
for my $key (keys %$repos_p) {
my $key2 = $key;
# subst $creator in the copy with the creator name
$key2 =~ s/\$creator/$creator/g;
# match the new key against $repo
if ($repo =~ /^$key2$/) {
# and if it matched you're done for this $repo
$repo_patts{$repo} = $key;
last;
}
}
}
}
return %repo_patts;
}
# ----------------------------------------------------------------------------
# where is the rc file hiding?
@ -243,38 +280,17 @@ sub get_set_desc
}
}
# ----------------------------------------------------------------------------
# IMPORTANT NOTE: next 3 subs (setup_*) assume $PWD is the bare repo itself
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# set/unset repo configs
# ----------------------------------------------------------------------------
sub setup_repo_configs
{
my ($repo_config_p, $repo, $wild) = @_;
wrap_chdir("$ENV{GL_REPO_BASE_ABS}/$repo.git");
# prep for wild and non-wild cases separately
my $repo_patt = ''; # actually "repo or repo_pattern"
if ($wild) {
chomp (my $creator = `cat gl-creater`);
# loop each key of %repo_config and make a copy
for my $key (keys %$repo_config_p) {
my $key2 = $key;
# subst $creator in the copy with the creator name
$key2 =~ s/\$creator/$creator/g;
# match the new key against $repo
if ($repo =~ /^$key2$/) {
# if it matches, proceed
$repo_patt = $key;
last;
}
}
} else {
$repo_patt ||= $repo; # just use the repo itself...
# XXX TODO there is a remote possibility of errors if you have a
# normal repo that fits a wild pattern; needs some digging into...
}
my ($repo, $repo_patt, $repo_config_p) = @_;
while ( my ($key, $value) = each(%{ $repo_config_p->{$repo_patt} }) ) {
if ($value) {
@ -286,6 +302,57 @@ sub setup_repo_configs
}
}
# ----------------------------------------------------------------------------
# set/unset daemon access
# ----------------------------------------------------------------------------
my $export_ok = "git-daemon-export-ok";
sub setup_daemon_access
{
my ($repo, $allowed) = @_;
if ($allowed) {
system("touch $export_ok");
} else {
unlink($export_ok);
}
}
# ----------------------------------------------------------------------------
# set/unset gitweb access
# ----------------------------------------------------------------------------
my $desc_file = "description";
sub setup_gitweb_access
# this also sets "owner" for gitweb, by the way
{
my ($repo, $allowed, $desc, $owner) = @_;
if ($allowed) {
if ($desc) {
open(DESC, ">", $desc_file);
print DESC $desc . "\n";
close DESC;
}
if ($owner) {
# set the repository owner
system("git", "config", "gitweb.owner", $owner);
} else {
# remove the repository owner setting
system("git config --unset-all gitweb.owner 2>/dev/null");
}
} else {
unlink $desc_file;
system("git config --unset-all gitweb.owner 2>/dev/null");
}
# if there are no gitweb.* keys set, remove the section to keep the config file clean
my $keys = `git config --get-regexp '^gitweb\\.' 2>/dev/null`;
if (length($keys) == 0) {
system("git config --remove-section gitweb 2>/dev/null");
}
}
# ----------------------------------------------------------------------------
# parse the compiled acl
# ----------------------------------------------------------------------------

View file

@ -23,7 +23,7 @@ use warnings;
# ----------------------------------------------------------------------------
# these are set by the "rc" file
our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH, $REPO_UMASK, $GL_ADMINDIR, $RSYNC_BASE, $HTPASSWD_FILE, $GL_WILDREPOS, $GL_WILDREPOS_DEFPERMS, $GL_ADC_PATH, $SVNSERVE, $GL_SLAVE_MODE);
our ($GL_LOGT, $GL_CONF_COMPILED, $REPO_BASE, $GIT_PATH, $REPO_UMASK, $GL_ADMINDIR, $RSYNC_BASE, $HTPASSWD_FILE, $GL_WILDREPOS, $GL_WILDREPOS_DEFPERMS, $GL_ADC_PATH, $SVNSERVE, $PROJECTS_LIST, $GL_SLAVE_MODE);
# and these are set by gitolite.pm
our ($R_COMMANDS, $W_COMMANDS, $REPONAME_PATT, $REPOPATT_PATT);
our %repos;
@ -207,7 +207,10 @@ if ($perm =~ /C/) {
# it was missing, and you have create perms
wrap_chdir("$ENV{GL_REPO_BASE_ABS}");
new_repo($repo, "$GL_ADMINDIR/hooks/common", $user);
&setup_repo_configs(\%repo_config, $repo, 1);
&setup_repo_configs($repo, $ENV{GL_REPOPATT}, \%repo_config);
&setup_daemon_access($repo, $repos{$ENV{GL_REPOPATT}}{'R'}{'daemon'} || '');
&setup_gitweb_access($repo, $repos{$ENV{GL_REPOPATT}}{'R'}{'gitweb'} || '', '', '');
system("echo $repo.git >> $PROJECTS_LIST");
wrap_chdir($ENV{HOME});
}

View file

@ -470,94 +470,82 @@ unless ($GL_NO_CREATE_REPOS) {
}
# ----------------------------------------------------------------------------
# update repo configurations
# collect repo_patt for each actual repo
# ----------------------------------------------------------------------------
# no gating required for this. If you don't have any "config" lines it won't
# run anyway. An example of a config line could be:
# config hooks.emailprefix = "[foo]"
# go through each actual repo on disk, and match it to either its own name in
# the config (non-wild) or a wild pattern that matches it. Lots of things
# later will need this correspondence so we may as well snarf it in one shot
for my $repo (keys %repo_config) {
&setup_repo_configs(\%repo_config, $repo) if $repo =~ $REPONAME_PATT;
}
# wild
if (%repo_config and $GL_WILDREPOS and $GL_GITCONFIG_WILD) {
wrap_chdir("$ENV{GL_REPO_BASE_ABS}");
for my $repo (`find . -type d -name "*.git"`) {
chomp ($repo);
next unless -f "$ENV{GL_REPO_BASE_ABS}/$repo/gl-creater"; # all/only wild repos have this file
my %repo_patts = ();
%repo_patts = &collect_repo_patts(\%repos) unless $GL_NO_DAEMON_NO_GITWEB;
# $repo will look like "./foo/bar.git", make it "foo/bar" before
# calling the repo config setup sub
$repo =~ s(\./(.*)\.git$)($1);
&setup_repo_configs(\%repo_config, $repo, 1);
}
}
# NOTE: we're overloading GL_NO_DAEMON_NO_GITWEB to mean "no git config" also.
# In fact anything that requires trawling through the existing repos doing
# stuff to all of them is skipped if this variable is set. This is primarily
# for the Fedora folks, but it should be useful for anyone who has a huge set
# of repos and wants to manage gitweb/daemon/etc access via other means (they
# typically have the whole thing controlled by a web-app and a database
# anyway, and gitolite is only doing the access control and nothing more).
# ----------------------------------------------------------------------------
# handle gitweb and daemon
# various updates to all real repos
# ----------------------------------------------------------------------------
# I just assume you'll never have any *real* users called "gitweb" or "daemon"
# :-) These are now "pseduo users" -- giving them "R" access to a repo is all
# you have to do
# ----------------------------------------------------------------------------
# update repo configurations, gitweb description, daemon export-ok, etc
# ----------------------------------------------------------------------------
wrap_chdir("$ENV{GL_REPO_BASE_ABS}");
unless ($GL_NO_DAEMON_NO_GITWEB) {
# daemons first...
for my $repo (sort keys %repos) {
next unless $repo =~ $REPONAME_PATT;
next if $repo =~ m(^\@|EXTCMD/); # these are not real repos
my $export_ok = "$repo.git/git-daemon-export-ok";
if ($repos{$repo}{'R'}{'daemon'}) {
system("touch $export_ok");
} else {
unlink($export_ok);
}
}
# all these require a "chdir" to the repo, so we club them for efficiency
my %projlist = ();
# ...then gitwebs
for my $repo (sort keys %repos) {
next unless $repo =~ $REPONAME_PATT;
next if $repo =~ m(^\@|EXTCMD/); # these are not real repos
my $desc_file = "$repo.git/description";
# note: having a description also counts as enabling gitweb
if ($repos{$repo}{'R'}{'gitweb'} or $desc{"$repo.git"}) {
# for each real repo (and remember this will be empty, thus skipping all this,
# if $GL_NO_DAEMON_NO_GITWEB is on!)
for my $repo (keys %repo_patts) {
my $repo_patt = $repo_patts{$repo}; # if non-wild, $repo_patt will be eq $repo anyway
wrap_chdir("$ENV{GL_REPO_BASE_ABS}/$repo.git");
# git config
if ($repo_patt eq $repo or $GL_GITCONFIG_WILD) {
# erm, what that means is that it's either a non-wild repo being
# config'd or a wild one but gitconfig is allowed on wilds.
# XXX do we really need GL_GITCONFIG_WILD now? It was meant to be
# only an efficiency thing, but that was before this whole revamp;
# we already trawl through $REPO_BASE exactly once now anyway!
# ...need to think about this
&setup_repo_configs($repo, $repo_patt, \%repo_config) if $repo_config{$repo_patt};
}
# daemon is easy
&setup_daemon_access($repo, $repos{$repo_patt}{'R'}{'daemon'} || '');
# gitweb is a little more complicated. Here're some notes:
# - "setup_gitweb_access" also sets "owner", despite the name
# - specifying a description also counts as enabling gitweb
# - description and owner are not specified for wildrepos; they're
# specified for *actual* repos, even if the repo was created by a
# wild card spec and "C" permissions. If you see the
# conf/example.conf file, you will see that repo owner/desc don't go
# into the "repo foo" section; they're essentialy independent.
# Anyway, I believe it doesn't make sense to have all wild repos
# (for some pattern) to have the same description and owner.
if ($repos{$repo_patt}{'R'}{'gitweb'} or $desc{"$repo.git"}) {
$projlist{"$repo.git"} = 1;
# add the description file; no messages to user or error checking :)
$desc{"$repo.git"} and open(DESC, ">", $desc_file) and print DESC $desc{"$repo.git"} . "\n" and close DESC;
if ($owner{"$repo.git"}) {
# set the repository owner
system("git", "--git-dir=$repo.git", "config", "gitweb.owner", $owner{"$repo.git"});
&setup_gitweb_access($repo, 1, $desc{"$repo.git"} || '', $owner{"$repo.git"} || '');
} else {
# remove the repository owner setting
system("git --git-dir=$repo.git config --unset-all gitweb.owner 2>/dev/null");
}
} else {
# delete the description file; no messages to user or error checking :)
unlink $desc_file;
# remove the repository owner setting
system("git --git-dir=$repo.git config --unset-all gitweb.owner 2>/dev/null");
}
# unless there are other gitweb.* keys set, remove the section to keep the
# config file clean
my $keys = `git --git-dir=$repo.git config --get-regexp '^gitweb\\.' 2>/dev/null`;
if (length($keys) == 0) {
system("git --git-dir=$repo.git config --remove-section gitweb 2>/dev/null");
&setup_gitweb_access($repo, 0, '', '');
}
}
# update the project list
# write out the project list
my $projlist_fh = wrap_open( ">", $PROJECTS_LIST);
for my $proj (sort keys %projlist) {
print $projlist_fh "$proj\n";
}
close $projlist_fh;
}
# ----------------------------------------------------------------------------
# "compile" ssh authorized_keys