sugar high!
make it easy to handle syntactic sugar. In summary, compile now calls parse(sugar('gitolite.conf')). Details: - cleanup_conf_line went from subar.pm to common.pm - explode() and minions went from conf.pm to the new explode.pm - the callback went away; everyone just passes whole arrays around now - the new sugar() takes a filename and returns a listref - all sugar scripts take and return a listref - the first "built-in" sugar is written (setting gitweb.owner and gitweb.description) the new RC file format (of being a hash called %rc) is getting a nice workout :-)
This commit is contained in:
parent
4ab8db4925
commit
acb2f8fe8e
|
@ -8,7 +8,7 @@ package Gitolite::Common;
|
||||||
print2 dbg _mkdir _open ln_sf tsh_rc sort_u
|
print2 dbg _mkdir _open ln_sf tsh_rc sort_u
|
||||||
say _warn _chdir _print tsh_text list_phy_repos
|
say _warn _chdir _print tsh_text list_phy_repos
|
||||||
say2 _die slurp tsh_lines
|
say2 _die slurp tsh_lines
|
||||||
trace tsh_try
|
trace cleanup_conf_line tsh_try
|
||||||
usage tsh_run
|
usage tsh_run
|
||||||
);
|
);
|
||||||
#>>>
|
#>>>
|
||||||
|
@ -143,6 +143,19 @@ sub sort_u {
|
||||||
return \@sort_u;
|
return \@sort_u;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub cleanup_conf_line {
|
||||||
|
my $line = shift;
|
||||||
|
|
||||||
|
# kill comments, but take care of "#" inside *simple* strings
|
||||||
|
$line =~ s/^((".*?"|[^#"])*)#.*/$1/;
|
||||||
|
# normalise whitespace; keeps later regexes very simple
|
||||||
|
$line =~ s/=/ = /;
|
||||||
|
$line =~ s/\s+/ /g;
|
||||||
|
$line =~ s/^ //;
|
||||||
|
$line =~ s/ $//;
|
||||||
|
return $line;
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
my @phy_repos = ();
|
my @phy_repos = ();
|
||||||
|
|
||||||
|
|
112
Gitolite/Conf.pm
112
Gitolite/Conf.pm
|
@ -23,13 +23,6 @@ use warnings;
|
||||||
|
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
# 'seen' for include/subconf files
|
|
||||||
my %included = ();
|
|
||||||
# 'seen' for group names on LHS
|
|
||||||
my %prefixed_groupname = ();
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------
|
|
||||||
|
|
||||||
sub compile {
|
sub compile {
|
||||||
trace(3);
|
trace(3);
|
||||||
# XXX assume we're in admin-base/conf
|
# XXX assume we're in admin-base/conf
|
||||||
|
@ -37,7 +30,7 @@ sub compile {
|
||||||
_chdir( $rc{GL_ADMIN_BASE} );
|
_chdir( $rc{GL_ADMIN_BASE} );
|
||||||
_chdir("conf");
|
_chdir("conf");
|
||||||
|
|
||||||
explode( 'gitolite.conf', 'master', \&parse );
|
parse(sugar('gitolite.conf'));
|
||||||
|
|
||||||
# the order matters; new repos should be created first, to give store a
|
# the order matters; new repos should be created first, to give store a
|
||||||
# place to put the individual gl-conf files
|
# place to put the individual gl-conf files
|
||||||
|
@ -45,41 +38,11 @@ sub compile {
|
||||||
store();
|
store();
|
||||||
}
|
}
|
||||||
|
|
||||||
sub explode {
|
|
||||||
trace( 4, @_ );
|
|
||||||
my ( $file, $subconf, $parser ) = @_;
|
|
||||||
|
|
||||||
# $parser is a ref to a callback; if not supplied we just print
|
|
||||||
$parser ||= sub { print shift, "\n"; };
|
|
||||||
|
|
||||||
# seed the 'seen' list if it's empty
|
|
||||||
$included{ device_inode("conf/gitolite.conf") }++ unless %included;
|
|
||||||
|
|
||||||
my $fh = _open( "<", $file );
|
|
||||||
my @fh = <$fh>;
|
|
||||||
my @lines = macro_expand( "# BEGIN $file\n", @fh, "# END $file\n" );
|
|
||||||
my $line;
|
|
||||||
while (@lines) {
|
|
||||||
$line = shift @lines;
|
|
||||||
|
|
||||||
$line = cleanup_conf_line($line);
|
|
||||||
next unless $line =~ /\S/;
|
|
||||||
|
|
||||||
$line = prefix_groupnames( $line, $subconf ) if $subconf ne 'master';
|
|
||||||
|
|
||||||
if ( $line =~ /^(include|subconf) "(.+)"$/ or $line =~ /^(include|subconf) '(.+)'$/ ) {
|
|
||||||
incsub( $1, $2, $subconf, $parser );
|
|
||||||
} else {
|
|
||||||
# normal line, send it to the callback function
|
|
||||||
$parser->($line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sub parse {
|
sub parse {
|
||||||
trace( 4, @_ );
|
my $lines = shift;
|
||||||
my $line = shift;
|
trace(4, scalar(@$lines) . " lines incoming");
|
||||||
|
|
||||||
|
for my $line (@$lines) {
|
||||||
# user or repo groups
|
# user or repo groups
|
||||||
if ( $line =~ /^(@\S+) = (.*)/ ) {
|
if ( $line =~ /^(@\S+) = (.*)/ ) {
|
||||||
add_to_group( $1, split( ' ', $2 ) );
|
add_to_group( $1, split( ' ', $2 ) );
|
||||||
|
@ -111,73 +74,6 @@ sub parse {
|
||||||
_warn "?? $line";
|
_warn "?? $line";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# ----------------------------------------------------------------------
|
|
||||||
|
|
||||||
sub incsub {
|
|
||||||
my $is_subconf = ( +shift eq 'subconf' );
|
|
||||||
my ( $include_glob, $subconf, $parser ) = @_;
|
|
||||||
|
|
||||||
_die "subconf $subconf attempting to run 'subconf'\n" if $is_subconf and $subconf ne 'master';
|
|
||||||
|
|
||||||
# XXX move this to Macros... substitute HOSTNAME word if GL_HOSTNAME defined, otherwise leave as is
|
|
||||||
# $include_glob =~ s/\bHOSTNAME\b/$GL_HOSTNAME/ if $GL_HOSTNAME;
|
|
||||||
|
|
||||||
# XXX g2 diff: include glob is *implicitly* from $rc{GL_ADMIN_BASE}/conf, not *explicitly*
|
|
||||||
# for my $file (glob($include_glob =~ m(^/) ? $include_glob : "$rc{GL_ADMIN_BASE}/conf/$include_glob")) {
|
|
||||||
|
|
||||||
trace( 3, $is_subconf, $include_glob );
|
|
||||||
|
|
||||||
for my $file ( glob($include_glob) ) {
|
|
||||||
_warn("included file not found: '$file'"), next unless -f $file;
|
|
||||||
_die "invalid include/subconf filename $file" unless $file =~ m(([^/]+).conf$);
|
|
||||||
my $basename = $1;
|
|
||||||
|
|
||||||
next if already_included($file);
|
|
||||||
|
|
||||||
if ($is_subconf) {
|
|
||||||
$parser->("subconf $basename");
|
|
||||||
explode( $file, $basename, $parser );
|
|
||||||
$parser->("subconf $subconf");
|
|
||||||
# XXX g2 delegaton compat: deal with this: $subconf_seen++;
|
|
||||||
} else {
|
|
||||||
explode( $file, $subconf, $parser );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sub prefix_groupnames {
|
|
||||||
my ( $line, $subconf ) = @_;
|
|
||||||
|
|
||||||
my $lhs = '';
|
|
||||||
# save 'foo' if it's an '@foo = list' line
|
|
||||||
$lhs = $1 if $line =~ /^@(\S+) = /;
|
|
||||||
# prefix all @groups in the line
|
|
||||||
$line =~ s/(^| )(@\S+)(?= |$)/ $1 . ($prefixed_groupname{$subconf}{$2} || $2) /ge;
|
|
||||||
# now prefix the LHS and store it if needed
|
|
||||||
if ($lhs) {
|
|
||||||
$line =~ s/^@\S+ = /"\@$subconf.$lhs = "/e;
|
|
||||||
trace( 3, "prefixed_groupname.$subconf.\@$lhs = \@$subconf.$lhs" );
|
|
||||||
}
|
|
||||||
|
|
||||||
return $line;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub already_included {
|
|
||||||
my $file = shift;
|
|
||||||
|
|
||||||
my $file_id = device_inode($file);
|
|
||||||
return 0 unless $included{$file_id}++;
|
|
||||||
|
|
||||||
_warn("$file already included");
|
|
||||||
trace( 3, "$file already included" );
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub device_inode {
|
|
||||||
my $file = shift;
|
|
||||||
trace( 3, $file, ( stat $file )[ 0, 1 ] );
|
|
||||||
return join( "/", ( stat $file )[ 0, 1 ] );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
121
Gitolite/Conf/Explode.pm
Normal file
121
Gitolite/Conf/Explode.pm
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
package Gitolite::Conf::Explode;
|
||||||
|
|
||||||
|
# include/subconf processor
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
@EXPORT = qw(
|
||||||
|
explode
|
||||||
|
);
|
||||||
|
|
||||||
|
use Exporter 'import';
|
||||||
|
|
||||||
|
use lib $ENV{GL_BINDIR};
|
||||||
|
use Gitolite::Common;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
# 'seen' for include/subconf files
|
||||||
|
my %included = ();
|
||||||
|
# 'seen' for group names on LHS
|
||||||
|
my %prefixed_groupname = ();
|
||||||
|
|
||||||
|
sub explode {
|
||||||
|
trace( 4, @_ );
|
||||||
|
my ( $file, $subconf, $out ) = @_;
|
||||||
|
|
||||||
|
# seed the 'seen' list if it's empty
|
||||||
|
$included{ device_inode("conf/gitolite.conf") }++ unless %included;
|
||||||
|
|
||||||
|
my $fh = _open( "<", $file );
|
||||||
|
while (<$fh>) {
|
||||||
|
my $line = cleanup_conf_line($_);
|
||||||
|
next unless $line =~ /\S/;
|
||||||
|
|
||||||
|
$line = prefix_groupnames( $line, $subconf ) if $subconf ne 'master';
|
||||||
|
|
||||||
|
if ( $line =~ /^(include|subconf) (\S.+)$/ ) {
|
||||||
|
incsub( $1, $2, $subconf, $out );
|
||||||
|
} else {
|
||||||
|
# normal line, send it to the callback function
|
||||||
|
push @{$out}, $line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub incsub {
|
||||||
|
my $is_subconf = ( +shift eq 'subconf' );
|
||||||
|
my ( $include_glob, $subconf, $out ) = @_;
|
||||||
|
|
||||||
|
_die "subconf $subconf attempting to run 'subconf'\n" if $is_subconf and $subconf ne 'master';
|
||||||
|
|
||||||
|
_die "invalid include/subconf file/glob '$include_glob'"
|
||||||
|
unless $include_glob =~ /^"(.+)"$/
|
||||||
|
or $include_glob =~ /^'(.+)'$/;
|
||||||
|
$include_glob = $1;
|
||||||
|
|
||||||
|
# XXX move this to Macros... substitute HOSTNAME word if GL_HOSTNAME defined, otherwise leave as is
|
||||||
|
# $include_glob =~ s/\bHOSTNAME\b/$GL_HOSTNAME/ if $GL_HOSTNAME;
|
||||||
|
|
||||||
|
# XXX g2 diff: include glob is *implicitly* from $rc{GL_ADMIN_BASE}/conf, not *explicitly*
|
||||||
|
# for my $file (glob($include_glob =~ m(^/) ? $include_glob : "$rc{GL_ADMIN_BASE}/conf/$include_glob")) {
|
||||||
|
|
||||||
|
trace( 3, $is_subconf, $include_glob );
|
||||||
|
|
||||||
|
for my $file ( glob($include_glob) ) {
|
||||||
|
_warn("included file not found: '$file'"), next unless -f $file;
|
||||||
|
_die "invalid include/subconf filename $file" unless $file =~ m(([^/]+).conf$);
|
||||||
|
my $basename = $1;
|
||||||
|
|
||||||
|
next if already_included($file);
|
||||||
|
|
||||||
|
if ($is_subconf) {
|
||||||
|
push @{$out}, "subconf $basename";
|
||||||
|
explode( $file, $basename, $out );
|
||||||
|
push @{$out}, "subconf $subconf";
|
||||||
|
# XXX g2 delegaton compat: deal with this: $subconf_seen++;
|
||||||
|
} else {
|
||||||
|
explode( $file, $subconf, $out );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub prefix_groupnames {
|
||||||
|
my ( $line, $subconf ) = @_;
|
||||||
|
|
||||||
|
my $lhs = '';
|
||||||
|
# save 'foo' if it's an '@foo = list' line
|
||||||
|
$lhs = $1 if $line =~ /^@(\S+) = /;
|
||||||
|
# prefix all @groups in the line
|
||||||
|
$line =~ s/(^| )(@\S+)(?= |$)/ $1 . ($prefixed_groupname{$subconf}{$2} || $2) /ge;
|
||||||
|
# now prefix the LHS and store it if needed
|
||||||
|
if ($lhs) {
|
||||||
|
$line =~ s/^@\S+ = /"\@$subconf.$lhs = "/e;
|
||||||
|
$prefixed_groupname{$subconf}{"\@$lhs"} = "\@$subconf.$lhs";
|
||||||
|
trace( 3, "prefixed_groupname.$subconf.\@$lhs = \@$subconf.$lhs" );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $line;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub already_included {
|
||||||
|
my $file = shift;
|
||||||
|
|
||||||
|
my $file_id = device_inode($file);
|
||||||
|
return 0 unless $included{$file_id}++;
|
||||||
|
|
||||||
|
_warn("$file already included");
|
||||||
|
trace( 3, "$file already included" );
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub device_inode {
|
||||||
|
my $file = shift;
|
||||||
|
trace( 3, $file, ( stat $file )[ 0, 1 ] );
|
||||||
|
return join( "/", ( stat $file )[ 0, 1 ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
||||||
|
|
|
@ -4,78 +4,97 @@ package Gitolite::Conf::Sugar;
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
@EXPORT = qw(
|
@EXPORT = qw(
|
||||||
macro_expand
|
sugar
|
||||||
cleanup_conf_line
|
|
||||||
);
|
);
|
||||||
|
|
||||||
use Exporter 'import';
|
use Exporter 'import';
|
||||||
|
|
||||||
use lib $ENV{GL_BINDIR};
|
use lib $ENV{GL_BINDIR};
|
||||||
use Gitolite::Common;
|
|
||||||
use Gitolite::Rc;
|
use Gitolite::Rc;
|
||||||
|
use Gitolite::Common;
|
||||||
|
use Gitolite::Conf::Explode;
|
||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
sub macro_expand {
|
sub sugar {
|
||||||
# site-local macros, if any, then gitolite internal macros, to munge the
|
# gets a filename, returns a listref
|
||||||
# input conf line if needed
|
|
||||||
|
|
||||||
my @lines = @_;
|
my @lines = ();
|
||||||
|
explode(shift, 'master', \@lines);
|
||||||
|
|
||||||
# TODO: user macros, how to allow the user to specify them?
|
my $lines;
|
||||||
|
$lines = \@lines;
|
||||||
|
|
||||||
# cheat, to keep *our* regexes simple :)
|
# run through the sugar stack one by one
|
||||||
# XXX but this also kills the special '# BEGIN filename' and '# END
|
|
||||||
# filename' lines that explode() surrounds the actual data with when it
|
|
||||||
# called macro_expand(). Right now we don't need it, but...
|
|
||||||
@lines = grep /\S/, map { cleanup_conf_line($_) } @lines;
|
|
||||||
|
|
||||||
@lines = owner_desc(@lines);
|
# first, user supplied sugar:
|
||||||
|
if (exists $rc{SYNTACTIC_SUGAR}) {
|
||||||
return @lines;
|
if (ref($rc{SYNTACTIC_SUGAR}) ne 'ARRAY') {
|
||||||
|
_warn "bad syntax for specifying sugar scripts; see docs";
|
||||||
|
} else {
|
||||||
|
for my $s (@{ $rc{SYNTACTIC_SUGAR} }) {
|
||||||
|
_warn "ignoring unreadable sugar script $s" if not -r $s;
|
||||||
|
do $s if -r $s;
|
||||||
|
$lines = sugar_script($lines);
|
||||||
|
$lines = [ grep /\S/, map { cleanup_conf_line($_) } @$lines ];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub cleanup_conf_line {
|
# then our stuff:
|
||||||
my $line = shift;
|
|
||||||
|
|
||||||
# kill comments, but take care of "#" inside *simple* strings
|
$lines = owner_desc($lines);
|
||||||
$line =~ s/^((".*?"|[^#"])*)#.*/$1/;
|
# $lines = name_vref($lines);
|
||||||
# normalise whitespace; keeps later regexes very simple
|
|
||||||
$line =~ s/=/ = /;
|
return $lines;
|
||||||
$line =~ s/\s+/ /g;
|
|
||||||
$line =~ s/^ //;
|
|
||||||
$line =~ s/ $//;
|
|
||||||
return $line;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub owner_desc {
|
sub owner_desc {
|
||||||
my @lines = @_;
|
my $lines = shift;
|
||||||
my @ret;
|
my @ret;
|
||||||
|
|
||||||
for my $line (@lines) {
|
# XXX compat breakage: (1) adding repo/owner does not automatically add an
|
||||||
# reponame = "some description string"
|
# entry to projects.list -- we need a post-procesor for that, and (2)
|
||||||
# reponame "owner name" = "some description string"
|
# removing the 'repo' line no longer suffices to remove the config entry
|
||||||
|
# from projects.list. Maybe the post-procesor should do that as well?
|
||||||
|
|
||||||
|
# owner = "owner name"
|
||||||
|
# -> config gitweb.owner = owner name
|
||||||
|
# description = "some long description"
|
||||||
|
# -> config gitweb.description = some long description
|
||||||
|
# category = "whatever..."
|
||||||
|
# -> config gitweb.category = whatever...
|
||||||
|
|
||||||
|
# older formats:
|
||||||
|
# repo = "some long description"
|
||||||
|
# repo = "owner name" = "some long description"
|
||||||
|
# -> config gitweb.owner = owner name
|
||||||
|
# -> config gitweb.description = some long description
|
||||||
|
|
||||||
|
for my $line (@$lines) {
|
||||||
if ( $line =~ /^(\S+)(?: "(.*?)")? = "(.*)"$/ ) {
|
if ( $line =~ /^(\S+)(?: "(.*?)")? = "(.*)"$/ ) {
|
||||||
my ( $repo, $owner, $desc ) = ( $1, $2, $3 );
|
my ( $repo, $owner, $desc ) = ( $1, $2, $3 );
|
||||||
# XXX these two checks should go into add_config
|
# XXX these two checks should go into add_config
|
||||||
# _die "bad repo name '$repo'" unless $repo =~ $REPONAME_PATT;
|
# _die "bad repo name '$repo'" unless $repo =~ $REPONAME_PATT;
|
||||||
# _die "$fragment attempting to set description for $repo"
|
# _die "$fragment attempting to set description for $repo"
|
||||||
# if check_fragment_repo_disallowed( $fragment, $repo );
|
# if check_fragment_repo_disallowed( $fragment, $repo );
|
||||||
push @ret, "config gitolite-options.repo-desc = $desc";
|
push @ret, "repo $repo";
|
||||||
push @ret, "config gitolite-options.repo-owner = $owner" if $owner;
|
push @ret, "config gitweb.description = $desc";
|
||||||
|
push @ret, "config gitweb.owner = $owner" if $owner;
|
||||||
} elsif ( $line =~ /^desc = (\S.*)/ ) {
|
} elsif ( $line =~ /^desc = (\S.*)/ ) {
|
||||||
push @ret, "config gitolite-options.repo-desc = $1";
|
push @ret, "config gitweb.description = $1";
|
||||||
} elsif ( $line =~ /^owner = (\S.*)/ ) {
|
} elsif ( $line =~ /^owner = (\S.*)/ ) {
|
||||||
my ( $repo, $owner, $desc ) = ( $1, $2, $3 );
|
push @ret, "config gitweb.owner = $1";
|
||||||
push @ret, "config gitolite-options.repo-owner = $1";
|
} elsif ( $line =~ /^category = (\S.*)/ ) {
|
||||||
|
push @ret, "config gitweb.category = $1";
|
||||||
} else {
|
} else {
|
||||||
push @ret, $line;
|
push @ret, $line;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return @ret;
|
return \@ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
Loading…
Reference in a new issue