class ASF::Committee
Representation for a committee (either a PMC, a board committee, or a President’s committee). This data is parsed from committee-info.txt|.yaml
, and is augmened by data from LDAP
, and ASF::Mail
.
Note that the simple attributes which are sourced from committee-info.txt
data is generally not available until ASF::Committee.load_committee_info
is called.
Similarly, the simple attributes which are sourced from LDAP
is generally not available until ASF::Project.preload
is called.
representation of Committee
, i.e. entry in committee-info.txt includes PMCs and other committees, but does not include podlings
Constants
- NAMEADDRLEN
- NAMELEN
-
Original field sizes for PMC section
Attributes
list of chairs for this committee. Returned as a list of hashes containing the :name
and :id
. Data is obtained from committee-info.txt
.
Date this committee was established in the format MM/YYYY. Data is obtained from committee-info.txt
.
list of members for this committee. Returned as a list of ids. Data is obtained from committee-info.txt
.
type of non-pmc entry (from its paragraph heading in committee-info.txt)
when this committee is next expected to report. May be a string containing values such as “Next month: missing in May”, “Next month: new, monthly through July”. Data is obtained from committee-info.txt
.
list of members for this committee. Returned as a list of hash mapping ids to a hash of :name
and :date
values. Data is obtained from committee-info.txt
.
list of months when this committee typically reports. Returned as a comma separated string. Data is obtained from committee-info.txt
.
Public Class Methods
Source
# File lib/whimsy/asf/ldap.rb, line 1390 def self.[](name) committee = super # Cannot rely on presence/absence of LDAP record as projects includes podlings (ASF::Committee.pmcs + ASF::Committee.nonpmcs).map(&:name).include?(name) ? committee : nil end
return committee only if it actually exists
ASF::Base::[]
Source
# File lib/whimsy/asf/committee.rb, line 778 def self.appendtlpmetadata(input, committee, description, date_established) YamlFile.replace_section(input, :tlps) do |section, yaml| output = section # default no change if yaml[:cttees][committee] && !yaml[:cttees][committee][:retired] Wunderbar.warn "Entry for '#{committee}' already exists under :cttees" elsif yaml[:tlps][committee] && !yaml[:tlps][committee][:retired] Wunderbar.warn "Entry for '#{committee}' already exists under :tlps" else if section[committee] # already exists; must be retired diary = section[committee][:diary] if !diary diary = section[committee][:diary] = [] diary << {established: section[committee][:established]} end diary << {retired: section[committee].delete(:retired)} diary << {resumed: date_established.strftime('%Y-%m')} else section[committee] = { site: "http://#{committee}.apache.org", description: description, established: date_established.strftime('%Y-%m'), } end output = section.sort.to_h end output end end
append the description for a new tlp committee. this is intended to be called from todos.json.rb in the block for ASF::SVN.update
Source
# File lib/whimsy/asf/committee.rb, line 410 def self.establish(contents, pmc, date, people) ######################################################################## # insert into assigned quarterly reporting periods # ######################################################################## # split into blocks blocks = contents.split("\n\n") # find the reporting schedules index = blocks.find_index {|section| section =~ /January/} # extract reporting schedules slots = [ blocks[index + 0].split("\n"), blocks[index + 1].split("\n"), blocks[index + 2].split("\n"), ] unless slots.any? {|slot| slot.include? ' ' + pmc} # ensure that spacing is uniform slots.each {|slot| slot.unshift '' unless slot[0] == ''} # determine tie breakers between months of the same length preference = [(date.month) % 3, (date.month - 1) % 3, (date.month - 2) % 3] # pick the month with the shortest list slot = (0..2).map {|i| [slots[i].length, preference, i]}.min.last # temporarily remove headers headers = slots[slot].shift(3) # insert pmc into the reporting schedule slots[slot] << ' ' + pmc # sort entries, case insensitive slots[slot].sort_by!(&:downcase) # restore headers slots[slot].unshift(*headers) # () are required here to prevent warning # re-insert reporting schedules blocks[index + 0] = slots[0].join("\n") blocks[index + 1] = slots[1].join("\n") blocks[index + 2] = slots[2].join("\n") # re-attach blocks contents = blocks.join("\n\n") end ######################################################################## # insert into COMMITTEE MEMBERSHIP AND CHANGE PROCESS # ######################################################################## # split into foot, sections (array) and head foot = contents[/^=+\s*\Z/] contents.sub! %r{^=+\s*\Z}, '' sections = contents.split(/^\* /) head = sections.shift # remove existing section (if present) sections.delete_if {|section| section.downcase.start_with? pmc.downcase} # build new section entries = people.map do |id, person| [person[:name], "<#{id}@apache.org>", "[#{date.strftime('%Y-%m-%d')}]"] end people = format_pmc(entries) section = ["#{pmc} (est. #{date.strftime('%m/%Y')})"] + people.sort # add new section sections << section.join("\n") + "\n\n\n" # sort sections sections.sort_by!(&:downcase) # re-attach parts head + '* ' + sections.join('* ') + foot end
insert (replacing if necessary) a new committee into committee-info.txt
Source
# File lib/whimsy/asf/committee.rb, line 665 def self.find(name, clear=false) raise ArgumentError.new('name: must not be nil') unless name namelc = @@namemap.call(name.downcase) collection[namelc] = nil if clear result = super(namelc) result.display_name = name if name =~ /[A-Z]/ result end
Finds a committee based on the name of the Committee
. Is aware of a number of aliases for a given committee. Will set display name if the name being searched on contains an uppercase character. If clear is true, then remove any cached entry
ASF::Base::find
Source
# File lib/whimsy/asf/committee.rb, line 496 def self.format_pmc(people, namelen=NAMELEN, nameaddrlen=NAMEADDRLEN) maillen = 0 people.each do |name, email, _datefield| namelen = [namelen, name.size].max maillen = [maillen, email.size].max end # +1 for space between fields nameaddrlen = [nameaddrlen, namelen + maillen + 1].max people.map do |name, email, datefield| nameaddr = "#{name.ljust(namelen)} #{email}" " #{(nameaddr).ljust(nameaddrlen)} #{datefield}" end end
format a PMC entry people: array of entries in the form [name, email, date+comment] namelen: default size to allow for name field nameaddrlen: default size to allow for name + email field fields will be separated by at least one space on output The defaults are taken from the originals to avoid needless change
Source
# File lib/whimsy/asf/committee.rb, line 163 def self.load_committee_info(contents = nil, info = nil) if contents if info @committee_mtime = @@svn_change = Time.parse(info[/Last Changed Date: (.*) \(/, 1]).gmtime else @committee_mtime = @@svn_change = Time.now end @nonpmcs, @officers, @committee_info = parse_committee_info_nocache(contents) else board = ASF::SVN.find('board') raise ArgumentError.new("Could not find 'board' checkout") unless board file = File.join(board, 'committee-info.txt') raise ArgumentError.new("Could not find #{file}") unless File.exist? file if @committee_mtime and File.mtime(file) <= @committee_mtime return @committee_info if @committee_info end @committee_mtime = File.mtime(file) @@svn_change = Time.parse(ASF::SVN.getInfoItem(file, 'last-changed-date')).gmtime Wunderbar.debug 'Parsing CI file' @nonpmcs, @officers, @committee_info = parse_committee_info_nocache(File.read(file)) end @committee_info end
load committee info from committee-info.txt
. Will not reparse if the file has already been parsed and the underlying file has not changed. the parameters are currently only used by www/board/agenda/routes.rb
Source
# File lib/whimsy/asf/committee.rb, line 741 def self.load_committee_metadata board = ASF::SVN.find('board') return unless board file = File.join(board, 'committee-info.yaml') return unless File.exist? file return @committee_metadata if @committee_metadata and @committee_metadata_mtime and File.mtime(file) <= @committee_metadata_mtime @committee_metadata_mtime = File.mtime(file) @committee_metadata = YAML.load_file file end
load committee metadata from committee-info.yaml
. Will not reparse if the file has already been parsed and the underlying file has not changed.
Source
# File lib/whimsy/asf/committee.rb, line 754 def self.meta_change @committee_metadata_mtime end
get the changed date for the meta data
Source
# File lib/whimsy/asf/committee.rb, line 759 def self.metadata(committee) committee = committee.name if committee.is_a? ASF::Committee load_committee_metadata[:tlps][committee] || load_committee_metadata[:cttees][committee] end
get the metadata for a given committee.
Source
# File lib/whimsy/asf/committee.rb, line 57 def initialize(*args) @info = [] @chairs = [] @roster = {} super end
create an empty committee instance
ASF::Base::new
Source
# File lib/whimsy/asf/committee.rb, line 641 def self.nonpmcs ASF::Committee.load_committee_info # ensure data exists @nonpmcs end
return a list of non-PMC committees. Data is obtained from committee-info.txt
Source
# File lib/whimsy/asf/committee.rb, line 656 def self.officer(role) office = self.officers.find {|officer| officer.name == role} office && ASF::Person.find(office.chairs.first[:id]) end
look up an individual officer
Source
# File lib/whimsy/asf/committee.rb, line 650 def self.officers ASF::Committee.load_committee_info # ensure data exists @officers end
return a list of officers. Data is obtained from committee-info.txt
. Note that these entries are returned as instances of ASF::Committee
with display_name
being the name of the office, and chairs being the individuals who hold that office.
Source
# File lib/whimsy/asf/committee.rb, line 516 def self.parse_committee_info_nocache(contents) # List uses full (display) names as keys, but the entries use the canonical names # - the local version of find() converts the name # - and stores the original as the display name if it has some upper case list = Hash.new {|hash, name| hash[name] = find(name, true)} # Split the file on lines starting "* ", i.e. the start of each group in section 3 info = contents.split(/^\* /) # Extract the text before first entry in section 3 and split on section headers, # keeping sections 1 (COMMITTEES) and 2 (REPORTING). head, report = info.shift.split(/^\d\./)[1..2] # Drop lines which could match group headers head.gsub! %r{^\s+NAME\s+CHAIR\s*$}, '' head.gsub! %r{^\s+Office\s+Officer\s*$}i, '' # extract the committee chairs (e-mail address is required here) # Note: this includes the non-PMC entries # Scan for entries even if there is a missing extra space before the chair column head.scan(/^[ \t]+\w.*?[ \t]+.*[ \t]+<.*?@apache\.org>/).each do |line| # Now weed out the malformed lines m = line.match(/^[ \t]+(\w.*?)[ \t][ \t]+(.*)[ \t]+<(.*?)@apache\.org>/) if m committee, name, id = m.captures # committee may not be canonical here unless list[committee].chairs.any? {|chair| chair[:id] == id} list[committee].chairs << {name: name, id: id} end else # not possible to determine where one name starts and the other begins Wunderbar.warn "Missing separator before chair name in: '#{line}'" end end # Any duplicates? dupes = list.group_by{|x| x.first.downcase}.select{|k,v|v.size!=1} if dupes.size > 0 Wunderbar.warn "Dulicate chairs: #{dupes}}" end # Extract the non-PMC committees (e-mail address may be absent) # first drop leading text (and Officers) so we only match non-PMCs nonpmcs = head.sub(/.*?also has /m, '').sub(/ Officers:.*/m, ''). scan(/^[ \t]+(\w.*?)(?:[ \t][ \t]|[ \t]?$)/).flatten.uniq. map {|name| list[name]} # Extract officers # first drop leading text so we only match officers at end of section officers = head.sub(/.*?also has .*? Officers/m, ''). scan(/^[ \t]+(\w.*?)(?:[ \t][ \t]|[ \t]?$)/).flatten.uniq. map {|name| list[name]} # store the paragraph identifiers: Board Committees etc head_parts = head.split(/^The ASF also has the following +/) (1..head_parts.size - 1).each do |h| # skip the first section part = head_parts[h] type = part[/^([^:]+)/, 1] # capture remains of line excluding colon part.scan(/^[ \t]+(\w.*?)(?:[ \t][ \t]|[ \t]?$)/).flatten.uniq.each do |cttee| list[cttee].paragraph = type end end # for each committee in section 3 info.each do |roster| # extract the committee name (and parenthesised comment if any) name = roster[/(\w.*?)[ \t]+\(est/, 1] unless list.include?(name) Wunderbar.warn "No chair entry detected for #{name} in section 3" end committee = list[name] # get and normalize the start date established = roster[/\(est\. (.*?)\)/, 1] established = "0#{established}" if established =~ /^\d\// committee.established = established # match non-empty entries and check the syntax roster.scan(/^[ \t]+.+$/) do |line| Wunderbar.warn "Invalid syntax: #{committee.name} '#{line}'" unless line =~ /\s<(.*?)@apache\.org>\s/ end # extract the availids (is this used?) committee.info = roster.scan(/<(.*?)@apache\.org>/).flatten # drop (chair) markers and extract 0: name, 1: availid, 2: [date], 3: date # the date is optional (e.g. infrastructure) committee.roster = Hash[roster.gsub(/\(\w+\)/, ''). scan(/^[ \t]*(.*?)[ \t]*<(.*?)@apache\.org>(?:[ \t]+(\[(.*?)\]))?/). map {|l| [l[1], {name: l[0], date: l[3]}]}] end # process report section report.scan(/^([^\n]+)\n---+\n(.*?)\n\n/m).each do |period, committees| committees.scan(/^ [ \t]*(.*)/).each do |committee| committee, comment = committee.first.split(/[ \t]+#[ \t]+/, 2) unless list.include? committee Wunderbar.warn "Unexpected name '#{committee}' in report section; ignored" next end committee = list[committee] if comment committee.report = "#{period}: #{comment}" elsif period == 'Next month' committee.report = 'Every month' else committee.schedule = period end end end committee_info = (list.values - officers).uniq # Check if there are duplicates. committee_info.each do |c| if c.chairs.length != 1 && c.name != 'fundraising' # hack to avoid reporting non-PMC entry Wunderbar.warn "Unexpected chair count for #{c.display_name}: #{c.chairs.inspect rescue ''}" end end return nonpmcs, officers, committee_info end
extract chairs, list of nonpmcs, roster, start date, and reporting information from committee-info.txt
. @return nonpmcs, officers, committees (including nonpmcs) This can safely be called with any input as it is idempotent For general use, use ASF::Committee.load_committee_info
which caches the data
Source
# File lib/whimsy/asf/committee.rb, line 634 def self.pmcs committees = ASF::Committee.load_committee_info committees - @nonpmcs - @officers end
return a list of PMC committees. Data is obtained from committee-info.txt
Source
# File lib/whimsy/asf/committee.rb, line 367 def self.record_termination(input, pmc, yyyymm) YamlFile.replace_section(input, :tlps) do |section, _yaml| key = ASF::Committee.to_canonical(pmc) if section[key] section[key][:retired] = yyyymm section[key][:name] = pmc else section[key] = {retired: yyyymm, name: pmc} end section.sort.to_h end end
record termination date in committee-info.yaml Params:
-
input: the contents of committee-info.yaml
-
pmc: the pmc name
-
yyyymm: YYYY-MM retirement date
Returns: the updated contents
Source
# File lib/whimsy/asf/committee.rb, line 677 def self.svn_change @@svn_change end
Return the Last Changed Date for committee-info.txt
in svn as a Time
object. Data is based on the previous call to ASF::Committee.load_committee_info
.
Source
# File lib/whimsy/asf/committee.rb, line 381 def self.terminate(contents, pmc) ######################################################################## # remove from assigned quarterly reporting periods # ######################################################################## # split into blocks blocks = contents.split("\n\n") # find the reporting schedules index = blocks.find_index {|section| section =~ /January/} # remove from each reporting period blocks[index + 0].sub! "\n #{pmc}\n", "\n" blocks[index + 1].sub! "\n #{pmc}\n", "\n" blocks[index + 2].sub! "\n #{pmc}\n", "\n" # re-attach blocks contents = blocks.join("\n\n") ######################################################################## # remove from COMMITTEE MEMBERSHIP AND CHANGE PROCESS # ######################################################################## contents.sub! %r{^\* #{Regexp.escape(pmc)} ?\(est.*?\n\n+}m, '' contents end
remove committee from committee-info.txt
Source
# File lib/whimsy/asf/committee.rb, line 105 def self.to_canonical(name) @@namemap.call(name.downcase) end
convert committee name to canonical name
Source
# File lib/whimsy/asf/committee.rb, line 265 def self.update_chairs(contents, todos) # extract committee section; and then extract the lines containing # committee names and chairs section = contents[/^1\..*?\n=+/m] committees = section[/-\n(.*?)\n\n/m, 1].scan(/^ +(.*?) +(.*)/).to_h # update/add chairs based on resolutions todos.each do |resolution| name = resolution['display_name'] if resolution['action'] == 'terminate' committees.delete(name) elsif resolution['chair'] person = ASF::Person.find(resolution['chair']) committees[name] = "#{person.public_name} <#{person.id}@apache.org>" end end # sort and concatenate committees committees = committees.sort_by { |name, _chair| name.downcase }. # ensure 2 spaces before chair email even if name is long map { |name, chair| " #{name.ljust(22)} #{chair}" }. join("\n") # replace committee info in the section, and then replace the # section in the committee-info contents section[/-\n(.*?)\n\n/m, 1] = committees contents[/^1\..*?\n=+/m] = section # return result contents end
update chairs
Source
# File lib/whimsy/asf/committee.rb, line 195 def self.update_next_month(contents, date, missing, rejected, todos) # extract next month section; and then extract the lines containing # '#' signs from within that section next_month = contents[/Next month.*?\n\n/m].chomp block = next_month[/(.*#.*\n)+/] || '' # remove expired entries month = date.strftime('%B') block.gsub!(/.* # new, monthly through #{month}\n/, '') # update/remove existing 'missing' entries existing = [] block.gsub! %r{(.*?)# (missing|not accepted) in .*\n} do |line| if missing.include? $1.strip existing << $1.strip if line.chomp.end_with? month line elsif line.split(',').last.include? 'not accepted' "#{line.chomp}, missing #{month}\n" else "#{line.chomp}, #{month}\n" end elsif rejected.include? $1.strip existing << $1.strip if line.chomp.end_with? month line else "#{line.chomp}, not accepted #{month}\n" end else '' end end # add new 'missing' entries (missing - existing).each do |pmc| block += " #{pmc.ljust(22)} # missing in #{month}\n" end # add new 'rejected' entries (rejected - missing - existing).each do |pmc| block += " #{pmc.ljust(22)} # not accepted in #{month}\n" end # add new 'established' entries and remove 'terminated' entries month = (date + 91).strftime('%B') todos.each do |resolution| pmc = resolution['display_name'] if resolution['action'] == 'terminate' block.sub! %r{^ #{Regexp.escape(pmc).ljust(22)} # .*\n}, '' elsif resolution['action'] == 'establish' and not existing.include? pmc block += " #{pmc.ljust(22)} # new, monthly through #{month}\n" end end # replace/append block if next_month.include? '#' next_month[/(.*#.*\n)+/] = block.split("\n").sort.join("\n") else next_month += block end # replace next month section contents[/Next month.*?\n\n/m] = next_month + "\n\n" # return result contents end
update next month section. Remove entries that have reported or or expired; add (or update) entries that are missing; add entries for new committees.
Source
# File lib/whimsy/asf/committee.rb, line 305 def self.update_roster(contents, cttee, people, action) found = false contents.scan(/^\* (?:.|\n)*?\n\s*?\n/).each do |block| # find committee next unless ASF::Committee.find(block[/\* (.*?)\s+\(/, 1]).id == cttee # split block into lines lines = block.strip.split("\n") header = lines.shift # get the first line and use that to calculate the default offsets to use # This is done to avoid changing the spacing needlessly sample = lines.first namelen = NAMELEN # original nameaddrlen = NAMEADDRLEN # original # N.B. 4 spaces are assumed at the start if sample =~ %r{^ (\S.+) (<\S+?>\s+)\[} namelen = $1.size nameaddrlen = namelen + $2.size end # add or remove people # There are generally more people already in a PMC than are added or removed, # so try to scan the lines once # Get list of emails affected yyyymmdd = Time.new.gmtime.strftime('[%Y-%m-%d]') # gather list of potential new entries (some may be removed below) newentries = people.map do |person| [person.public_name, "<#{person.id}@apache.org>", yyyymmdd] end.to_a if action == 'add' # parse the lines so we can use format_pmc to recreate the entry, adjusting lengths if need be parsed = lines.map do |line| m = line.match(%r{^ (\S.+?) (<[^>]+>)\s+(\[\d.+)}) if m newentries.reject! {|entry| m[2] == entry[1]} [m[1].strip, m[2], m[3]] else raise ArgumentError.new("Unexpected entry: #{line}") end end parsed += newentries lines = format_pmc(parsed, namelen, nameaddrlen) elsif action == 'remove' lines.reject! {|line| newentries.any? {|entry| line.include? entry[1]}} else raise ArgumentError.new("Expected action=[add|remove], found '#{action}'") end # replace committee block with new information contents.sub! block, ([header] + lines.sort).join("\n") + "\n\n" found = true break end raise ArgumentError.new("Could not find project id='#{cttee}'") unless found contents end
update roster for a project Intended for use in ASF::SVN.update()
block
contents = current contents (normally provided by ASF::SVN.update
); will be updated cttee = committee id (lower case) people = array of Person
objects action = add|remove Note: ignores duplicate changes (e.g. if person to add is already present)
Public Instance Methods
Source
# File lib/whimsy/asf/ldap.rb, line 1451 def add_committers(people) ASF::Project.find(name).add_members(people) end
add people as committers of a project. This information is stored in LDAP
using a members
attribute.
Source
# File lib/whimsy/asf/ldap.rb, line 1445 def add_owners(people) ASF::Project.find(name).add_owners(people) end
add people as owners of a project in LDAP
Source
# File lib/whimsy/asf/committee.rb, line 682 def chair Committee.load_committee_info if @chairs.length >= 1 ASF::Person.find(@chairs.first[:id]) else nil end end
returns the (first) chair as an instance of the ASF::Person
class.
Source
# File lib/whimsy/asf/ldap.rb, line 1430 def committerids ASF::Project.find(name).memberids end
List of committer ids for this committee Takes info from Project
Source
Source
Source
# File lib/whimsy/asf/committee.rb, line 771 def description meta = ASF::Committee.metadata(name) meta[:description] if meta end
description for this committee.
Source
# File lib/whimsy/asf/committee.rb, line 693 def display_name Committee.load_committee_info @display_name || name end
Version of name suitable for display purposes. Typically in uppercase. Data is sourced from committee-info.txt
.
Source
# File lib/whimsy/asf/committee.rb, line 700 def display_name=(name) @display_name ||= name end
setter for display_name
, should only be used by ASF::Committee.load_committee_info
Source
# File lib/whimsy/asf/ldap.rb, line 1456 def dn @dn ||= ASF::Project.find(name).dn end
Designated Name from LDAP
Source
# File lib/whimsy/asf/committee.rb, line 714 def info=(list) @info = list end
setter for display_name
, should only be used by ASF::Committee.load_committee_info
Source
# File lib/whimsy/asf/committee.rb, line 116 def mail_list case name.downcase when 'comdev' 'community' when 'httpcomponents' 'hc' when 'whimsy' 'whimsical' when 'brand' 'trademarks@apache.org' when 'infrastructure', 'infra' 'private@infra.apache.org' when 'dataprivacy' 'privacy@apache.org' when 'legalaffairs' # Not sure what uses this 'legal-internal@apache.org' when 'legal' # This seems to be used by the board agenda 'legal-private@apache.org' when 'fundraising' 'fundraising-private@apache.org' when 'marketingandpublicity' 'markpub@apache.org' when 'w3crelations' 'w3c@apache.org' when 'concom' 'planners@apachecon.com' when 'publicaffairs' 'public-affairs-private@apache.org' when 'logodev' 'logo-dev@apache.org' # their only list as at 2024-08-25 else name.downcase end end
mailing list for this committee. Generally returns the first name in the dns (e.g. whimsical). If so, it can be prefixed by a number of list names (e.g. dev, private) and .apache.org
is to be appended. In some cases, the name contains an @
sign and is the full name for the mail list. TODO: this is awkward to use as some non-PMCs have their own domain and some don’t Should probably be replaced by mail_private and mail_dev
Source
Source
# File lib/whimsy/asf/committee.rb, line 720 def names Committee.load_committee_info Hash[@roster.map {|id, info| [id, info[:name]]}] end
hash of availid => public_name for members (owners) of this committee Data is obtained from committee-info.txt
.
Source
# File lib/whimsy/asf/committee.rb, line 727 def nonpmc? Committee.load_committee_info # ensure data is there Committee.nonpmcs.include? self end
if true, this committee is not a PMC. Data is obtained from committee-info.txt
.
Source
# File lib/whimsy/asf/ldap.rb, line 1417 def ownerids ASF::Project.find(name).ownerids end
List of owner ids for this committee Takes info from Project
Source
Source
# File lib/whimsy/asf/committee.rb, line 734 def pmc? Committee.load_committee_info # ensure data is there Committee.pmcs.include? self end
if true, this committee is a PMC. Data is obtained from committee-info.txt
.
Source
# File lib/whimsy/asf/committee.rb, line 153 def private_mail_list ml = mail_list return mail_list if mail_list.include? '@' "private@#{mail_list}.apache.org" end
Return the committee private list
Source
# File lib/whimsy/asf/ldap.rb, line 1440 def remove_committers(people) ASF::Project.find(name).remove_members(people) end
remove people as members of a project in LDAP
Source
# File lib/whimsy/asf/ldap.rb, line 1435 def remove_owners(people) ASF::Project.find(name).remove_owners(people) end
remove people as owners of a project in LDAP
Source
# File lib/whimsy/asf/committee.rb, line 708 def report @report || @schedule end
when this committee is next expected to report. May be a string containing values such as “Next month: missing in May”, “Next month: new, monthly through July”. Or may be a list of months, separated by commas. Data is obtained from committee-info.txt
.
Source
# File lib/whimsy/asf/committee.rb, line 765 def site meta = ASF::Committee.metadata(name) meta[:site] if meta end
website for this committee.