#!/usr/local/bin/perl # # Original version by Sam Ruby, written in Python. # Ported to Perl, and enhanced by Jim Jagielski # Enhanced to have links to personal pages by Sam Ruby. # Start making it aware of signed CLAs # # Usage: makestats.pl [-c] # creates the projects.html (or committers.html, if the -c # command line arg is used) which lists the ASF CVS project # modules and the committers of those modules. If they are # also ASF members, their CVS/SVN id and name will be in bold. # This must be run on cvs.apache.org, to read the correct # avail and passwd files. Also, the iclas.txt file # (from the foundation svn tree) should be located # in the same dir as this script. # use strict; # The orig version used Python's 'in' method to avoid # duplicate entries in the arrays. Perl lacks such a # thingie so we use a function. Needless to say, this # is ugly and slow, and a much better way would be to # also store the added entries in a hash and then # check for the existance of that key before adding... # maybe later. sub isin { my $val = shift(@_); my $foo; foreach $foo (@_) { if ($val eq $foo) { return 1; } } return 0; } # "globals" my ($iam, $title, $timestamp); # temp vars my ($line, @data, @modules, @dummy, @clasnocommit); # autocreated hashs my (%fullname, %members, %clas, %website, %module, %tally); # data construction my ($id, $name, $row, $rows, $col, $cols); # printvars my ($header, $listocvs, $cvss, $a1, $a2, $b1, $b2); if ($ARGV[0] eq "-c") { $iam = "committers"; $title = "Apache Committers"; } else { $iam = "projects"; $title = "Apache Committers by Project Modules"; } $timestamp = `date`; open(STDOUT, ">${iam}.html") || die "Cannot open ${iam}.html\n"; # populate fullname hash from passwd file open(PASSWD, '/etc/passwd') || die "Cannot open passwd file\n"; while ($line = ) { chomp $line; @data = split(':', $line); $fullname{$data[0]} = (split(',', $data[4]))[0]; } close PASSWD; # Now populate the members array open(MEMBERS, '/etc/group') || die "Cannot open /etc/group file\n"; while ($line = ) { next unless $line =~ s/^member:\*:\d+://; chomp $line; %members = map {$_, $_} split(/,/, $line); } close MEMBERS; # Now populate the clas array # simple for now... just store the avail IDs # and prefer using their "public" name as their # fullname open(CLAS, './iclas.txt') || die "Cannot open iclas.txt file\n"; while ($line = ) { chomp $line; @data = split(':', $line); $clas{$data[0]} = "yes"; if ($data[0] ne 'notinavail') { $fullname{$data[0]} = $data[2]; } else { push (@clasnocommit, $data[2]); } } close CLAS; # A fairly liberal regular expression that matches non-relative URLs my $urlre='[a-zA-Z][\w+\-.]*://[\w;/?:@&=+\$.\-_!~*\'()%,#]+'; # retrieve various lists `rm -rf wget-workarea`; `/usr/local/bin/wget -q -Pwget-workarea http://www.apache.org/foundation/members.html`; `/usr/local/bin/wget -q -Pwget-workarea http://jakarta.apache.org/site/whoweare.html`; `/usr/local/bin/wget -q -Pwget-workarea http://httpd.apache.org/contributors/`; my $page; # Consume Jakarta open(JAKARTA, 'wget-workarea/whoweare.html'); $page = join('',); $page =~ s/\s+/ /g; while ($page =~ m{ ?([\w ]+) ?}msg) { $website{$2}=$1; } close JAKARTA; # Consume httpd open(HTTPD, 'wget-workarea/index.html'); my $name = undef; while () { ($id,$name)=($1,$2) if /Name:.*(.*?)<\/a>/; if (/URL:/) { $website{$id}=$website{$name}=$1 if $name and m{href="($urlre)"}; $name = undef; } } close HTTPD; # Consume members open(MEMBERS, 'wget-workarea/members.html'); $page = join('',); $page =~ s/\s+/ /g; while ($page =~ m{ ?([\w ]+) ?}msg) { $website{$2}=$1; } close MEMBERS; # populate module hash from avail file # tally stores the CVS modules the user is a member of if (0) { open(AVAIL, '/home/cvs/CVSROOT/avail') || die "Cannot open avail file\n"; while ($line = ) { chomp $line; @data = split('\|', $line); if ($data[0] eq 'avail') { foreach $name (split(',', $data[2])) { next unless -d "/home/cvspublic/$name"; foreach $id (split(',', $data[1])) { if ($iam eq 'projects') { if (!isin($id, @{$module{$name}})) { push (@{$module{$name}}, $id); } } else { if (!isin($id, @{$module{"cvs/svn-committers"}})) { push (@{$module{"cvs/svn-committers"}}, $id); } } if (!isin($name, @{$tally{$id}})) { push (@{$tally{$id}}, $name); } } } } } } # Be SVN aware my $section; open(SVNAUTH, './asf-authorization') || die "Cannot open svnauth file\n"; while () { $section=$1 if /\[(.*?)\]/; if ($section eq "groups" and /=/) { my ($name,$ids) = /(.*?)=(.*)/; next if ($name =~ /committers-./); foreach $id (split(',', $ids)) { if ($iam eq 'projects') { if (!isin($id, @{$module{$name}})) { push (@{$module{$name}}, $id); } } else { if (!isin($id, @{$module{"cvs/svn-committers"}})) { push (@{$module{"cvs/svn-committers"}}, $id); } } if (!isin($name, @{$tally{$id}})) { push (@{$tally{$id}}, $name); } } } } # Start of the HTML file. The LEFT SIDE NAVIGATION shamelessly # stolen from the (old) main www.apache.org static pages. print < $title
The Apache Software Foundation

