diff --git a/doc/misc.mkd b/doc/misc.mkd index 0b8c9e7..d598401 100644 --- a/doc/misc.mkd +++ b/doc/misc.mkd @@ -78,3 +78,55 @@ is effectively the same as this, for repo foo: R = gitweb This extends to patterns also, but I'll leave an example for later. + +## #subconf the subconf command + +This is just like the include command: + + subconf "foo/bar.conf" # example 1 + subconf "foo/*.conf" # example 2 + +with the difference that, for the duration of the file(s) being included, a +subconf restriction is in effect. This restrictions limits the repos that can +be access controlled by the lines within the included file(s). + +Here's how it works. First, a subconf *name* is derived from the filename +being included, which is basically the basename of the file. For example 1 +that is "bar". For example 2, assuming foo contains "a.conf" and "b.conf", +the subconf name is "a" while processing "a.conf", and "b" while processing +"b.conf". + +A variation of the subconf command allows you to specify the subconf *name* +explicitly, while including files as before: + + subconf frob "foo/bar.conf # example 3 + subconf frob "foo/*.conf # example 4 + +In this case the subconf name is "frob" in both cases. + +A subconf restricts the repos that can be named in 'repo' lines while the +subconf is in effect. If the subconf name is "foo", the conf lines parsed +while under the subconf restriction can only refer to + + * a repo called 'foo' + * a repo group called '@foo' + * the members of the same repo group '@foo' + * match a regex that is a member of the repo group '@foo' + +In the last 3 cases, the repo group '@foo' must be defined in the main conf +file (i.e., outside the subconf restriction). + +For example, if you have this in the main conf file: + + @foo = bar baz frob/..* + + subconf "foo.conf" + +then foo.conf can only refer to 'foo', '@foo', 'bar', 'baz', or any repo name +matching the pattern `frob/..*`. + +## #HOSTNAME HOSTNAME substitution + +Wherever gitolite sees the word `%HOSTNAME`, it will replace it with the +HOSTNAME supplied in the rc file, if one was supplied. This is mainly useful +in [mirroring][]. diff --git a/src/Gitolite/Conf/Explode.pm b/src/Gitolite/Conf/Explode.pm index c19975b..f72878b 100644 --- a/src/Gitolite/Conf/Explode.pm +++ b/src/Gitolite/Conf/Explode.pm @@ -9,6 +9,7 @@ package Gitolite::Conf::Explode; use Exporter 'import'; +use Gitolite::Rc; use Gitolite::Common; use strict; @@ -33,10 +34,13 @@ sub explode { my $line = cleanup_conf_line($_); next unless $line =~ /\S/; + # subst %HOSTNAME word if rc defines a hostname, else leave as is + $line =~ s/%HOSTNAME\b/$rc{HOSTNAME}/g if $rc{HOSTNAME}; + $line = prefix_groupnames( $line, $subconf ) if $subconf ne 'master'; - if ( $line =~ /^(include|subconf) (\S.+)$/ ) { - incsub( $1, $2, $subconf, $out ); + if ( $line =~ /^(include|subconf) (?:(\S+) )?(\S.+)$/ ) { + incsub( $1, $2, $3, $subconf, $out ); } else { # normal line, send it to the callback function push @{$out}, $line; @@ -46,9 +50,9 @@ sub explode { sub incsub { my $is_subconf = ( +shift eq 'subconf' ); - my ( $include_glob, $subconf, $out ) = @_; + my ( $new_subconf, $include_glob, $current_subconf, $out ) = @_; - _die "subconf $subconf attempting to run 'subconf'\n" if $is_subconf and $subconf ne 'master'; + _die "subconf $current_subconf attempting to run 'subconf'\n" if $is_subconf and $current_subconf ne 'master'; _die "invalid include/subconf file/glob '$include_glob'" unless $include_glob =~ /^"(.+)"$/ @@ -65,11 +69,11 @@ sub incsub { next if already_included($file); if ($is_subconf) { - push @{$out}, "subconf $basename"; - explode( $file, $basename, $out ); - push @{$out}, "subconf $subconf"; + push @{$out}, "subconf " . ( $new_subconf || $basename ); + explode( $file, ( $new_subconf || $basename ), $out ); + push @{$out}, "subconf $current_subconf"; } else { - explode( $file, $subconf, $out ); + explode( $file, $current_subconf, $out ); } } } diff --git a/src/Gitolite/Conf/Store.pm b/src/Gitolite/Conf/Store.pm index eef34d0..0c917ac 100644 --- a/src/Gitolite/Conf/Store.pm +++ b/src/Gitolite/Conf/Store.pm @@ -218,7 +218,7 @@ sub store { sub parse_done { for my $ig ( sort keys %ignored ) { - _warn "$ig.conf attempting to set access for " . join( ", ", sort keys %{ $ignored{$ig} } ); + _warn "subconf '$ig' attempting to set access for " . join( ", ", sort keys %{ $ignored{$ig} } ); } } diff --git a/t/deleg-1.t b/t/deleg-1.t index b969543..5f4d6e5 100755 --- a/t/deleg-1.t +++ b/t/deleg-1.t @@ -81,7 +81,7 @@ put "conf/fragments/u3r.conf", ' RW+ = u6 '; try "SUBCONF_PUSH u3 u3; - /WARNING: u3r.conf attempting to set access for r2a/ + /WARNING: subconf 'u3r' attempting to set access for r2a/ "; try "git reset --hard origin/master; ok"; @@ -95,5 +95,5 @@ put "conf/fragments/u3r.conf", ' '; try "SUBCONF_PUSH u3 u3 - /WARNING: u3r.conf attempting to set access for locally modified \@u3r/ + /WARNING: subconf 'u3r' attempting to set access for locally modified \@u3r/ "; diff --git a/t/deleg-2.t b/t/deleg-2.t index 54f5156..cf55972 100755 --- a/t/deleg-2.t +++ b/t/deleg-2.t @@ -103,7 +103,7 @@ put "conf/fragments/u3r.conf", ' RW+ = u6 '; try "SUBCONF_PUSH u3 u3; - /WARNING: u3r.conf attempting to set access for r2a/ + /WARNING: subconf 'u3r' attempting to set access for r2a/ "; try "git reset --hard origin/master; ok"; @@ -117,5 +117,5 @@ put "conf/fragments/u3r.conf", ' '; try "SUBCONF_PUSH u3 u3 - /WARNING: u3r.conf attempting to set access for locally modified \@u3r/ + /WARNING: subconf 'u3r' attempting to set access for locally modified \@u3r/ "; diff --git a/t/hostname.t b/t/hostname.t new file mode 100755 index 0000000..a6cbde2 --- /dev/null +++ b/t/hostname.t @@ -0,0 +1,77 @@ +#!/usr/bin/perl +use strict; +use warnings; + +# this is hardcoded; change it if needed +use lib "src"; +use Gitolite::Test; + +# %HOSTNAME tests +# ---------------------------------------------------------------------- + +try "plan 60"; + +try "pwd"; +my $od = text(); +chomp($od); + +# without setting HOSTNAME in rc +confreset;confadd ' + + repo foo + RW dev/%HOSTNAME = u1 +'; + +try "ADMIN_PUSH set1; /FATAL/"; +try "/bad ref 'refs/heads/dev/%HOSTNAME'/"; + +# make a hostname entry +$ENV{G3T_RC} = "$ENV{HOME}/g3trc"; +put "$ENV{G3T_RC}", "\$rc{HOSTNAME} = 'frodo';\n"; + +confreset;confadd ' + + repo bar + RW %HOSTNAME_baz = u1 +'; + +try "ADMIN_PUSH set1; /FATAL/"; +try "/bad ref 'refs/heads/%HOSTNAME_baz'/"; + +confreset;confadd ' + + repo bar + RW %HOSTNAME/ = u1 + RW %HOSTNAME-baz = u1 +'; + +try "ADMIN_PUSH set1; !/FATAL/"; +try " + gitolite access bar u2 R any; /R any bar u2 DENIED by fallthru/ + gitolite access bar u2 W any; /W any bar u2 DENIED by fallthru/ + gitolite access bar u1 W any; !/DENIED/; /refs/heads/frodo/; !/baz/ + gitolite access bar u1 R any; !/DENIED/; /refs/heads/frodo/; !/baz/ + gitolite access bar u1 R refs/heads/frodo; /R refs/heads/frodo bar u1 DENIED by fallthru/ + gitolite access bar u1 W refs/heads/frodo; /W refs/heads/frodo bar u1 DENIED by fallthru/ + gitolite access bar u1 R refs/heads/frodo/1; !/DENIED/; /refs/heads/frodo/; !/baz/ + gitolite access bar u1 W refs/heads/frodo/1; !/DENIED/; /refs/heads/frodo/; !/baz/ + gitolite access bar u1 R refs/heads/sam; /R refs/heads/sam bar u1 DENIED by fallthru/ + gitolite access bar u1 W refs/heads/sam; /W refs/heads/sam bar u1 DENIED by fallthru/ + gitolite access bar u1 R refs/heads/master; /R refs/heads/master bar u1 DENIED by fallthru/ + gitolite access bar u1 W refs/heads/master; /W refs/heads/master bar u1 DENIED by fallthru/ + + gitolite access bar u1 R refs/heads/frodo-baz; !/DENIED/; /refs/heads/frodo-baz/ + gitolite access bar u1 W refs/heads/frodo-baz; !/DENIED/; /refs/heads/frodo-baz/ +"; + +confreset;confadd ' + + repo foo-%HOSTNAME + RW = u1 +'; + +try "ADMIN_PUSH set1; !/FATAL/"; +try " + gitolite list-repos; /foo-frodo/ + gitolite list-phy-repos; /foo-frodo/ +"; diff --git a/t/include-subconf.t b/t/include-subconf.t index a5fc908..a3d2a57 100755 --- a/t/include-subconf.t +++ b/t/include-subconf.t @@ -9,7 +9,7 @@ use Gitolite::Test; # include and subconf # ---------------------------------------------------------------------- -try 'plan 37'; +try 'plan 55'; confreset; confadd ' include "i1.conf" @@ -39,7 +39,7 @@ try "ADMIN_PUSH set2; !/FATAL/" or die text(); try " /i1.conf already included/ - /i2.conf attempting to set access for \@i1, b2, bar, i1, locally modified \@g2/ + /subconf 'i2' attempting to set access for \@i1, b2, bar, i1, locally modified \@g2/ !/attempting to set access.*i2/ /Initialized.*empty.*baz.git/ /Initialized.*empty.*foo.git/ @@ -62,7 +62,7 @@ confadd 'g2.conf', ' try "ADMIN_PUSH set3; !/FATAL/" or die text(); try " - /g2.conf attempting to set access for locally modified \@g2/ + /subconf 'g2' attempting to set access for locally modified \@g2/ !/Initialized.*empty/ "; @@ -80,3 +80,37 @@ confadd 'g2.conf', ' try " ADMIN_PUSH set3; ok; /FATAL: subconf g2 attempting to run 'subconf'/ "; + +# ---------------------------------------------------------------------- + +confreset; confadd ' + include "i1.conf" + @i2 = b1 + subconf i2 "eye2.conf" +'; +confadd 'eye2.conf', ' + repo @eye2 + RW = u2 +'; + +try "ADMIN_PUSH set2; !/FATAL/" or die text(); + +try " + /subconf 'i2' attempting to set access for \@eye2/ +"; + +confreset; confadd ' + include "i1.conf" + @i2 = b1 + subconf i2 "eye2.conf" +'; +confadd 'eye2.conf', ' + repo @i2 + RW = u2 +'; + +try "ADMIN_PUSH set2; !/FATAL/" or die text(); + +try " + !/subconf 'i2' attempting to set access for \@eye2/ +";