From b31f49b2dfaef7faa8b3e653f59edcdeb138872e Mon Sep 17 00:00:00 2001 From: cramakri <cramakri> Date: Tue, 5 Jun 2012 13:22:10 +0000 Subject: [PATCH] MINOR Initial commit of cramakri ruby scripts for getting hudson status and jira sprint information SVN: 25563 --- openbis_all/source/ruby/dashboard/hudson | 181 ++++++++++++ openbis_all/source/ruby/dashboard/jira | 348 +++++++++++++++++++++++ 2 files changed, 529 insertions(+) create mode 100755 openbis_all/source/ruby/dashboard/hudson create mode 100755 openbis_all/source/ruby/dashboard/jira diff --git a/openbis_all/source/ruby/dashboard/hudson b/openbis_all/source/ruby/dashboard/hudson new file mode 100755 index 00000000000..4683391d1d4 --- /dev/null +++ b/openbis_all/source/ruby/dashboard/hudson @@ -0,0 +1,181 @@ +#!/usr/bin/env ruby + +require 'rubygems' +require 'json' +require 'pp' + +# +# = A wrapper for scripting hudson +# + +# +# == Preferences +# + +# The url for Hudson +$hudson_url = 'http://bs-ci01.ethz.ch:8090' + +# The url for the Hudson api +$hudson_api_url = "#{$hudson_url}/api/json" + +# +# A module that implements some helpful operations +# +module HudsonHelpers + + # Return summary data for all jobs + def HudsonHelpers.jobs(silent) + ans = `curl -s '#{$hudson_api_url}'` + data = JSON.load(ans) + return data + end + + # Search and return the full data for the found objects + def HudsonHelpers.job(job, silent) + ans = `curl -s '#{$hudson_url}/job/#{job}/api/json'` + data = JSON.load(ans) + return data + end + +end + + + +# +# == Commands +# + +# +# The abstract superclass of commands +# +class HudsonCommand + + attr_accessor :silent + + def initialize + @silent = true + end + + # Return a description of the command to run + def description + return nil + end + + # Run the command and return the result + def run + return nil + end + + # Return true if the result should be printed. + # + # Default: print if the result is not empty. + def should_print_result(result) + return !result.empty? + end + + def print_jobs(jobs) + header = "%24s\t%8s\t%s" % ["Job", "Color", "URL"] + puts header + jobs.each do | job | + row = "%24s\t%8s\t%s" % [job["name"], job["color"], job["url"]] + puts row + end + end + +end + +# +# The help command +# +class Help < HudsonCommand + def description + return "help" + end + + def run + # show help + return "valid commands: broken, all" + end +end + +# +# The broken command +# +class Broken < HudsonCommand + def description + return "broken" + end + + def run + data = HudsonHelpers.jobs(@silent) + broken = data["jobs"].select { | each | each["color"] == "yellow" || each["color"] == "red" } + print_jobs(broken) + return "" + end +end + + +# +# The all command +# +class All < HudsonCommand + def description + return "all" + end + + def run + data = HudsonHelpers.jobs(@silent) + jobs = data["jobs"].select { | each | each["color"] != "disabled" && each["color"] != "aborted" } + print_jobs(jobs) + return "" + end +end + +# +# The job command +# +class Job < HudsonCommand + + def initialize + @job = ARGV[1] + end + + def description + return "job" + end + + def run + data = HudsonHelpers.job(@job, @silent) + return JSON.pretty_generate(data) + end +end + + +def get_command + return Broken.new if ARGV.length < 1 + + ans = case ARGV[0] + when "broken" then Broken.new + when "all" then All.new + when "job" then Job.new + else Help.new + end + + return ans +end + + +# +# == Main logic +# +cmd = get_command + +print cmd.description, "\n" + +result = cmd.run + +if cmd.should_print_result(result) + puts result +end + +print "Done.\n" diff --git a/openbis_all/source/ruby/dashboard/jira b/openbis_all/source/ruby/dashboard/jira new file mode 100755 index 00000000000..011f9197292 --- /dev/null +++ b/openbis_all/source/ruby/dashboard/jira @@ -0,0 +1,348 @@ +#!/usr/bin/env ruby + +require 'rubygems' +require 'json' +require 'pp' + +# +# = A wrapper for scripting jira +# +# Uses the Rest API: http://docs.atlassian.com/jira/REST/4.2.1/ +# +# ---- +# +# This script requires the json gem: +# +# http://flori.github.com/json/ +# +# Which can be installed: +# +# gem install json +# + +# +# == Preferences +# + +# The url for JIRA +$jira_url = 'https://jira-bsse.ethz.ch' + +# The url portion for the API +$jira_api_url = "#{$jira_url}/rest/api/2.0.alpha1" + +# Prefs path +$jira_prefs_path = File.expand_path('~/.jira') + +# Cookie location +$jira_cookie_path = File.join($jira_prefs_path, 'cookie.txt') + +# +# A module that implements some helpful operations +# +module JiraHelpers + def JiraHelpers.args(first_arg) + cmd = ARGV[first_arg .. -1].inject("") { | all, each | all + " " + each } + cmd.strip! + return cmd + end + + def JiraHelpers.search(query) + return `curl -s --get --cookie #{$jira_cookie_path} '#{$jira_api_url}/search' --data-urlencode 'jql=#{query}'` + end + + # Search and return the full data for the found objects + def JiraHelpers.search_full(query, silent) + print "Retrieving issues" unless silent + ans = JiraHelpers.search(query) + data = JSON.load(ans) + full_issues = [] + data["issues"].each do | issue | + print "." unless silent + issue_data = `curl -s --get --cookie #{$jira_cookie_path} #{issue["self"]}` + full_issues << JSON.load(issue_data) + end + print "\n" unless silent + return full_issues + end + + def JiraHelpers.session_valid? + ans = `curl -s --head --get --cookie #{$jira_cookie_path} '#{$jira_url }/auth/1/session'` + return $?.to_i == 0 + end +end + +# +# == Commands +# + +# +# The abstract superclass of commands +# +class JiraCommand + + attr_accessor :silent + + def initialize + @silent = true + end + + # Return a description of the command to run + def description + return nil + end + + # Run the command and return the result + def run + return nil + end + + # Return true if the result should be printed. + # + # Default: print if the result is not empty. + def should_print_result(result) + return !result.empty? + end + +end + +# +# The help command +# +class Help < JiraCommand + def description + return "help" + end + + def run + # show help + return "valid commands: login, sprint, dump, plan" + end +end + +# +# The login command +# +class Login < JiraCommand + + def initialize + super + @jira_user = ARGV[1] + end + + def description + return "login" + end + + def run + # The url portion for logging in + jira_login = 'secure/Dashboard.jspa?os_authType=basic' + Dir.mkdir($jira_prefs_path) unless File.exists?($jira_prefs_path) + return `curl --head -s -u #{@jira_user} --cookie-jar #{$jira_cookie_path} '#{$jira_url}/secure/Dashboard.jspa?os_authType=basic'` + end +end + +# +# Lists the issues in the sprint +# +class LoggedInCommand < JiraCommand + def run + Login.new.run unless File.exists?($jira_cookie_path) + Login.new.run unless JiraHelpers.session_valid? + return self.run_logged_in + end + + # For subclasses to implement + def run_logged_in + return "" + end +end + +# +# Shows the JSON for an issue -- useful for debugging +# +class DumpIssue < LoggedInCommand + def initialize + super + @issueUrl = ARGV[1] + end + + def description + return "dump #{@issueUrl}" + end + + + def run_logged_in + if @issueUrl.nil? + ans = JiraHelpers.search("project=SP AND fixVersion = S133 ORDER BY \"Global Rank\" ASC") + @issueUrl = JSON.load(ans)["issues"][0]["self"] + end + + ans = `curl -s --get --cookie #{$jira_cookie_path} '#{@issueUrl}'` + data = JSON.load(ans) + return JSON.pretty_generate(data) + end + +end + +# +# Lists the issues in the sprint +# +class ListSprint < LoggedInCommand + def initialize + super + @sprintNumber = ARGV[1] + @sprintNumber = "S" if @sprintNumber.nil? + @sprintNumber = "S" + @sprintNumber unless @sprintNumber.match("^[S|s].*") + end + + def description + return "sprint #{@sprintNumber}" + end + + + def run_logged_in + query = "project=SP AND fixVersion = #{@sprintNumber} ORDER BY \"Global Rank\" ASC" + full_issues = JiraHelpers.search_full(query, @silent) + self.print_issues_table(full_issues) + # Nothing to show + return "#{full_issues.length} issues" + end + + def print_issues_table(full_issues) + header = "%8s\t%12s\t%6s\t%12s\t%8s\t%s" % ["Key", "Implements", "Time", "Status", "Tester", "Summary"] + puts header + time_remaining = 0.0 + full_issues.each do | issue | + key = issue["key"] + fields = issue["fields"] + implements = nil + fields["links"]["value"].each { | link | implements = link["issueKey"] if "implements" == link["type"]["description"] } + time = fields["timetracking"]["value"] ? fields["timetracking"]["value"]["timeestimate"] : 0 + status = fields["status"]["value"]["name"] + tester = fields["customfield_10250"]["value"] ? fields["customfield_10250"]["value"]["name"] : nil + summary = fields["summary"]["value"] + row = "%8s\t%12s\t%5.1fh\t%12s\t%8s\t%s" % [key, implements, time / 60.0, status, tester, summary] + puts row + + # Tasks that are resolved can be considered to have 0 time remaining + time_remaining = time_remaining + time unless status == "Resolved" + end + print " ", ("-" * 27), "\n" + puts " Time Remaining : %.1fh" % (time_remaining / 60.0) + end +end + +# +# List the issues slated for a sprint in a form that is helpful for planning +# +class PlanSprint < LoggedInCommand + def initialize + super + @sprintNumber = ARGV[1] + @sprintNumber = "S" if @sprintNumber.nil? + @sprintNumber = "S" + @sprintNumber unless @sprintNumber.match("^S.*") + end + + def description + return "plan #{@sprintNumber}" + end + + + def run_logged_in + sp_query = "project=SP AND fixVersion = #{@sprintNumber} ORDER BY \"Global Rank\" ASC" + sp_issues = JiraHelpers.search_full(sp_query, @silent) + init_sp_issue_dict(sp_issues) + + bis_query= "project = BIS AND status not in (Resolved, Closed) AND \"Next Sprint\" = YES ORDER BY \"Global Rank\" ASC" + bis_issues = JiraHelpers.search_full(bis_query, @silent) + print_issues_table("BIS", bis_issues) + + ccs_query= "project = CCS AND status not in (Resolved, Closed) AND \"Next Sprint\" = YES ORDER BY \"Global Rank\" ASC" + ccs_issues = JiraHelpers.search_full(ccs_query, @silent) + print_issues_table("CCS", ccs_issues) + + # Nothing to show + return "#{bis_issues.length} issues" + end + + def init_sp_issue_dict(sp_issues) + @sp_issue_dict = {} + sp_issues.each do | issue | + key = issue["key"] + @sp_issue_dict[key] = issue + end + end + + def print_issues_table(title, full_issues) + puts ("=" * 12) + puts title + puts ("-" * 12) + header = "%8s\t%12s\t%12s\t%6s\t%s" % ["Subtotal", "Key", "SP", "Time", "Summary"] + puts header + subtotal = 0.0 + full_issues.each do | issue | + key = issue["key"] + fields = issue["fields"] + summary = fields["summary"]["value"] + + implementedby = [] + fields["links"]["value"].each do | link | + sp = link["issueKey"] + # We are only interested in links to issues in the specified sprint + next unless @sp_issue_dict[sp] + implementedby << sp if "is implemented by" == link["type"]["description"] + end + + if implementedby.length < 1 + row = "%8s\t%12s\t%12s\t%5.1fh\t%s" % ["----", key, "----", 0, summary] + puts row + next + end + + implementedby.each_with_index do | sp, index | + # print one row for each implemented by + spissue = @sp_issue_dict[sp] + next if spissue.nil? + spfields = spissue["fields"] + time = 0 + time = spfields["timetracking"]["value"]["timeestimate"] if spfields["timetracking"]["value"] != nil + if index < 1 + row = "%8.1fh\t%12s\t%12s\t%5.1fh\t%s" % [subtotal / 60.0, key, sp, time / 60.0, summary] + else + row = "%8.1fh\t%12s\t%12s\t%5.1fh\t%s" % [subtotal / 60.0, "\"", sp, time / 60.0, "\""] + end + puts row + + # Tasks that are resolved can be considered to have 0 time remaining + status = spfields["status"]["value"]["name"] + subtotal = subtotal + time unless status == "Resolved" + end + end + end +end + +def get_command + return Help.new if ARGV.length < 1 + + ans = case ARGV[0] + when "login" then Login.new + when "sprint" then ListSprint.new + when "dump" then DumpIssue.new + when "plan" then PlanSprint.new + else Help.new + end + + return ans +end + + +# +# == Main logic +# +cmd = get_command + +result = cmd.run + +if cmd.should_print_result(result) + puts result +end -- GitLab