$title


Apache Projects

  • HTTP Server
  • APR
  • Jakarta
  • Perl
  • TCL
  • XML
  • Conferences
  • Foundation
  • Foundation

  • FAQ
  • Management
  • News & Status
  • Press Kit
  • Contact
  • Get Involved

  • Contributing
  • Mailing Lists
  • CVS Repositories
  • Download

  • from a mirror
  • from here
  • Sister Projects

  • Module Registry
  • Apache-SSL
  • mod_ssl
  • Project Modules

    EOF # produce an index; @modules = sort keys %module; # create at most a "table" with 5 columns... May need to reduce that # (making it longer) to avoid VERY wide pages. For now, we just # reduce the font size one more tic $cols = 5; $rows = (scalar(@modules) + $cols - 1) / $cols; print < EOF foreach $row (0..$rows) { print " \n"; foreach $col (0..$cols) { $name = $modules[$row+$col*$rows]; if (defined($name)) { print " \n"; } } print " \n"; } if ($iam ne 'projects') { print " \n"; } print < EOF # The committers page includes an extra table cell, which lists the cvs # modules they are committers of if ($iam eq "projects") { $header = ""; $listocvs = ""; } else { $header = ""; } # produce a sorted list of committers to each project print "
    \n"; foreach $name (@modules) { print <$name
    $name
    Unlisted CLAs
    CVS/SVN idName
    CVS/SVN idNameCVS/SVN Projects
    $header EOF @dummy = sort @{$module{$name}}; foreach $id (@dummy) { if (exists($members{$id})) { $b1=""; $b2=""; } else { $b1=$b2=""; } if (!exists($clas{$id})) { $b1="$b1"; $b2="$b2"; } if (exists($website{$id})) { $a1=""; $a2=""; } elsif (exists($website{$fullname{$id}})) { $a1=""; $a2=""; } else { $a1=$a2=""; } if ($iam ne "projects") { $listocvs = ""; foreach $cvss (sort @{$tally{$id}}) { $listocvs .= " $cvss,"; } chop $listocvs; $listocvs = ""; } print < $listocvs EOF } print "
    $listocvs${b1}${a1}${id}${a2}${b2} ${b1}${fullname{$id}}${b2}
    \n"; } # Now list those with CLAs on file, but who are not # committers (ie: lack commit privs). Only do this # for the committer page if ($iam ne "projects") { print <Unlisted CLAs EOF while (@clasnocommit > 0) { print < EOF splice(@clasnocommit, 0, 4); } print "
    Persons with signed CLAs but are not committers:
    $clasnocommit[0] $clasnocommit[1] $clasnocommit[2] $clasnocommit[3]
    \n"; } # closing print <
    Last updated: $timestamp
    Entries in italics do NOT have a signed Contributor License Agreement on file (this knowledge is keyed by CVS/SVN id)
    Entries in bold are ASF members.
    EOF exit;