module Tracker::Cmd

Constants

GIT_CMD
GIT_JSON_FORMAT
GIT_OPTS

Public Class Methods

ack(directory, opts={}) click to toggle source
# File lib/command.rb, line 273
def self.ack(directory, opts={}); action(:ack, directory, opts); end
action(name, directory, options={}) click to toggle source

Method perform given action on GIT branch with recorded commits. The patches need to be recorded on Tracker server to perfom any action.

  • name - Action name (:ack, :nack, :push)

  • directory - If given, cmd app will 'chdir' into that directory (default: nil)

# File lib/command.rb, line 224
def self.action(name, directory, options={})
  if options[:set]
    begin
      RestClient.post(
        config[:url] + ('/set/%s/%s' % [options[:set], name]),
        {
          :message => options[:message]
        },
        {
          :content_type => 'application/json',
          'Authorization' => "Basic #{basic_auth}"
        }
      )
      'All patches in set %s marked as %s' % [options[:set], name.to_s.upcase]
    rescue => e
      '[ERR] %s' % e.message
    end
  else
    patches = JSON::parse(patches_to_json(directory))
    messages = patches.pop
    patches_counter = 0
    patches.each do |p|
      if messages[p['hashes']['commit']]['full_message'][/TrackedAt: (.*)./m]
        tracker_commit_url = $1.chop
      else
        tracker_commit_url = config[:url] + '/patch/' + p['hashes']['commit']
        warn "[warn] Patches not applied by tracker. Using current commit hash."
      end
      begin
        RestClient.post(
          tracker_commit_url + '/' + name.to_s,
          {
            :message => options[:message]
          },
          {
            :content_type => 'application/json',
            'Authorization' => "Basic #{basic_auth}"
          }
        )
        puts "\e[1m%s\e[0m send for \e[1m%s\e[0m" % [name.to_s.upcase, tracker_commit_url]
        patches_counter += 1
      rescue => e
        puts '[ERR] %s' % e.message
      end
    end
    '%i patches status updated.' % patches_counter
  end
end
apply(directory, commit_id) click to toggle source
# File lib/command.rb, line 93
def self.apply(directory, commit_id)
  patch_body = download_patch_body(commit_id)
  File.open(File.join(directory, "#{commit_id}.patch"), 'w') { |f| f.write patch_body }
  print '[%s] Are you sure you want to apply patch to current branch? [Y/n]' % commit_id
  exit if (STDIN.gets.chomp) == 'n'
  git_cmd("git am #{commit_id}.patch", directory)
end
config(action=:get, opts={}) click to toggle source

Retrieve/Set the configuration from YAML file, if YAML file does not exists, use the default_configuration instead

# File lib/command.rb, line 45
def self.config(action=:get, opts={})
  configuration(YAML.load_file(opts[:file])) if (action == :set) && File.exists?(opts[:file])
  configuration || configuration(default_configuration)
end
configuration(conf=nil) click to toggle source

Set/Get configuration for the command-line client

# File lib/command.rb, line 37
def self.configuration(conf=nil); @configuration ||= conf; end
default_configuration() click to toggle source
# File lib/command.rb, line 21
def self.default_configuration
  {
    :url => 'http://localhost:9292',
    :user => 'mfojtik@redhat.com',
    :password => 'test123'
  }
