class ASF::Podling
Represents a podling, drawing information from both podlings.xml and LDAP
.
Constants
- NAME_FIXES
Override Proposed Names that are wrong
Attributes
userid of the champion, from podlings.xml
description of the podling, from podlings.xml
list of userids of the mentors, from podlings.xml
if reporting monthly, a list of months reports are expected. Can also ge nil
or an empty list. From podlings.xml.
name of the podling, from podlings.xml
<resolution link=“”>, from podlings.xml
<resolution tlp=“”>, from podlings.xml
<resolution url=“”>, from podlings.xml
name of the podling, from podlings.xml
array of aliases for the podling, from podlings.xml
status of the podling, from podlings.xml. Valid values are current
, graduated
, or retired
.
Public Class Methods
# File lib/whimsy/asf/podling.rb, line 493 def self._list(status) list.select { |podling| podling.status == status } end
# File lib/whimsy/asf/podling.rb, line 497 def self._listids(status) list.select { |podling| podling.status == status }.map(&:id) end
list of current podlings
# File lib/whimsy/asf/podling.rb, line 202 def self.current self._list('current') end
list of current podling ids
# File lib/whimsy/asf/podling.rb, line 207 def self.currentids self._listids('current') end
provide a list of podling names and descriptions
# File lib/whimsy/asf/podling.rb, line 265 def self.each(&block) list.each { |podling| block.call podling.name, podling } end
find a podling by name
# File lib/whimsy/asf/podling.rb, line 238 def self.find(name) name = name.downcase result = list.find do |podling| podling.name == name or podling.display_name.downcase == name or podling.resourceAliases.any? {|aname| aname.downcase == name} end result ||= list.find do |podling| podling.resource == name or podling.tlp_name.downcase == name end end
list of graduated podlings
# File lib/whimsy/asf/podling.rb, line 212 def self.graduated self._list('graduated') end
list of graduated podling ids
# File lib/whimsy/asf/podling.rb, line 217 def self.graduatedids self._listids('graduated') end
list of all podlings, regardless of status
# File lib/whimsy/asf/podling.rb, line 175 def self.list incubator_content = ASF::SVN['incubator-content'] podlings_xml = File.join(incubator_content, 'podlings.xml') # see if there is a later version cache = ASF::Config.get(:cache) if File.exist? File.join(cache, 'podlings.xml') if File.mtime(File.join(cache, 'podlings.xml')) > File.mtime(podlings_xml) podlings_xml = File.join(cache, 'podlings.xml') end end if @mtime != File.mtime(podlings_xml) @list = [] podlings = Nokogiri::XML(File.read(podlings_xml)) # check for errors as they adversely affect the generated output raise Exception.new(podlings.errors.inspect) if podlings.errors.size > 0 podlings.search('podling').map do |node| @list << new(node) end @mtime = File.mtime(podlings_xml) end @list end
last modified time of podlings.xml in the local working directory, as of the last time list was called.
# File lib/whimsy/asf/podling.rb, line 233 def self.mtime @mtime end
parse (and cache) names mentioned in podlingnamesearches Note: customfield_12310520 = ‘Podling’, customfield_12310521 = ‘Proposed Name’
# File lib/whimsy/asf/podling.rb, line 441 def self.namesearch # cache JIRA response cache = File.join(ASF::Config.get(:cache), 'pns.jira') if not File.exist?(cache) or File.mtime(cache) < Time.now - 300 query = 'https://issues.apache.org/jira/rest/api/2/search?' + 'maxResults=1000&' + 'jql=project=PODLINGNAMESEARCH&fields=summary,resolution,customfield_12310521' begin res = Net::HTTP.get_response(URI(query)) res.value() # Raises error if not OK file = File.new(cache, 'wb') # Allow for non-UTF-8 chars file.write res.body rescue StandardError => e Wunderbar.warn 'ASF::Podling.namesearch: ' + e.message FileUtils.touch cache # Don't try again for a while end end # parse JIRA titles for proposed name issues = JSON.parse(File.read(cache))['issues'].map do |issue| resolution = issue['fields']['resolution'] resolution = resolution ? resolution['name'] : 'Unresolved' # Ignore duplicates and abandoned entries etc. # PODLINGNAMESEARCH-9 is resolved as 'Not A Problem': this means it is cleared for use next unless %w{Fixed Unresolved Resolved Implemented}.include?(resolution) || issue['key'] == 'PODLINGNAMESEARCH-9' name = issue['fields']['customfield_12310521'] if name name.sub!(/^Apache\s+/, '') name.gsub!(/\s+\(.*?\)/, '') # Fix up incorrect 'Proposed Name' entries name = NAME_FIXES[name] || name name = nil if name =~ /^\s*This/ or name !~ /[A-Za-z]{3}/ or name =~ %r{^N/A} end next unless name [name, {issue: issue['key'], resolution: resolution}] end issues.compact.sort_by(&:first).to_h end
create a podling from a Nokogiri node built from podlings.xml
# File lib/whimsy/asf/podling.rb, line 57 def initialize(node) @mtime = nil @name = node['name'] @resource = node['resource'] # Validate resource for later use resource can contain '-' and '.' (lucene.net) raise ArgumentError, "Invalid resource #{@resource}" unless @resource =~ /\A[-.\w]+\z/ @sponsor = node['sponsor'] # Needed for matching against mailing list names @resourceAliases = [] @resourceAliases = node['resourceAliases'].split(/,\s*/) if node['resourceAliases'] @status = node['status'] @enddate = node['enddate'] @startdate = node['startdate'] @description = node.at('description').text @mentors = node.search('mentor').map { |mentor| mentor['username'] } @champion = node.at('champion')['availid'] if node.at('champion') @reporting = node.at('reporting') || nil # ensure variable is defined @monthly = @reporting&.text&.split(/,\s*/) res = node.at('resolution') if res @resolutionLink = res.attr('link') @resolutionURL = res.attr('url') @resolutionTLP = res.attr('tlp') else @resolutionLink = nil @resolutionURL = nil @resolutionTLP = nil end # Note: the following optional elements are not currently processed: # - resolution (except for resolution/@link) # - retiring/graduating # The following podling attributes are not processed: # - longname end
list of retired podlings
# File lib/whimsy/asf/podling.rb, line 222 def self.retired self._list('retired') end
list of retired podling ids
# File lib/whimsy/asf/podling.rb, line 227 def self.retiredids self._listids('retired') end
return the entire list as a hash
# File lib/whimsy/asf/podling.rb, line 260 def self.to_h Hash[self.to_a] end
Public Instance Methods
allow attributes to be accessed as hash
# File lib/whimsy/asf/podling.rb, line 270 def [](name) return self.send name if self.respond_to? name end
Match against new and old list types
# File lib/whimsy/asf/podling.rb, line 333 def _match_mailname?(list, name) return true if list.start_with?("#{name}-") return true if list.start_with?("incubator-#{name}-") end
Return the instance as a hash. Keys in the hash are: :name
, :status
, :description
, :mentors
, :startdate
, :champion
, :reporting
, :resource
, :resourceAliases
, :sponsor
, :duration
, and :podlingStatus
# File lib/whimsy/asf/podling.rb, line 374 def as_hash # might be confusing to use to_h here? hash = { name: @name, status: status, description: description, mentors: mentors, startdate: startdate, } hash[:enddate] = enddate if enddate hash[:champion] = champion if champion # Tidy up the reporting output podlingStatus = self.podlingStatus r = @reporting if r.instance_of? Nokogiri::XML::Element group = r['group'] hash[:reporting] = { group: group } hash[:reporting][:text] = r.text if r.text.length > 0 hash[:reporting][:monthly] = r.text.split(/,\s*/) if r['monthly'] hash[:reporting][:schedule] = self.schedule elsif r hash[:reporting] = r end hash[:resource] = resource hash[:resourceAliases] = resourceAliases hash[:namesearch] = namesearch if namesearch hash[:sponsor] = @sponsor if @sponsor hash[:duration] = self.duration hash[:podlingStatus] = podlingStatus hash[:resolutionLink] = resolutionLink if resolutionLink hash[:resolutionURL] = resolutionURL if resolutionURL hash[:resolutionTLP] = resolutionTLP if resolutionTLP hash end
# File lib/whimsy/asf/podling.rb, line 117 def current? @status == 'current' end
status information associated with this podling. Keys in the hash return include: :issueTracker
, :wiki
, :jira
, :proposal
, :asfCopyright, <tt>:distributionRights
, :ipClearance
, :sga
, :website
, :graduationDate
, :resolution
# File lib/whimsy/asf/podling.rb, line 417 def default_status { issueTracker: 'jira', wiki: self.resource.upcase, jira: self.resource.upcase, proposal: "http://wiki.apache.org/incubator/#{self.resource.capitalize}Proposal", asfCopyright: nil, distributionRights: nil, ipClearance: nil, sga: nil, website: "http://#{self.resource}.incubator.apache.org", graduationDate: nil, resolution: nil } end
development mailing list associated with a given podling
# File lib/whimsy/asf/podling.rb, line 299 def dev_mail_list case name when 'climatemodeldiagnosticanalyzer' 'dev@cmda.incubator.apache.org' when 'odftoolkit' 'odf-dev@incubator.apache.org' when 'log4cxx2' 'log4cxx-dev@logging.apache.org' else "dev@#{name}.apache.org" end end
display name for this podling, originally from the name attribute in podlings.xml.
# File lib/whimsy/asf/podling.rb, line 108 def display_name @name || @resource end
number of days in incubation
# File lib/whimsy/asf/podling.rb, line 151 def duration last = enddate || Date.today first = startdate || Date.today (last - first).to_i end
date this podling either retired or graduated. nil
for current podlings.
# File lib/whimsy/asf/podling.rb, line 141 def enddate return unless @enddate and @enddate.length >= 7 # assume 15th (mid-month) if no day specified return Date.parse("#{@enddate}-15") if @enddate.length == 7 Date.parse(@enddate) rescue ArgumentError nil end
# File lib/whimsy/asf/podling.rb, line 121 def graduated? @status == 'graduated' end
# File lib/whimsy/asf/podling.rb, line 284 def hasLDAP? ASF::Project.find(id).hasLDAP? end
also map resource to id
# File lib/whimsy/asf/podling.rb, line 102 def id @resource end
base name used in constructing mailing list name.
# File lib/whimsy/asf/podling.rb, line 289 def mail_list case name.downcase when 'odftoolkit' 'odf' else name.downcase end end
Is this a podling mailing list?
# File lib/whimsy/asf/podling.rb, line 323 def mail_list?(list) return true if _match_mailname?(list, name()) # Also check aliases @resourceAliases.each { |name| return true if _match_mailname?(list, name) } return false end
list of PPMC committers from LDAP
# File lib/whimsy/asf/podling.rb, line 280 def members ASF::Project.find(id).members end
name for this podling, originally from the resource attribute in podlings.xml.
# File lib/whimsy/asf/podling.rb, line 97 def name @resource end
return podlingnamesearch for this podling
# File lib/whimsy/asf/podling.rb, line 487 def namesearch Podling.namesearch[display_name] end
list of PPMC owners from LDAP
# File lib/whimsy/asf/podling.rb, line 275 def owners ASF::Project.find(id).owners end
status information associated with this podling. Keys in the hash return include: :ipClearance
, :sourceControl
, :wiki
, :jira
, :proposal
, :website
, :news
# File lib/whimsy/asf/podling.rb, line 341 def podlingStatus incubator_content = ASF::SVN['incubator-podlings'] resource_yml = File.join(incubator_content, "#{@resource}.yml") if File.exist?(resource_yml) rawYaml = Psych.load_file(resource_yml, permitted_classes: [Date, Symbol]) hash = { } hash[:sga] = rawYaml[:sga].strftime('%Y-%m-%d') if rawYaml[:sga]&.class == Date hash[:asfCopyright] = rawYaml[:asfCopyright].strftime('%Y-%m-%d') if rawYaml[:asfCopyright]&.class == Date hash[:distributionRights] = rawYaml[:distributionRights].strftime('%Y-%m-%d') if rawYaml[:distributionRights]&.class == Date hash[:ipClearance] = rawYaml[:ipClearance].strftime('%Y-%m-%d') if rawYaml[:ipClearance]&.class == Date hash[:sourceControl] = rawYaml[:sourceControl] hash[:wiki] = rawYaml[:wiki] hash[:jira] = rawYaml[:jira] hash[:proposal] = rawYaml[:proposal] hash[:website] = rawYaml[:website] hash[:news] = [] rawYaml[:news]&.each do |ni| newsItem = {} newsItem[:date] = ni[:date].strftime('%Y-%m-%d') if ni[:date]&.class == Date newsItem[:note] = ni[:note] hash[:news].push(newsItem) end hash else {news: [], website: "http://#{self.resource}.incubator.apache.org"} end end
private mailing list associated with a given podling
# File lib/whimsy/asf/podling.rb, line 313 def private_mail_list if name == 'log4cxx2' 'private@logging.apache.org' else list = dev_mail_list list ? list.sub('dev', 'private') : 'private@incubator.apache.org' end end
three consecutive months, starting with this one
# File lib/whimsy/asf/podling.rb, line 39 def quarter [ Date.today.strftime('%B'), Date.today.next_month.strftime('%B'), Date.today.next_month.next_month.strftime('%B') ] end
lazy evaluation of reporting
# File lib/whimsy/asf/podling.rb, line 158 def reporting if @reporting.instance_of? Nokogiri::XML::Element group = @reporting['group'] @reporting = %w(January April July October) if group == '1' @reporting = %w(February May August November) if group == '2' @reporting = %w(March June September December) if group == '3' end @reporting end
# File lib/whimsy/asf/podling.rb, line 125 def retired? @status == 'retired' end
provides a concatenated reporting schedule
# File lib/whimsy/asf/podling.rb, line 170 def schedule self.reporting + self.monthly end
date this podling was accepted for incubation
# File lib/whimsy/asf/podling.rb, line 130 def startdate return unless @startdate and @startdate.length >= 7 # "YYYY-MM" # assume 15th (mid-month) if no day specified return Date.parse("#{@startdate}-15") if @startdate.length == 7 Date.parse(@startdate) rescue ArgumentError nil end
TLP name (name differ from podling name)
# File lib/whimsy/asf/podling.rb, line 113 def tlp_name @resolutionLink || name end