#!/usr/local/bin/ruby # # 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 # Ported to Ruby # More awareness of personal pages/urls # # Usage: makestats.pl [-c] # creates the projects.html (or committers.html, if the -c # command line arg is used) which lists the ASF SVN project # modules and the committers of those modules. If they are # also ASF members, their SVN id and name will be in bold. # This must be run on people.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. # title = "ASF Committers" if (ARGV[0] == "-c") iam = "committers" else iam = "projects" title += " by Project Modules" end timestamp = `date` fout = File.new("#{iam}.html", "w") if fout.nil?: abort("Unable to open #{iam}.html"); end # populate fullname hash from passwd file fullname = {} fullname.default = "" File::open('/etc/passwd', "r") do |pwd| if pwd.nil?: abort("Unable to open /etc/passwd"); end pwd.grep(/.*:.*:.*.*:.*:.*:/) do |line| data = line.chomp.split(':') fullname[data[0]] = data[4].split(',')[0] # fout.print "#{fullname[data[0]]}\n" end end # Now populate the members array members = {} File::open('/etc/group', "r") do |member| if member.nil?: abort("Unable to open /etc/group"); end member.each do |line| next unless line.sub!(/^member:\*:\d+:/, '') line.chomp.split(',').each {|x| members[x] = x } end end # Now populate the clas array # simple for now... just store the avail IDs # and prefer using their "public" name as their # fullname clasnocommit = [] clas = {} File::open('./iclas.txt', "r") do |iclas| if iclas.nil?: abort("Unable to open iclas.txt file"); end iclas.each do |line| next if line =~ /^#/ data = line.chomp.split(':') next if data.nil? or data[0].nil? clas[data[0]] = "yes" if (data[0] != 'notinavail') fullname[data[0]] = data[2] else clasnocommit.push(data[2]) end end end # A fairly liberal regular expression that matches non-relative URLs 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/` # Consume Jakarta website = {} page = "" File::open('wget-workarea/whoweare.html', "r") do |jakarta| page = jakarta.readlines.join end page.gsub!(/\s+/,' ') page.scan(%r! ?([\w ]+) ?!) do |a| website[a[1]]=a[0] end # Consume httpd File::open('wget-workarea/index.html', "r") do |httpd| name = "" svnid = "" httpd.each do |line| ### Trad Ruby way: ## if (m = /Name:.*(.*?)<\/a>/.match(line)) ## svnid = m[1] ## name = m[2] ## end svnid, name = $1, $2 if line =~ /Name:.*(.*?)<\/a>/ if (line =~ /URL:/) if !name.empty? and (m = /href="(#{urlre})"/.match(line)) website[svnid] = website[name] = m[1] name = "" end end end end # Consume members page= "" File::open('wget-workarea/members.html', "r") do |fmembers| page = fmembers.readlines.join end page.gsub!(/\s+/,' ') page.scan(%r! ?([\w ]+) ?!) do |a| website[a[1]]=a[0] end # Consume urls File::open('./urls.txt', "r") do |urls| urls.each do |line| if line =~ /^(.*): *(http:.*)$/ website[$1] = $2 end end end # Be SVN aware cmodule = {} tally = {} section = "" File::open('./asf-authorization', "r") do |svnauth| if svnauth.nil?: abort("Unable to open svn auth file"); end svnauth.each do |line| ### Perlish way: ### section=$1 if line =~ /\[(.*?)\]/ if (m = /\[(.*?)\]/.match(line)) section = m[1] end next if line.empty? if (section == "groups" and line =~ /=/) svnmod,ids = $1,$2 if line =~ /(.*?)=(.*)/ # Handle the committers-# groups, which really aren't # svn "modules/projects" at all. If we are displaying # just projects, then skip 'em totally. Otherwise # at least note someone's existance in there without # actually naming it next if (svnmod =~ /committers-./) && (iam == 'projects') osvnmod = svnmod if (iam == 'committers') svnmod = "svn-committers" if (osvnmod =~ /committers-./) osvnmod = "" end end # if !cmodule.has_key?(svnmod): cmodule[svnmod] = []; end cmodule[svnmod] ||= [] ids.split(',').each do |svnid| if !cmodule[svnmod].include?(svnid) cmodule[svnmod] << svnid end tally[svnid] ||= [] if !osvnmod.empty? && !tally[svnid].include?(svnid) tally[svnid] << osvnmod end end end end end # Start of the HTML file. The LEFT SIDE NAVIGATION shamelessly # stolen from the (old) main www.apache.org static pages. fout.print < #{title}
The Apache Software Foundation

#{title}


Project Modules

EOF # produce an index; modules = cmodule.keys.sort # 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 = (modules.size + cols - 1) / cols fout.print < EOF (0 ... rows).each do |row| fout.print " \n" (0 ... cols).each do |col| name = modules[row+col*rows] if !name.nil? fout.print " \n" else fout.print " \n" end end fout.print " \n" end if (iam == 'committers') fout.print " \n" end fout.print < EOF # The committers page includes an extra table cell, which lists the svn # modules they are committers of ### here ### if (iam == "projects") header = "" else header = "" end # produce a sorted list of committers to each project fout.print "
\n" modules.each do |name| fout.print <#{name}
#{name} 
Unlisted CLAs    
SVN idName
SVN idNameSVN Projects
#{header} EOF cmodule[name].sort.each do |svnid| if (members.include?(svnid)) b1=""; b2="" else b1=b2="" end if (!clas.include?(svnid)) b1="#{b1}"; b2="#{b2}" end if (website.include?(svnid)) a1=""; a2="" elsif (website.include?(fullname[svnid])) a1=""; a2="" else a1=a2="" end listosvn = "" if (iam == "committers") tally[svnid].sort.each do |svnmod| listosvn += " #{svnmod}," end listosvn.chop! listosvn = "" end fout.print < #{listosvn} EOF end fout.print "
#{listosvn}#{b1}#{a1}#{svnid}#{a2}#{b2} #{b1}#{fullname[svnid]}#{b2}
\n" end # 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 == "committers") fout.print <Unlisted CLAs EOF while (clasnocommit.size > 0) do fout.print < EOF clasnocommit.slice!(0, 4) end fout.print "
Persons with signed CLAs but are not committers:
#{clasnocommit[0]} #{clasnocommit[1]} #{clasnocommit[2]} #{clasnocommit[3]}
\n" end # closing fout.print <

Last updated: #{timestamp}
Entries in italics do NOT have a signed Contributor License Agreement on file (this knowledge is keyed by SVN id)
Entries in bold are ASF members.
EOF