end
download(directory, patchset_id, branch=nil) click to toggle source
# File lib/command.rb, line 161
def self.download(directory, patchset_id, branch=nil)
  patches = []
  begin
    response = RestClient.get(
      config[:url] + ('/set/%s' % patchset_id),
      {
        'Accept' => 'application/json',
        'Authorization' => "Basic #{basic_auth}"
      }
    )
    patches = JSON::parse(response)['patches']
  rescue => e
    puts "ERR: #{e.message}"
    exit
  end
  counter = 0
  if !branch.nil?
    puts git_cmd("git checkout -b #{branch}", directory)
    exit 1 if $? != 0
  end
  patches.each do |commit|
    patch_filename = File.join(directory, "#{counter}-#{commit}.patch")
    File.open(patch_filename, 'w') { |f| f.puts download_patch_body(commit) }
    if !branch.nil?
      begin
        puts git_cmd("git am #{patch_filename}", directory)
      rescue ChildCommandError => e
        puts git_cmd "git am --abort", directory
        puts git_cmd "git checkout master", directory
        puts git_cmd "git branch -D #{branch}", directory
        puts "ERROR: #{e.message}. Reverting back to 'master' branch."
        puts "Downloaded patch saved to :#{patch_filename}"
        exit 1
      end
      FileUtils.rm_f(patch_filename)
    end
    counter += 1
  end
  if !branch.nil?
    "#{counter} patches successfully applied"
  else
    "#{counter} patches successfully downloaded"
  end
end
download_patch_body(commit_id) click to toggle source
# File lib/command.rb, line 147
def self.download_patch_body(commit_id)
  begin
    RestClient.get(
      config[:url] + ('/patch/%s/download' % commit_id),
      {
        :content_type => 'text/plain',
        'Authorization' => "Basic #{basic_auth}"
      }
    )
  rescue => e
    puts "[ERR] #{e.message}"
  end
end
list(value, opts={}) click to toggle source
# File lib/command.rb, line 310
def self.list(value, opts={})
  filter = ''
  if !value.nil?
    if ['new', 'ack', 'nack', 'push'].include? value
      filter += '?filter=status&filter_value=%s' % value
    elsif !opts[:id].nil?
      filter += '?filter=%s&filter_value=%s' % [opts[:id], value]
    else
      puts "[ERR] To use filters other than status, you must use -i FILTER_NAME parameter"
      exit 1
    end
  end
  response = RestClient.get(
    config[:url] + ('/set%s' % filter),
    {
      'Accept' => 'application/json'
    }
  )
  set_arr = JSON::parse(response)
  puts
  set_arr.each do |set|
    puts "[%s][%s] \e[1m%s\e[0m (%s patches by %s)" % [
      set['id'],
      set['status'].upcase,
      set['first_patch_message'],
      set['num_of_patches'],
      set['author']
    ]
  end
  ''
end
nack(directory, opts={}) click to toggle source
# File lib/command.rb, line 274
def self.nack(directory, opts={}); action(:nack, directory, opts); end
note(directory, opts={}) click to toggle source
# File lib/command.rb, line 276
def self.note(directory, opts={}); action(:note, directory, opts); end
obsolete_patchset(patchset_id) click to toggle source
# File lib/command.rb, line 206
def self.obsolete_patchset(patchset_id)
  RestClient.post(
    config[:url] + ('/patchset/%s/obsolete' % patchset_id), '',
    {
      :content_type => 'application/json',
      'Authorization' => "Basic #{basic_auth}"
    }
  )
  puts 'Set %s obsoleted.' % patchset_id
end
patches_to_json(directory=nil) click to toggle source

Read commits between origin/master..HEAD and convert them to JSON string

  • directory - If given, cmd app will 'chdir' into that directory (default: nil)

# File lib/command.rb, line 54
def self.patches_to_json(directory=nil)
  patches_in_json = git_cmd(GIT_CMD, directory)
  commit_messages_raw = git_cmd('git log --pretty=oneline origin/master..HEAD', directory)
  commit_messages = commit_messages_raw.each_line.map.inject({}) do |result, line|
    hash, message = line.split(' ', 2)
    full_message = git_cmd("git rev-list --format=%B --max-count=1 #{hash}", directory)
    result[hash] = { :msg => message.strip, :full_message => full_message }
    result
  end
  "[#{patches_in_json}#{JSON::dump(commit_messages)}]"
end
push(directory, opts={}) click to toggle source
# File lib/command.rb, line 275
def self.push(directory, opts={}); action(:push, directory, opts); end
record(directory, opts={}) click to toggle source

Method will call patches_to_json and POST the JSON array to Tracker server. Authentication stored in config is used.

  • directory - If given, cmd app will 'chdir' into that directory (default: nil)

