whimsy-asf 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,81 @@
1
+
2
+ module ASF
3
+
4
+ class Mail
5
+ def self.list
6
+ return @list if @list
7
+
8
+ list = Hash.new
9
+
10
+ # load info from LDAP
11
+ ASF::Person.preload(['mail', 'asf-altEmail'])
12
+ ASF::Person.collection.each do |name, person|
13
+ (person.mail+person.alt_email).each do |mail|
14
+ list[mail.downcase] = person
15
+ end
16
+ end
17
+
18
+ # load all member emails in one pass
19
+ ASF::Member.each do |id, text|
20
+ Member.emails(text).each {|mail| list[mail.downcase] ||= Person[id]}
21
+ end
22
+
23
+ # load all ICLA emails in one pass
24
+ ASF::ICLA.new.each do |id, name, email|
25
+ list[email.downcase] ||= Person.find(id)
26
+ next if id == 'notinavail'
27
+ list["#{id.downcase}@apache.org"] ||= Person.find(id)
28
+ end
29
+
30
+ @list = list
31
+ end
32
+
33
+ def self.lists(public_private= false)
34
+ apmail_bin = ASF::SVN['infra/infrastructure/apmail/trunk/bin']
35
+ file = File.join(apmail_bin, '.archives')
36
+ if not @lists or File.mtime(file) != @list_mtime
37
+ @list_mtime = File.mtime(file)
38
+ @lists = Hash[File.read(file).scan(
39
+ /^\s+"(\w[-\w]+)", "\/home\/apmail\/(public|private)-arch\//
40
+ )]
41
+ end
42
+
43
+ public_private ? @lists : @lists.keys
44
+ end
45
+ end
46
+
47
+ class Person < Base
48
+ def self.find_by_email(value)
49
+ value.downcase!
50
+
51
+ person = Mail.list[value]
52
+ return person if person
53
+ end
54
+
55
+ def obsolete_emails
56
+ return @obsolete_emails if @obsolete_emails
57
+ result = []
58
+ if icla
59
+ unless active_emails.any? {|mail| mail.downcase == icla.email.downcase}
60
+ result << icla.email
61
+ end
62
+ end
63
+ @obsolete_emails = result
64
+ end
65
+
66
+ def active_emails
67
+ (mail + alt_email + member_emails).uniq
68
+ end
69
+
70
+ def all_mail
71
+ active_emails + obsolete_emails
72
+ end
73
+ end
74
+
75
+ class Committee
76
+ def mail_list
77
+ return 'hc' if name == 'httpcomponents'
78
+ name
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,82 @@
1
+ module ASF
2
+ class Member
3
+ include Enumerable
4
+ attr_accessor :full
5
+
6
+ def self.find_text_by_id(value)
7
+ new.each do |id, text|
8
+ return text if id==value
9
+ end
10
+ nil
11
+ end
12
+
13
+ def self.each(&block)
14
+ new.each(&block)
15
+ end
16
+
17
+ def self.list
18
+ result = Hash[self.new(true).map {|name, text| [name, {text: text}]}]
19
+ self.status.each do |name, value|
20
+ result[name]['status'] = value
21
+ end
22
+ result
23
+ end
24
+
25
+ def self.find_by_email(value)
26
+ value = value.downcase
27
+ each do |id, text|
28
+ emails(text).each do |email|
29
+ return Person[id] if email.downcase == value
30
+ end
31
+ end
32
+ nil
33
+ end
34
+
35
+ def self.status
36
+ return @status if @status
37
+ status = {}
38
+ foundation = ASF::SVN['private/foundation']
39
+ sections = File.read("#{foundation}/members.txt").split(/(.*\n===+)/)
40
+ sections.shift(3)
41
+ sections.each_slice(2) do |header, text|
42
+ header.sub!(/s\n=+/,'')
43
+ text.scan(/Avail ID: (.*)/).flatten.each {|id| status[id] = header}
44
+ end
45
+ @status = status
46
+ end
47
+
48
+ def initialize(full = nil)
49
+ @full = (full.nil? ? ASF::Person.new($USER).asf_member? : full)
50
+ end
51
+
52
+ def each
53
+ foundation = ASF::SVN['private/foundation']
54
+ File.read("#{foundation}/members.txt").split(/^ \*\) /).each do |section|
55
+ id = section[/Avail ID: (.*)/,1]
56
+ section = '' unless @full
57
+ yield id, section.sub(/\n.*\n===+\s*?\n(.*\n)+.*/,'').strip if id
58
+ end
59
+ nil
60
+ end
61
+
62
+ def self.find(id)
63
+ each {|availid| return true if availid == id}
64
+ return false
65
+ end
66
+
67
+ def self.emails(text)
68
+ emails = text.to_s.scan(/Email: (.*(?:\n\s+\S+@.*)*)/).flatten.
69
+ join(' ').split(/\s+/).grep(/@/)
70
+ end
71
+ end
72
+
73
+ class Person
74
+ def members_txt
75
+ @members_txt ||= ASF::Member.find_text_by_id(name)
76
+ end
77
+
78
+ def member_emails
79
+ ASF::Member.emails(members_txt)
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,32 @@
1
+ module ASF
2
+
3
+ class Person < Base
4
+
5
+ def self.member_nominees
6
+ return @member_nominees if @member_nominees
7
+
8
+ meetings = ASF::SVN['private/foundation/Meetings']
9
+ nominations = Dir["#{meetings}/*/nominated-members.txt"].sort.last.untaint
10
+
11
+ nominations = File.read(nominations).split(/^\s*---+\s*/)
12
+ nominations.shift(2)
13
+
14
+ nominees = {}
15
+ nominations.each do |nomination|
16
+ id = nomination[/^\s?\w+.*<(\S+)@apache.org>/,1]
17
+ id ||= nomination[/^\s?\w+.*\((\S+)@apache.org\)/,1]
18
+ id ||= nomination[/^\s?\w+.*\(([a-z]+)\)/,1]
19
+
20
+ next unless id
21
+
22
+ nominees[find(id)] = nomination
23
+ end
24
+
25
+ @member_nominees = nominees
26
+ end
27
+
28
+ def member_nomination
29
+ Person.member_nominees[self]
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,23 @@
1
+ require 'nokogiri'
2
+ require_relative '../asf'
3
+
4
+ module ASF
5
+ class Podlings
6
+ include Enumerable
7
+
8
+ def each
9
+ incubator_content = ASF::SVN['asf/incubator/public/trunk/content']
10
+ podlings = Nokogiri::XML(File.read("#{incubator_content}/podlings.xml"))
11
+ podlings.search('podling').map do |node|
12
+ data = {
13
+ name: node['name'],
14
+ status: node['status'],
15
+ description: node.at('description').text,
16
+ mentors: node.search('mentor').map {|mentor| mentor['username']}
17
+ }
18
+ data[:champion] = node.at('champion')['availid'] if node.at('champion')
19
+ yield node['resource'], data
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,81 @@
1
+ require_relative '../asf.rb'
2
+ require 'rack'
3
+ require 'etc'
4
+
5
+ module ASF
6
+ module Auth
7
+ DIRECTORS = {
8
+ 'rbowen' => 'rb',
9
+ 'cutting' => 'dc',
10
+ 'bdelacretaz' => 'bd',
11
+ 'rgardler' => 'rg',
12
+ 'jim' => 'jj',
13
+ 'mattmann' => 'cm',
14
+ 'brett' => 'bp',
15
+ 'rubys' => 'sr',
16
+ 'gstein' => 'gs'
17
+ }
18
+
19
+ # Simply 'use' the following class in config.ru to limit access
20
+ # to the application to ASF members and officers and the EA.
21
+ class MembersAndOfficers < Rack::Auth::Basic
22
+ def initialize(app)
23
+ super(app, "ASF Members and Officers", &proc {})
24
+ end
25
+
26
+ def call(env)
27
+ authorized = false
28
+
29
+ user = env['REMOTE_USER'] ||= ENV['USER'] || Etc.getpwuid.name
30
+ person = ASF::Person.new(user)
31
+
32
+ authorized ||= DIRECTORS[user]
33
+ authorized ||= person.asf_member?
34
+ authorized ||= ASF.pmc_chairs.include? person
35
+ authorized ||= (user == 'ea')
36
+
37
+ if authorized
38
+ class << env; attr_accessor :user, :password; end
39
+ if env['HTTP_AUTHORIZATION']
40
+ require 'base64'
41
+ env.user, env.password = Base64.decode64(env['HTTP_AUTHORIZATION'][
42
+ /^Basic ([A-Za-z0-9+\/=]+)$/,1]).split(':',2)
43
+ else
44
+ env.user = user
45
+ end
46
+
47
+ @app.call(env)
48
+ else
49
+ unauthorized
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ # Apache httpd on whimsy-vm is behind a proxy that converts https
56
+ # requests into http requests. Update the environment variables to
57
+ # match.
58
+ class HTTPS_workarounds
59
+ def initialize(app)
60
+ @app = app
61
+ end
62
+
63
+ def call(env)
64
+ if env['HTTPS'] == 'on'
65
+ env['SCRIPT_URI'].sub!(/^http:/, 'https:')
66
+ env['SERVER_PORT'] = '443'
67
+
68
+ # for reasons I don't understand, Passenger on whimsy doesn't
69
+ # forward root directory requests directly, so as a workaround
70
+ # these requests are rewritten and the following code maps
71
+ # the requests back:
72
+ if env['PATH_INFO'] == '/index.html'
73
+ env['PATH_INFO'] = '/'
74
+ env['SCRIPT_URI'] += '/'
75
+ end
76
+ end
77
+ return @app.call(env)
78
+ end
79
+ end
80
+
81
+ end
@@ -0,0 +1,24 @@
1
+ require_relative '../asf'
2
+
3
+ module ASF
4
+
5
+ class Site
6
+ @@list = {}
7
+
8
+ def self.list
9
+ Committee.load_committee_info
10
+ templates = ASF::SVN['asf/infrastructure/site/trunk/templates']
11
+ file = "#{templates}/blocks/projects.mdtext"
12
+ return @@list if not @@list.empty? and File.mtime(file) == @@mtime
13
+ @@mtime = File.mtime(file)
14
+
15
+ projects = File.read(file)
16
+ projects.scan(/\[(.*?)\]\((http.*?) "(.*)"\)/).each do |name, link, text|
17
+ @@list[Committee.find(name).name] = {link: link, text: text}
18
+ end
19
+
20
+ @@list
21
+ end
22
+ end
23
+
24
+ end
@@ -0,0 +1,27 @@
1
+ require 'uri'
2
+ require 'thread'
3
+
4
+ module ASF
5
+
6
+ class SVN
7
+ @base = URI.parse('https://svn.apache.org/repos/')
8
+ @mock = 'file:///var/tools/svnrep/'
9
+ @semaphore = Mutex.new
10
+
11
+ def self.repos
12
+ @semaphore.synchronize do
13
+ @repos ||= Hash[Dir['/home/whimsysvn/svn/*'].map { |name|
14
+ Dir.chdir name.untaint do
15
+ [`svn info`[/URL: (.*)/,1].sub(/^http:/,'https:'), Dir.pwd.untaint]
16
+ end
17
+ }]
18
+ end
19
+ end
20
+
21
+ def self.[](name)
22
+ repos[(@mock+name.sub('private/','')).to_s.sub(/\/*$/, '')] ||
23
+ repos[(@base+name).to_s.sub(/\/*$/, '')] # lose trailing slash
24
+ end
25
+ end
26
+
27
+ end
@@ -0,0 +1,41 @@
1
+ module ASF
2
+
3
+ class Person < Base
4
+
5
+ def self.member_watch_list
6
+ return @member_watch_list if @member_watch_list
7
+
8
+ foundation = ASF::SVN['private/foundation']
9
+ text = File.read "#{foundation}/potential-member-watch-list.txt"
10
+
11
+ nominations = text.scan(/^\s+\*\)\s+\w.*?\n\s*(?:---|\Z)/m)
12
+
13
+ i = 0
14
+ member_watch_list = {}
15
+ nominations.each do |nomination|
16
+ id = nil
17
+ name = nomination[/\*\)\s+(.+?)\s+(\(|\<|$)/,1]
18
+ id ||= nomination[/\*\)\s.+?\s\((.*?)\)/,1]
19
+ id ||= nomination[/\*\)\s.+?\s<(.*?)@apache.org>/,1]
20
+
21
+ unless id
22
+ id = "notinavail_#{i+=1}"
23
+ find(id).attrs['cn'] = name
24
+ end
25
+
26
+ member_watch_list[find(id)] = nomination
27
+ end
28
+
29
+ @member_watch_list = member_watch_list
30
+ end
31
+
32
+ def member_watch
33
+ text = Person.member_watch_list[self]
34
+ if text
35
+ text.sub!(/\A\s*\n/,'')
36
+ text.sub!(/\n---\Z/,'')
37
+ end
38
+ text
39
+ end
40
+ end
41
+ end
metadata ADDED
@@ -0,0 +1,149 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: whimsy-asf
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Sam Ruby
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-09-22 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: nokogiri
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rack
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: ruby-ldap
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: tzinfo
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: wunderbar
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ description: ! " This package contains a set of classes which encapsulate access\n
95
+ \ to a number of data sources such as LDAP, ICLAs, auth lists, etc.\n"
96
+ email: rubys@intertwingly.net
97
+ executables: []
98
+ extensions: []
99
+ extra_rdoc_files: []
100
+ files:
101
+ - asf.gemspec
102
+ - asf.version
103
+ - lib/whimsy/asf.rb
104
+ - lib/whimsy/asf/member.rb
105
+ - lib/whimsy/asf/ldap.rb
106
+ - lib/whimsy/asf/mail.rb
107
+ - lib/whimsy/asf/icla.rb
108
+ - lib/whimsy/asf/svn.rb
109
+ - lib/whimsy/asf/podlings.rb
110
+ - lib/whimsy/asf/site.rb
111
+ - lib/whimsy/asf/auth.rb
112
+ - lib/whimsy/asf/nominees.rb
113
+ - lib/whimsy/asf/agenda/back.rb
114
+ - lib/whimsy/asf/agenda/front.rb
115
+ - lib/whimsy/asf/agenda/special.rb
116
+ - lib/whimsy/asf/agenda/attachments.rb
117
+ - lib/whimsy/asf/agenda/exec-officer.rb
118
+ - lib/whimsy/asf/agenda/minutes.rb
119
+ - lib/whimsy/asf/agenda/committee.rb
120
+ - lib/whimsy/asf/agenda.rb
121
+ - lib/whimsy/asf/rack.rb
122
+ - lib/whimsy/asf/committee.rb
123
+ - lib/whimsy/asf/watch.rb
124
+ homepage: https://whimsy.apache.org/
125
+ licenses:
126
+ - Apache License, Version 2.0
127
+ post_install_message:
128
+ rdoc_options: []
129
+ require_paths:
130
+ - lib
131
+ required_ruby_version: !ruby/object:Gem::Requirement
132
+ none: false
133
+ requirements:
134
+ - - ! '>='
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ required_rubygems_version: !ruby/object:Gem::Requirement
138
+ none: false
139
+ requirements:
140
+ - - ! '>='
141
+ - !ruby/object:Gem::Version
142
+ version: '0'
143
+ requirements: []
144
+ rubyforge_project:
145
+ rubygems_version: 1.8.23
146
+ signing_key:
147
+ specification_version: 3
148
+ summary: Whimsy 'model' of the ASF
149
+ test_files: []