/
icla.rb
201 lines (168 loc) · 5.14 KB
/
icla.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
require 'json'
module ASF
class ICLA
# N.B. only id and name should be considered public
# form and claRef may contain details of the legal name beyond that in the public name
attr_accessor :id, :legal_name, :name, :email, :form
attr_accessor :claRef # cla name or SVN revision info
@@mtime = nil
# list of availids that should not be used
@@availids_reserved = nil
OFFICERS = ASF::SVN.find('private/foundation/officers')
SOURCE = OFFICERS ? "#{OFFICERS}/iclas.txt" : nil
# flush caches if source file changed
def self.refresh
if not SOURCE or File.mtime(SOURCE) != @@mtime
@@mtime = SOURCE ? File.mtime(SOURCE) : Time.now
@@id_index = nil
@@email_index = nil
@@name_index = nil
@@svn_change = nil
@@availids = nil
end
end
def self.svn_change
self.refresh
if SOURCE
@@svn_change ||= Time.parse(
`svn info #{SOURCE}`[/Last Changed Date: (.*) \(/, 1]).gmtime
end
end
# load ICLA information for every committer
def self.preload
people = []
each do |icla|
unless icla.id == 'notinaval'
person = ASF::Person.find(icla.id)
people << person
person.icla = icla
end
end
people
end
# find ICLA by ID
def self.find_by_id(value)
return if value == 'notinavail' or not SOURCE
refresh
unless @@id_index
@@id_index = {}
each {|icla| @@id_index[icla.id] = icla}
end
@@id_index[value]
end
# find ICLA by email
def self.find_by_email(value)
return unless SOURCE
refresh
unless @@email_index
@@email_index = {}
each {|icla| @@email_index[icla.email.downcase] = icla}
end
@@email_index[value.downcase]
end
# find ICLA by name
def self.find_by_name(value)
return unless SOURCE
refresh
unless @@name_index
@@name_index = {}
each {|icla| @@name_index[icla.name] = icla}
end
@@name_index[value]
end
# list of all ids
def self.availids
return [] unless SOURCE
refresh
return @@availids if @@availids
availids = []
each {|icla| availids << icla.id unless icla.id == 'notinavail'}
@@availids = availids
end
# iterate over all of the ICLAs
def self.each(&block)
refresh
if @@id_index and not @@id_index.empty?
@@id_index.values.each(&block)
elsif @@email_index and not @@email_index.empty?
@@email_index.values.each(&block)
elsif @@name_index and not @@name_index.empty?
@@name_index.values.each(&block)
elsif SOURCE and File.exist?(SOURCE)
File.read(SOURCE).scan(/^([-\w]+):(.*?):(.*?):(.*?):(.*)/).each do |list|
icla = ICLA.new()
icla.id = list[0]
icla.legal_name = list[1]
icla.name = list[2]
icla.email = list[3]
icla.form = list[4]
match = icla.form.match(/^Signed CLA(?:;(\S+)| \((\+=.+)\))/)
if match
# match either the cla name or the SVN ref (+=...)
icla.claRef = match[1] || match[2]
end
block.call(icla)
end
end
end
# rearrange line in an order suitable for sorting
def self.lname(line)
return '' if line.start_with? '#'
id, name, rest = line.split(':',3)
return '' unless name
# Drop trailing (comment string) or /* comment */
name.sub! /\(.+\)$/,''
name.sub! /\/\*.+\*\/$/,''
return '' if name.strip.empty?
name = ASF::Person.sortable_name(name)
"#{name}:#{rest}"
end
# sort an entire iclas.txt file
def self.sort(source)
headers = source.scan(/^#.*/)
lines = source.scan(/^\w.*/)
headers.join("\n") + "\n" +
lines.sort_by {|line| lname(line + "\n")}.join("\n") + "\n"
end
def self.availids_reserved
return @@availids_reserved if @@availids_reserved
archive = ASF::SVN['private/foundation/officers']
reserved = File.read("#{archive}/reserved-ids.yml").scan(/^- (\S+)/).flatten.uniq
@@availids_reserved = reserved
end
# list of all availids that are are taken or reserved
def self.availids_taken()
self.availids_reserved + self.availids
end
# is the availid taken (in use or reserved)?
def self.taken?(id)
puts(id)
return self.availids_reserved.include?(id) ||
self.availids.include?(id)
end
# is the id available?
def self.available?(id)
return ! self.taken?(id)
end
end
class Person
def icla
@icla ||= ASF::ICLA.find_by_id(name)
end
def icla=(icla)
@icla = icla
end
def icla?
@icla || ICLA.availids.include?(name)
end
end
# Search archive for historical records of people who were committers
# but never submitted an ICLA (some of which are still ASF members or
# members of a PMC).
def self.search_archive_by_id(id)
archive = ASF::SVN['private/foundation/officers/historic']
name = JSON.parse(File.read("#{archive}/committers.json"))[id]
name = id if name and name.empty?
name
end
end