# File lib/command.rb, line 71
def self.record(directory, opts={})
  number_of_commits = JSON::parse(patches_to_json(directory)).pop.size
  begin
    response = RestClient.post(
      config[:url] + '/set',
      patches_to_json(directory),
      {
        :content_type => 'application/json',
        'Authorization' => "Basic #{basic_auth}",
        'X-Obsoletes' => opts[:obsolete] || 'no'
      }
    )
    response = JSON::parse(response)
    output = "#{number_of_commits} patches were recorded to the tracker server"+
      " [\e[1m#{config[:url]}set/#{response['id']}\e[0m] [revision #{response['revision']}]"
    output += "\n" + upload(directory) if opts[:upload]
    output
  rescue => e
    e.message
  end
end
status(directory) click to toggle source
# File lib/command.rb, line 278
def self.status(directory)
  patches = JSON::parse(patches_to_json(directory))
  # Remove messages from Array
  patches.pop
  puts
  counter = 0
  patches.each do |p|
    begin
      response = RestClient.get(
        config[:url] + ('/patch/%s' % p['hashes']['commit']),
        {
          'Accept' => 'application/json',
          'Authorization' => "Basic #{basic_auth}"
        }
      )
      response = JSON::parse(response)
      puts "[%s] \e[1m%s\e[0m" % [
        response['status'].upcase,
        response['message']
      ]
      counter+=1
    rescue => e
      next if response == 'null'
      puts '[ERR][%s] %s' % [p['hashes']['commit'][-8, 8], e.message]
    end
  end
  if counter == 0
    "ERR: This branch is not recorded yet. ($ tracker record)\n\n"
  else
  end
end
upload(directory) click to toggle source
# File lib/command.rb, line 101
def self.upload(directory)
  diffs = git_cmd('git format-patch --stdout master', directory)
  patches = {}
  current_patch_commit = ''
  diffs.each_line do |line|
    if line =~ %r[^From (\w{40}) ]
      current_patch_commit = $1
      patches[current_patch_commit] = line
    else
      patches[current_patch_commit] += line
    end
  end
  upload_counter = 0
  patches.each do |commit, body|
    begin
      upload_patch_body(commit, body)
      upload_counter += 1
    rescue;end
  end
  '%i patches successfully uploaded' % [upload_counter]
end
upload_patch_body(commit_id, body) click to toggle source
# File lib/command.rb, line 123
def self.upload_patch_body(commit_id, body)
  # Inject TrackedAt header to the commit message
  body.sub!(/^---/m, "TrackedAt: #{config[:url].gsub(/\/$/, '')}/patch/#{commit_id}\n\n---")
  patch_file = Tempfile.new(commit_id)
  begin
    patch_file.write(body)
    patch_file.rewind
    RestClient.post(
      config[:url] + ('/patch/%s/body' % commit_id),
      {
        :diff => patch_file
      },
      {
        'Authorization' => "Basic #{basic_auth}"
      }
    )
  rescue => e
    puts "[ERR] Upload of #{commit_id} failed. (#{e.message})"
  ensure
    patch_file.close
    patch_file.unlink
  end
end

Private Class Methods

basic_auth() click to toggle source
# File lib/command.rb, line 368
def self.basic_auth
 Base64.encode64("#{config[:user]}:#{config[:password]}")
end
git_cmd(cmd, directory) click to toggle source

Execute GIT command ('git') in the specified directory. Method will then revert pwd to its original value and return command output in string.

  • cmd - GIT command to perform (eg. 'git log')

# File lib/command.rb, line 351
def self.git_cmd(cmd, directory)
  old_pwd = Dir.pwd
  result = ''
  begin
    Dir.chdir(directory)
    result = %x[#{cmd}]
    raise ChildCommandError.new("Child process returned #{$?}") if $? != 0
  rescue => e
    raise e if e.kind_of? ChildCommandError
    puts "ERROR: #{e.message}"
    exit(1)
  ensure
    Dir.chdir(old_pwd)
  end
  result
end