]> Sergey Matveev's repositories - public-inbox.git/blobdiff - lib/PublicInbox/Config.pm
config: support cgit scan-path and scan-hidden-path
[public-inbox.git] / lib / PublicInbox / Config.pm
index da443e580eb3a72692d10541690140b53b23314c..9f1e57ac7554dc58764b3bb4cf3504a6803b0adc 100644 (file)
@@ -30,6 +30,7 @@ sub new {
        $self->{-no_obfuscate} ||= {};
        $self->{-limiters} ||= {};
        $self->{-code_repos} ||= {}; # nick => PublicInbox::Git object
+       $self->{-cgitrc_unparsed} = $self->{'publicinbox.cgitrc'};
 
        if (my $no = delete $self->{'publicinbox.noobfuscate'}) {
                $no = _array($no);
@@ -131,8 +132,10 @@ sub limiter {
        my ($self, $name) = @_;
        $self->{-limiters}->{$name} ||= do {
                require PublicInbox::Qspawn;
-               my $max = $self->{"publicinboxlimiter.$name.max"};
-               PublicInbox::Qspawn::Limiter->new($max);
+               my $max = $self->{"publicinboxlimiter.$name.max"} || 1;
+               my $limiter = PublicInbox::Qspawn::Limiter->new($max);
+               $limiter->setup_rlimit($name, $self);
+               $limiter;
        };
 }
 
@@ -147,6 +150,7 @@ sub default_file {
 sub git_config_dump {
        my ($file) = @_;
        my (%section_seen, @section_order);
+       return {} unless -e $file;
        my @cmd = (qw/git config/, "--file=$file", '-l');
        my $cmd = join(' ', @cmd);
        my $fh = popen_rd(\@cmd) or die "popen_rd failed for $file: $!\n";
@@ -196,15 +200,87 @@ sub valid_inbox_name ($) {
        1;
 }
 
+sub cgit_repo_merge ($$) {
+       my ($self, $repo) = @_;
+       # $repo = { url => 'foo.git', path => '/path/to/foo.git' }
+       my $nick = $repo->{url};
+       $self->{"coderepo.$nick.dir"} ||= $repo->{path};
+       $self->{"coderepo.$nick.cgiturl"} ||= $nick;
+}
+
+sub is_git_dir ($) {
+       my ($git_dir) = @_;
+       -d "$git_dir/objects" && -f "$git_dir/HEAD";
+}
+
+sub scan_path_coderepo {
+       my ($self, $base, $path) = @_;
+       opendir my $dh, $path or return;
+       while (defined(my $dn = readdir $dh)) {
+               next if $dn eq '.' || $dn eq '..';
+               if (index($dn, '.') == 0 && !$self->{-cgit_scan_hidden_path}) {
+                       next;
+               }
+               my $nick = $base eq '' ? $dn : "$base/$dn";
+               my $git_dir = "$path/$dn";
+               if (is_git_dir($git_dir)) {
+                       my $repo = { url => $nick, path => $git_dir };
+                       cgit_repo_merge($self, $repo);
+               } elsif (-d $git_dir) {
+                       scan_path_coderepo($self, $nick, $git_dir);
+               }
+       }
+}
+
+sub parse_cgitrc {
+       my ($self, $cgitrc, $nesting) = @_;
+
+       # same limit as cgit/configfile.c::parse_configfile
+       return if $nesting > 8;
+
+       open my $fh, '<', $cgitrc or do {
+               warn "failed to open cgitrc=$cgitrc: $!\n";
+               return;
+       };
+
+       # FIXME: this doesn't support macro expansion via $VARS, yet
+       my $repo;
+       foreach (<$fh>) {
+               chomp;
+               if (m!\Arepo\.url=(.+?)/*\z!) {
+                       my $nick = $1;
+                       cgit_repo_merge($self, $repo) if $repo;
+                       $repo = { url => $nick };
+               } elsif (m!\Arepo\.path=(.+)\z!) {
+                       if (defined $repo) {
+                               $repo->{path} = $1;
+                       } else {
+                               warn "$_ without repo.url\n";
+                       }
+               } elsif (m!\Ainclude=(.+)\z!) {
+                       parse_cgitrc($self, $1, $nesting + 1);
+               } elsif (m!\Ascan-hidden-path=(\d+)\z!) {
+                       $self->{-cgit_scan_hidden_path} = $1;
+               } elsif (m!\Ascan-path=(.+)\z!) {
+                       scan_path_coderepo($self, '', $1);
+               }
+       }
+       cgit_repo_merge($self, $repo) if $repo;
+}
+
 # parse a code repo
 # Only git is supported at the moment, but SVN and Hg are possibilities
 sub _fill_code_repo {
        my ($self, $nick) = @_;
        my $pfx = "coderepo.$nick";
 
+       # TODO: support gitweb and other repository viewers?
+       if (defined(my $cgitrc = delete $self->{-cgitrc_unparsed})) {
+               parse_cgitrc($self, $cgitrc, 0);
+       }
        my $dir = $self->{"$pfx.dir"}; # aka "GIT_DIR"
        unless (defined $dir) {
-               warn "$pfx.repodir unset";
+               warn "$pfx.dir unset";
                return;
        }
 
@@ -225,8 +301,6 @@ sub _fill_code_repo {
                        } @$cgits;
                }
        }
-       # TODO: support gitweb and other repository viewers?
-       # TODO: parse cgitrc
 
        $git;
 }
@@ -291,7 +365,11 @@ sub _fill {
                my $code_repos = $self->{-code_repos};
                my $repo_objs = $rv->{-repo_objs} = [];
                foreach my $nick (@$ibx_code_repos) {
-                       valid_inbox_name($nick) or next;
+                       my @parts = split(m!/!, $nick);
+                       my $valid = 0;
+                       $valid += valid_inbox_name($_) foreach (@parts);
+                       $valid == scalar(@parts) or next;
+
                        my $repo = $code_repos->{$nick} ||=
                                                _fill_code_repo($self, $nick);
                        push @$repo_objs, $repo if $repo;