retire.rb (152 lines of code) (raw):

#!/usr/bin/env ruby # Licensed to the Apache Software Foundation (ASF) under one or more # contributor license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright ownership. # The ASF licenses this file to You under the Apache License, Version 2.0 # (the "License"); you may not use this file except in compliance with # the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # #-------------------------------------------------------- # # Script to create _data/projects YAML file for one or more project ids # # Usage: # ruby retire.rb <list of project ids> # e.g. # ruby retire.rb gora mxnet # This will create/update _data/projects/pid.yaml # Review this and commit # TODO: # - does not check for Bugzilla issues # - does not check for GitHub issues # - Jira: juddi - does not find scout (is there an alias documented somewhere?) # - Cwiki: did not find ODE2 # - aliases: does not check for previous names # Input: # - https://whimsy.apache.org/public/committee-retired.json # - https://lists.apache.org/api/preferences.lua # - https://cwiki.apache.org/confluence/rest/api/space/ # - https://issues.apache.org/jira/rest/api/2/project # - https://svn.apache.org/repos/asf/ # - https://gitbox.apache.org/repositories.json # - https://svn.apache.org/repos/asf/infrastructure/site/trunk/content/foundation/records/minutes (temporary) # # Output: # _data/projects/pid.yaml require 'yaml' require 'net/http' require 'uri' require 'open3' # Sources CTTEE_RETIRED = 'https://whimsy.apache.org/public/committee-retired.json' CWIKI_INFO = 'https://cwiki.apache.org/confluence/rest/api/space/?type=global&limit=1000' # Note: Two Wikis are lower-case: labs, usergrid. Upper case does seem to be equivalent for display. JIRA = 'https://issues.apache.org/jira/rest/api/2/project' SVNURL = 'https://svn.apache.org/repos/asf/' GITREPOS = 'https://gitbox.apache.org/repositories.json' LISTSAO = 'https://lists.apache.org/api/preferences.lua' MINUTES = 'https://svn.apache.org/repos/asf/infrastructure/site/trunk/content/foundation/records/minutes/' def get_url(url) uri = URI.parse(url) Net::HTTP.start(uri.host, uri.port, open_timeout: 5, read_timeout: 5, ssl_timeout: 5, use_ssl: uri.scheme == 'https') do |http| return http.request Net::HTTP::Get.new uri.request_uri end end def get_json(url, key=nil) begin response = get_url(url) json = YAML.safe_load(response.body) if key return json[key] else return json end rescue Net::OpenTimeout => to puts to return nil end end def has_svn(pid) cmd = ['svn', 'ls', SVNURL + pid] _out, _err, status = Open3.capture3(*cmd) status.success? end # TODO: replace with full date from committee-retired when implemented def get_board_date(yyyymm) cmd = ['svn', 'ls', MINUTES+yyyymm[0,4]] out, err, status = Open3.capture3(*cmd) if status.success? m = out.split("\n").select {|l| l.include? yyyymm.sub('-','_')}.map {|d| d[14,10].gsub('_', '-')} return m # e.g. 2011-11 has two dates else p err return yyyymm end end # "key": "GMOxDOC21es", # "name": " Apache Geronimo v2.1 - ES ", def find_wikis(pid) names = [] WIKIS.each do |a| key = a['key'] names << key if key == pid or key.start_with?(pid) or a['name'].downcase =~ /\b#{pid}\b/ end names.sort end # Allow for Apache prefix and (Retired) suffixes etc. # returns first word, downcased # TODO: what about subnames? def canon_name(name) name.downcase.sub('apache','').gsub('(retired)', '').sub('(old)', '').strip.split.first end def get_jiras(pid) jiras = [] JIRAS.each do |project| key = project['key'] if pid == key.downcase jiras << key elsif pid == canon_name(project['name']) # Allow jiras << key else cat = project['projectCategory'] if cat catname = cat['name'] jiras << key if pid == catname.downcase end end end return jiras end def main() retirees = get_json(CTTEE_RETIRED)['retired'] not_retired = ARGV - retirees.keys if not_retired.size > 0 puts "The following projects are not recorded as retired: #{not_retired}" end # TODO filter out done ones # process valid projects (ARGV - not_retired).each do |pid| puts "Processing #{pid}" meta = retirees[pid] bdates = get_board_date(meta['retired']).map{|d| Date.parse(d)} bdates = bdates.first if bdates.size == 1 data = { retirement_date: bdates, attic_issue: 'ATTIC-nnn', attic_date: nil, attic_banner: true, # revived_date: nil, project_name: meta['display_name'], # project_longname: Where to find this?, project_description: meta['display_name'] + ' was a ' + meta['description'] + '.', board_resolution: true, board_reports: true, downloads: true, # check if there are any? } data.delete(:project_name) if data[:project_name] == pid.capitalize # Not needed if has_svn pid data[:source_repositories] ||= [] data[:source_repositories] << { type: 'Subversion' } end has_git = get_json(GITREPOS)['projects'][pid] if has_git data[:source_repositories] ||= [] data[:source_repositories] << { type: 'Git' } end mlists = get_json(LISTSAO)['lists']["#{pid}.apache.org"]&.keys if mlists # ensure dev sorts first data[:mailing_lists] = mlists.sort_by {|l| l == 'dev' ? 'aaa' : l} if mlists.size > 0 else $stderr.puts "No mailing lists found for #{pid}" end jiras = get_jiras pid if jiras.size > 0 data[:issue_trackers] ||= [] tracker = { type: 'JIRA' } tracker[:keys] = jiras.sort if jiras.size > 1 or jiras.first != pid.upcase data[:issue_trackers] << tracker end # TODO: Allow for Bugzilla and GitHub wikis = find_wikis pid if wikis.size > 0 data[:wiki] = { type: 'CWIKI' } data[:wiki][:keys] = wikis if wikis.size > 1 or wikis.first != pid.upcase end dir = ENV['OUTPUT'] || '_data/projects' # Allow override for testing output = File.join(dir, "#{pid}.yaml") puts "Creating #{output}" content = YAML.safe_dump(data, permitted_classes: [Date], stringify_names: true, indentation: 4) # Massage the output to look more like existing files tmp = content.sub('project_description: ', "project_description: >-\n ") File.write(output, tmp) end end if __FILE__ == $0 JIRAS = get_json(JIRA) WIKIS = get_json(CWIKI_INFO, 'results') main end