| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 | #!/usr/bin/rubyrequire 'json'require 'net/http'require 'yaml'require 'logger'$log = Logger.new(STDOUT)$log.level = Logger::WARNclass ForwardMerge  attr_reader :issue, :milestone, :message, :line  def initialize(issue, milestone, message, line)    @issue = issue    @milestone = milestone    @message = message    @line = line  endenddef find_forward_merges(message_file)  $log.debug "Searching for forward merge"  rev=`git rev-parse -q --verify MERGE_HEAD`.strip  $log.debug "Found #{rev} from git rev-parse"  return nil unless rev  message = File.read(message_file)  forward_merges = []  message.each_line do |line|    $log.debug "Checking #{line} for message"    match = /^(?:Fixes|Closes) gh-(\d+) in (\d\.\d\.[\dx](?:[\.\-](?:M|RC)\d)?)$/i.match(line)    if match then      issue = match[1]      milestone = match[2]      $log.debug "Matched reference to issue #{issue} in milestone #{milestone}"      forward_merges << ForwardMerge.new(issue, milestone, message, line)    end  end  $log.debug "No match in merge message" unless forward_merges  return forward_mergesenddef get_issue(username, password, repository, number)  $log.debug "Getting issue #{number} from GitHub repository #{repository}"  uri = URI("https://api.github.com/repos/#{repository}/issues/#{number}")  http = Net::HTTP.new(uri.host, uri.port)  http.use_ssl=true  request = Net::HTTP::Get.new(uri.path)  request.basic_auth(username, password)  response = http.request(request)  $log.debug "Get HTTP response #{response.code}"  return JSON.parse(response.body) unless response.code != '200'  puts "Failed to retrieve issue #{number}: #{response.message}"  exit 1enddef find_milestone(username, password, repository, title)  $log.debug "Finding milestone #{title} from GitHub repository #{repository}"  uri = URI("https://api.github.com/repos/#{repository}/milestones")  http = Net::HTTP.new(uri.host, uri.port)  http.use_ssl=true  request = Net::HTTP::Get.new(uri.path)  request.basic_auth(username, password)  response = http.request(request)  milestones = JSON.parse(response.body)  if title.end_with?(".x")    prefix = title.delete_suffix('.x')    $log.debug "Finding nearest milestone from candidates starting with #{prefix}"    titles = milestones.map { |milestone| milestone['title'] }    titles = titles.select{ |title| title.start_with?(prefix) unless title.end_with?('.x')}    titles = titles.sort_by { |v| Gem::Version.new(v) }    $log.debug "Considering candidates #{titles}"    if(titles.empty?)      puts "Cannot find nearest milestone for prefix #{title}"      exit 1    end    title = titles.first    $log.debug "Found nearest milestone #{title}"  end  milestones.each do |milestone|    $log.debug "Considering #{milestone['title']}"    return milestone['number'] if milestone['title'] == title  end  puts "Milestone #{title} not found in #{repository}"  exit 1enddef create_issue(username, password, repository, original, title, labels, milestone, milestone_name, dry_run)  $log.debug "Finding forward-merge issue in GitHub repository #{repository} for '#{title}'"  uri = URI("https://api.github.com/repos/#{repository}/issues")  http = Net::HTTP.new(uri.host, uri.port)  http.use_ssl=true  request = Net::HTTP::Post.new(uri.path, 'Content-Type' => 'application/json')  request.basic_auth(username, password)  request.body = {    title: title,    labels: labels,    milestone: milestone.to_i,    body: "Forward port of issue ##{original} to #{milestone_name}."  }.to_json  if dry_run then    puts "Dry run"    puts "POSTing to #{uri} with body #{request.body}"    return "dry-run"  end  response = JSON.parse(http.request(request).body)  $log.debug "Created new issue #{response['number']}"  return response['number']end$log.debug "Running forward-merge hook script"message_file=ARGV[0]forward_merges = find_forward_merges(message_file)exit 0 unless forward_merges$log.debug "Loading config from ~/.spring-boot/forward_merge.yml"config = YAML.load_file(File.join(Dir.home, '.spring-boot', 'forward-merge.yml'))username = config['github']['credentials']['username']password = config['github']['credentials']['password']dry_run = config['dry_run']repository = 'spring-projects/spring-security'forward_merges.each do |forward_merge|  existing_issue = get_issue(username, password, repository, forward_merge.issue)  title = existing_issue['title']  labels = existing_issue['labels'].map { |label| label['name'] }  labels << "status: forward-port"  $log.debug "Processing issue '#{title}'"  milestone = find_milestone(username, password, repository, forward_merge.milestone)  new_issue_number = create_issue(username, password, repository, forward_merge.issue, title, labels, milestone, forward_merge.milestone, dry_run)  puts "Created gh-#{new_issue_number} for forward port of gh-#{forward_merge.issue} into #{forward_merge.milestone}"  rewritten_message = forward_merge.message.sub(forward_merge.line, "Closes gh-#{new_issue_number}\n")  File.write(message_file, rewritten_message)end
 |