This is an automated email from the ASF dual-hosted git repository. sebb pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/whimsy.git
commit 12dff001191ac049d3cf9eea1ca5482c4c176a40 Author: Sebb <s...@apache.org> AuthorDate: Sun Jun 1 17:39:09 2025 +0100 Only send email for missing ICLA --- www/secretary/icla-lint copy.cgi | 405 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 405 insertions(+) diff --git a/www/secretary/icla-lint copy.cgi b/www/secretary/icla-lint copy.cgi new file mode 100755 index 00000000..af04467a --- /dev/null +++ b/www/secretary/icla-lint copy.cgi @@ -0,0 +1,405 @@ +#!/usr/bin/env ruby + +$LOAD_PATH.unshift '/srv/whimsy/lib' + +require 'wunderbar/script' +require 'ruby2js/filter/functions' +require 'whimsy/asf' + +people = ASF::Person.listids +committers = ASF.committerids # to check for missing ICLAs + +ASF::ICLAFiles.update_cache({}) + +errors = 0 + +_html do + _style %{ + table {border-collapse: collapse} + table, th, td {border: 1px solid black} + td {padding: 3px 6px} + tr:hover td {background-color: #FF8} + th {background-color: #a0ddf0} + } + + _h1 'iclas.txt lint check' + + _h2_ 'LDAP Status' + _div do + _label "#{people.length} People entries found." + _br + _label "#{committers.length} committers found." + end + + _h2_ 'Error Status' + _div do + _label "#{errors} errors found." + end + + _h2_ 'Show' + _div do + _input id: 'missing', type: 'checkbox', checked: true + _label 'missing stub/dir name after Signed CLA', for: 'missing' + end + + _div do + _input id: 'extra', type: 'checkbox', checked: true + _label 'extra text', for: 'extra' + end + + _div do + _input id: 'comment', type: 'checkbox', checked: true + _label 'parenthetical comment', for: 'comment' + end + + _div do + _input id: 'error', type: 'checkbox', checked: true + _label 'icla not found', for: 'error' + end + + _div do + _input id: 'mismatch', type: 'checkbox', checked: true + _label "doesn't match pattern", for: 'mismatch' + end + + _div do + _input id: 'notinldap', type: 'checkbox', checked: true + _label "id not in LDAP people", for: 'notinldap' + end + + _div do + _input id: 'notinavail', type: 'checkbox', checked: true + _label "notinavail entries", for: 'notinavail' + end + + iclafiles = Hash.new{|h,k| h[k]=[]} + dupes = 0 + ASF::ICLAFiles.listnames.each do |file| + next if file == '__keys__/' + name = File.basename(file) + stem = name.sub(/\.\w+$/, '') + dupes += 1 if iclafiles.has_key? stem + iclafiles[stem] << name + end + + seen = Hash.new { |h, k| h[k] = []} # iclas.txt CLA stem values (value = id,name) + icla_ids = Hash.new { |h, k| h[k] = []} # to check for duplicates and missing entries + icla_mails = Hash.new { |h, k| h[k] = []} # to check for duplicates + + _h2_ 'Issues' + + _table_ do + _tr do + _th 'availid' + _th 'public name' + _th 'issue' + _th 'email' + _th 'ICLA stub' + end + + input = File.join(ASF::SVN['officers'], 'iclas.txt') + document = File.read(input) + document.scan(/^((\w.*?):.*?:(.*?):(.*?):(.*))/) do |(line, id, name, email, comment)| + issue, note = nil, nil + comment2 = comment.dup + claRef = nil + + if comment.sub!(/\s*(\(.*?\))\s*/, '') + issue, note = 'comment', "parenthetical comment: #{$1.inspect}" + end + + if comment.sub!(/Signed CLA(.+?);/, 'Signed CLA;') + issue, note = 'extra', "extra text: #{$1.inspect}" + end + + if id != 'notinavail' + icla_ids[id] << line + apachemail = "," + id + "@apache.org" + end + + # check LDAP independently; may be overridden by issues with comment field + if id != 'notinavail' and people.length > 0 and not people.include? id + issue, note = 'notinldap', 'not in LDAP people' + end + if comment =~ /Signed CLA;(.*)/ + claRef = $1 + # to be valid, the entry must exist; also record what we have seen + missing = claRef.split(',').reject {|path| seen[path] << [id,name,email]; iclafiles.include? path} + + if not missing.empty? + issue, note = 'error', "missing icla: #{missing.first.inspect}" + end + if id != 'notinavail' + committers.delete(id) # drop committers that have ICLAs + end + elsif comment =~ /^Treasurer;/ or comment =~ /^President;/ + + elsif comment == 'Signed CLA' + issue, note = 'missing', 'missing stub/dir name' + elsif comment.start_with? 'disabled;' + unless ASF::Person.new(id).banned? + issue, note = 'mismatch', "LDAP entry not marked disabled" + end + else + issue, note = 'mismatch', "comment doesn't match pattern" + end + + icla_mails[email] << [id, claRef, line] + + if issue + issue = "#{issue} notinavail" if id =='notinavail' + + _tr_ class: issue do + _td! do + if id == 'notinavail' or issue == 'notinldap' + _ id + else + _a id, href: '/roster/committer/' + id + end + end + + if id != 'notinavail' and ASF::Person.new(id).asf_member? + _td! {_b name} + else + _td name + end + + _td do + if note.start_with? ('missing') # email only applies to missing ICLA currently + _button 'email', data_email: "#{name} <#{email}>#{apachemail}", + data_issue: note, data_name: name + end + _span note + end + + _td email + _td comment2 + end + end + end + end + + # drop known test entries + TEST_ENTRIES = %w(testsebb testrubys testcml testdooh) + noiclas = committers.reject {|id| TEST_ENTRIES.include? id} + + _h2 "Committers without an ICLA recorded (#{noiclas.size})" + + if noiclas.size > 0 + _table do + _tr do + _th 'id' + _th 'Public Name' + _th 'Join Date' + _th 'Nologin?' + end + noiclas.each do |id| + _tr do + _td do + _a id, href: '/roster/committer/' + id + end + _td ASF::Person[id].public_name + _td ASF::Person[id].createDate + _td ASF::Person[id].nologin? + end + end + end + else + _ 'All committers have ICLAs' + end + + if dupes > 0 + _h2_ 'Files with duplicate stems' + _table_ do + _tr do + _th 'stem' + _th 'paths' + end + iclafiles.each do |icla,paths| + if paths.size > 1 + _tr do + _td icla + _td do + paths.each do |path| + _a path, href: ASF::SVN.svnpath!('iclas', path) + end + end + end + end + end + end + end + + # drop any stems we have seen + iclafiles_not_seen = iclafiles.reject {|path| seen.include? path} + + # select entries with count != 1 + seen.select! { |k, v| v.length != 1} + if seen.size > 0 + _h2_ 'Duplicate stem entries in iclas.txt' + _table_ do + _tr do + _th 'stem' + _th 'count' + _th 'entries' + end + seen.each do |k,v| + _tr do + _td k + _td v.length + _td do + v.each do |w| + _ w + _ ' ' + end + end + end + end + end # table + end + + if iclafiles_not_seen.size > 0 + _h2_ 'ICLA files not matched against iclas.txt' + _table do + _tr do + _th 'stem' + end + iclafiles_not_seen.each do |k,v| + v.each do |p| + _tr do + _td do + _a k, href: ASF::SVN.svnpath!('iclas', p) + end + end + end + end + end + end + + # Check if there are any duplicate ids + dups=icla_ids.select{|k,v| v.size > 1} + if dups.size > 0 + _h2_ 'Duplicate availids in iclas.txt' + _table do + _tr do + _th 'Availid' + _th 'Entry' + end + dups.each do |k,v| + v.each do |l| + _tr do + _td k + _td l + end + end + end + end + end + + # Check that all LDAP entries appear in iclas.txt + no_icla = people.reject {|k| icla_ids.has_key? k} + # remove known exceptions + %w(testsebb testrubys testcml testdooh apldaptest cml-test iroh-test rmonk).each {|w| no_icla.delete w} + if no_icla.size > 0 + _h2 'LDAP entries not listed in iclas.txt' + _table_ do + _tr do + _th 'Availid' + _th 'Public name' + _th 'Creation Date' + _th 'Whimsy page' + end + no_icla.each do |k| + _tr do + _td k + _td ASF::Person[k].public_name + _td ASF::Person[k].createDate + _td do + _a k, href: '/roster/committer/' + k + end + end + end + end + end + + # Check if there are any duplicate mails + mdups = icla_mails.select {|_k, v| v.size > 1} + if mdups.size > 0 + _h2_ 'Duplicate mails in iclas.txt' + _table do + _tr do + _th 'Email' + _th 'Availid' + _th 'ICLA' + _th 'Entry' + end + mdups.each do |k,v| + v.each do |l| + id_, icla_, line_ = l + _tr do + _td k + _td do + if id_ != 'notinavail' + _a id_, href: '/roster/committer/' + k + else + _ id_ + end + end + _td do + file = ASF::ICLAFiles.match_claRef(icla_) + if file + _a icla_, href: ASF::SVN.svnpath!('iclas', file) + else + _ icla_ + end + end + _td line_ + end + end + end + end + end + + _script do + inputs = document.querySelectorAll('input') + for i in 0...inputs.length + inputs[i].checked = true + inputs[i].addEventListener('click') do |event| + rows = document.getElementsByClassName(event.target.id) + errors = rows.length + for j in 0...rows.length + if event.target.checked + rows[j].style.display = '' + else + rows[j].style.display = 'none' + end + end + end + end + buttons = document.querySelectorAll('button') + for i in 0...buttons.length + # Only supports missing ICLA currently + buttons[i].addEventListener('click') do |event| + email = event.target.getAttribute('data-email') + # issue = event.target.getAttribute('data-issue') + name = event.target.getAttribute('data-name') + + destination = "mailto:#{email}?cc=secret...@apache.org" + subject = 'Your Apache ICLA has gone missing' + body = "Dear " + name + ",\n\n" + + "We are reviewing our records to be sure that all submitted ICLAs are on file.\n" + + "Unfortunately, we are unable to locate the ICLA that you submitted earlier.\n\n" + + "Can you please resubmit to secret...@apache.org? https://apache.org/licenses/contributor-agreements.html#submitting\n" + + "Please do *not* use an apache email as your E-Mail address.\n" + + "You can send the original ICLA (if the email address is still valid) or a new one.\n\n" + + "Best regards,\n" + + window.location = destination + + "&subject=#{encodeURIComponent(subject)}" + + "&body=#{encodeURIComponent(body)}" + + end + end + + end +end