commit:     22546d7465a9c58a7bb3487d5611b33e93b1f6cc
Author:     Alex Legler <a3li <AT> gentoo <DOT> org>
AuthorDate: Tue May 17 17:27:55 2011 +0000
Commit:     Pavlos Ratis <dastergon <AT> gentoo <DOT> org>
CommitDate: Tue May 17 17:27:55 2011 +0000
URL:        
http://git.overlays.gentoo.org/gitweb/?p=proj/security.git;a=commit;h=22546d74

acutally add the new tool

svn path=/; revision=2228

---
 bin/target | 346 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 346 insertions(+)

diff --git a/bin/target b/bin/target
new file mode 100755
index 0000000..22001dd
--- /dev/null
+++ b/bin/target
@@ -0,0 +1,346 @@
+#!/usr/bin/env ruby
+# Target 2
+# written by Alex Legler <a...@gentoo.org>
+# dependencies: app-portage/gentoolkit, dev-lang/ruby[ssl], dev-ruby/highline
+# vim: set sw=2 ts=2:
+
+require 'optparse'
+require 'highline'
+require 'fileutils'
+require 'xmlrpc/client'
+
+class Net::HTTP
+       alias_method :old_initialize, :initialize
+       def initialize(*args)
+               old_initialize(*args)
+               @ssl_context = OpenSSL::SSL::SSLContext.new
+               @ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
+       end
+end
+
+module GenSec
+       module Target
+               # These architectures don't stabilize packages
+               NOSTABLE_ARCHES = ['mips']
+
+               def main(argv)
+                       $opts = {
+                               :auth_cache => true,
+                               :force => false,
+                               :liaisons => false,
+                               :username => nil,
+                               :prestable => false,
+                               :quiet => false
+                       }
+
+                       $ui = HighLine.new
+
+                       bug = nil
+                       version = nil
+                       slot = nil
+
+                       optparse = OptionParser.new do |opts|
+                               opts.on('-b', '--bug BUGNO', 'The number of the 
bug to change') do |b|
+                                       bug = Integer(b)
+                               end
+
+                               opts.on('-v', '--version VERSION', 'Use this 
version as stabilization target') do |v|
+                                       version = v
+                               end
+
+                               opts.on('-s', '--slot SLOT', 'Use ebuilds from 
this slot to find the best ebuild') do |s|
+                                       slot = s
+                               end
+
+                               opts.on('-l', '--liaisons', 'CC the arch 
liaisons instead of arch teams') do
+                                       $opts[:liaisons] = true
+                               end
+
+                               opts.on('-p', '--prestable', 'Use prestabling 
instructions') do
+                                       $opts[:prestable] = true
+                               end
+
+                               opts.on('-u', '--username USERNAME', 'Use this 
user name to log in at Bugzilla') do |username|
+                                       $opts[:username] = username
+                               end
+
+                               opts.on_tail('-f', '--force', 'Force the 
operation. Disables asking for confirmation and version checks.') do
+                                       $opts[:force] = true
+                               end
+
+                               opts.on_tail('-q', '--quiet', 'Be less noisy') 
do
+                                       $opts[:quiet] = true
+                               end
+
+                               opts.on_tail('-h', '--help', 'Display this 
screen') do
+                                       puts opts
+                                       exit
+                               end
+                               
+                       end
+
+                       optparse.banner = "Usage: #{$0} [options] 
[package]\n\nAvailable options:\n"
+                       cmd_options = optparse.parse!(argv)
+
+                       if argv.length > 0
+                               package = argv.shift
+                       else
+                               package = Dir.pwd.split('/').last(2).join('/')
+                       end
+
+                       metadata = get_metadata(package)
+                       do_package(metadata, bug, version, slot)
+               end
+
+               def do_package(metadata, bug, version, slot)
+                       if metadata[:package] == nil or metadata[:package] == ''
+                               e("No package found.")
+                       end
+
+                       i("Using #{metadata[:package]}") unless $opts[:quiet]
+                       #puts metadata.inspect
+
+                       best_version = find_best_version(metadata, slot, 
version)
+                       i("Target version: #{best_version}") unless 
$opts[:quiet]
+
+                       # Cover a custom version string that is not there in 
the local tree
+                       if metadata[:keywords].include? best_version
+                               already_stable = 
filter_unstable(metadata[:keywords][best_version]) - NOSTABLE_ARCHES
+                       else
+                               already_stable = []
+                       end
+
+                       need_stable = metadata[:stable_arches] - NOSTABLE_ARCHES
+
+                       i("Arches this package was ever stable on: 
#{$ui.color(need_stable.join(', '), :red, :bold)}") unless $opts[:quiet]
+
+                       if already_stable.length > 0
+                               i("Target version is already stable on:    
#{$ui.color(already_stable.join(', '), :green, :bold)}") unless $opts[:quiet]
+                       end
+
+                       if $opts[:prestable]
+                               msg = "Arch Security Liaisons, please test the 
attached ebuild and report it stable on this bug.\n"
+                       elsif $opts[:liaisons] and not $opts[:prestable]
+                               msg = "Arch Security Liaisons, please test and 
mark stable:\n"
+                       else
+                               msg = "Arches, please test and mark stable:\n"
+                       end
+
+                       if not $opts[:prestable]
+                               msg += "=%s-%s\n" % [metadata[:package], 
best_version]
+                       end
+
+                       msg += "Target keywords : \"%s\"\n" % 
metadata[:stable_arches].join(' ')
+
+                       if already_stable.length > 0 and not $opts[:prestable]
+                               msg += "Already stable  : \"%s\"\n" % 
(already_stable.join(' '))
+                               msg += "Missing keywords: \"%s\"\n" % 
(metadata[:stable_arches] - already_stable).join(' ')
+                       end
+
+                       puts
+                       puts msg
+                       puts
+
+                       if $opts[:liaisons]
+                               require File.join(File.dirname(__FILE__), 
'liaisons')
+                               cc_list = need_stable.map {|arch| 
@liaisons[arch]}.flatten.map {|liaison| "#{liaison}@gentoo.org"}
+                       else
+                               cc_list = need_stable.map {|arch| 
"#{arch}@gentoo.org" }
+                       end
+                       puts "CC: %s" % cc_list.join(',')
+                       exit if bug == nil
+
+                       bugi = bug_info(bug)
+                       new_whiteboard = update_whiteboard(bugi['whiteboard'])
+
+                       puts "Whiteboard: '%s' -> '%s'" % [bugi['whiteboard'], 
new_whiteboard]
+                       puts
+
+                       if $opts[:force] or $ui.agree('Continue? (yes/no)')
+                               update_bug(bug, new_whiteboard, cc_list, msg)
+                       end
+               end
+
+               # Collects metadata information from equery meta
+               def get_metadata(ebuild = Dir.pwd.split('/').last(2).join('/'))
+                       keywords = IO.popen("equery --no-color --no-pipe meta 
--keywords #{ebuild}")
+                       result = {:slots => {}, :keywords => {}, :stable_arches 
=> [], :versions => []}
+
+                       keywords.lines.each do |line|
+                               if line =~ /^ \* (\S*?)\/(\S*?) \[([^\]]*)\]$/
+                                       result[:package] = "#{$1}/#{$2}"
+                                       result[:repo] = $3
+                                       next
+                               end
+
+                               if line =~ /^(.*?):(.*?):(.*?)$/
+                                       version, slot, kws = $1, $2, $3
+                                       result[:versions] << version
+                                       result[:slots][slot] = [] unless 
result[:slots].include? slot
+                                       result[:slots][slot] << version
+                                       result[:keywords][version] = []
+
+                                       kws.strip.split(' ').each do |arch|
+                                               result[:keywords][version] << 
arch
+
+                                               if arch =~ /^[^~]*$/
+                                                       result[:stable_arches] 
<< arch
+                                               end
+                                       end
+
+                                       result[:keywords][version].sort!
+                                       next
+                               end
+
+                               raise RuntimeError, "Invalid line in equery 
output. Aborting."
+                       end
+
+                       result[:stable_arches].uniq!
+                       result[:stable_arches].sort!
+                       result
+               end
+
+               # Tries to find the best version following the needed 
specification
+               def find_best_version(metadata, slot, version)
+                       if slot == nil and version == nil
+                               return metadata[:versions].reject {|item| item 
=~ /^9999/}.last
+                       elsif slot == nil
+                               return version
+                       else
+                               if version == nil
+                                       return metadata[:slots][slot].reject 
{|item| item =~ /^9999/}.last
+                               elsif metadata[:slots][slot].include?(version)
+                                       return version
+                               else
+                                       return false
+                               end
+                       end
+               end
+
+               def update_whiteboard(old_wb)
+                       old_wb.gsub(/(ebuild\+?|upstream\+?|stable)\??/, 
'stable').gsub(/stable\/stable/, 'stable')
+               end
+
+               def update_bug(bug, whiteboard, cc_list, comment)
+                       i("Updating bug #{bug}...")
+                       client = xmlrpc_client
+                       did_retry = false
+
+                       begin
+                               result = client.call('Bug.update', {
+                                       'ids' => [Integer(bug)],
+                                       'whiteboard' => whiteboard,
+                                       'cc' => {'add' => cc_list},
+                                       'keywords' => {'add' => 'STABLEREQ'},
+                                       'status' => 'IN_PROGRESS',
+                                       'comment' => {'body' => comment}
+                               })
+
+                               i("done!")
+                               return true
+                       rescue XMLRPC::FaultException => e
+                               if did_retry
+                                       e "Failure updating bug information: 
#{e.message}"
+                                       return false
+                               end
+
+                               if e.faultCode == 410
+                                       log_in
+                                       did_retry = true
+                                       retry
+                               else
+                                       e "Failure updating bug information: 
#{e.message}"
+                               end
+                       end
+               end
+
+               def bug_info(bugno)
+                       client = xmlrpc_client
+                       did_retry = false
+
+                       begin
+                               result = client.call('Bug.get', {'ids' => 
[Integer(bugno)]})
+                               result['bugs'].first
+                       rescue XMLRPC::FaultException => e
+                               if did_retry
+                                       e "Failure reading bug information: 
#{e.message}"
+                                       return false
+                               end
+
+                               if e.faultCode == 410
+                                       log_in
+                                       did_retry = true
+                                       retry
+                               else
+                                       e "Failure reading bug information: 
#{e.message}"
+                               end
+                       end
+               end
+
+               def log_in
+                       client = xmlrpc_client
+
+                       if $opts[:username] == nil
+                               user = $ui.ask("Bugzilla login: ")
+                       else
+                               user = $opts[:username]
+                       end
+
+                       password = $ui.ask("Password: ") {|q| q.echo = false}
+
+                       begin
+                               i("Logging in...")
+                               result = client.call('User.login', {
+                                       'login' => user,
+                                       'password' => password
+                               })
+
+                               cookie_file = File.join(ENV['HOME'], 
'.gensec-target-auth')
+                               FileUtils.rm(cookie_file) if 
File.exist?(cookie_file)
+                               FileUtils.touch(cookie_file)
+                               File.chmod(0600, cookie_file)
+                               File.open(cookie_file, 'w') {|f| f.write 
client.cookie }
+
+                               return true
+                       rescue XMLRPC::FaultException => e
+                               e "Failure logging in: #{e.message}"
+                               return false
+                       end
+               end
+
+               def xmlrpc_client
+                       client = XMLRPC::Client.new('bugs.gentoo.org', 
'/xmlrpc.cgi', 443, nil, nil, nil, nil, true)
+                       client.http_header_extra = {'User-Agent' => "Target/2.0 
(arch CC tool; http://security.gentoo.org/)"}
+
+                       cookie_file = File.join(ENV['HOME'], 
'.gensec-target-auth')
+                       if File.readable? cookie_file
+                               client.cookie = File.read(cookie_file)
+                       end
+
+                       client
+               end
+
+               # Output and misc methods
+               def i(str)
+                       $ui.say($ui.color(" * ", :green, :bold) + str)
+               end
+
+               def w(str)
+                       $ui.say($ui.color(" * ", :yellow, :bold) + str)
+               end
+
+               def e(str)
+                       $ui.say($ui.color(" * ", :red, :bold) + str)
+                       exit 1
+               end
+
+               def filter_unstable(ary)
+                       ary.reject {|item| item =~ /^~/}
+               end
+       end
+end
+
+if __FILE__ == $0
+       include GenSec::Target
+       main(ARGV)
+end

Reply via email to