diff --git a/openbis_all/source/ruby/dashboard/jira b/openbis_all/source/ruby/dashboard/jira index dc5897b149a4e4bc221c17dd502aa730b2c4f4e9..6310000f57505097b33b8df6d1003547b2b68b9e 100755 --- a/openbis_all/source/ruby/dashboard/jira +++ b/openbis_all/source/ruby/dashboard/jira @@ -85,6 +85,12 @@ class Issue return self.fields["timetracking"]["remainingEstimateSeconds"] ? self.fields["timetracking"]["remainingEstimateSeconds"] : 0 end + def time_spent + # The result is preformatted by JIRA + return "" if self.fields["timetracking"].nil? + return self.fields["timetracking"]["timeSpent"] ? self.fields["timetracking"]["timeSpent"] : "" + end + def status return self.fields["status"] ? self.fields["status"]["name"] : nil end @@ -97,6 +103,10 @@ class Issue return self.fields["summary"] end + def assignee + return self.fields["assignee"] ? self.fields["assignee"]["name"] : nil + end + def fix_version fix_versions = self.fields["fixVersions"] return "Unscheduled" if fix_versions.nil? @@ -104,6 +114,12 @@ class Issue return fix_versions[0]["name"] end + def fix_versions + fix_versions = self.fields["fixVersions"] + return "Unscheduled" if fix_versions.nil? + return fix_versions.collect { | each | each["name"] }.join(" ") + end + def transitions @issue["transitions"] end @@ -199,6 +215,15 @@ module JiraHelpers return `#{update_cmd}` end + def JiraHelpers.add_fix_version(issue, fixversion) + fix_versions = issue.fields["fixVersions"] + fix_versions = [] unless fix_versions + fix_versions.push({"name" => fixversion }) + update_data = { "fields" => { "fixVersions" => fix_versions } } + update_cmd = "curl -s --cookie #{$jira_cookie_path} -H 'Content-Type: application/json' '#{$jira_api_url}/issue/#{issue.key}' -X PUT -d '#{JSON.generate(update_data)}'" + return `#{update_cmd}` + end + def JiraHelpers.transition(bis_key, trans_id) transition_data = { "transition" => { "id" => "#{trans_id}"} @@ -246,6 +271,14 @@ module JiraHelpers end end +# +# A module that simplifies interacting with git +module GitHelpers + def GitHelpers.list_commits(key) + return `git cisd log --grep="#{key}" | sed -n '/git-svn-id/p' | sed -E 's/.*trunk@([0-9]+).*/r\\1/'` + end +end + # # A module that implements some helpful operations # @@ -324,6 +357,8 @@ DESCRIPTION A simple api for jira. Login as jira user and get some info. The available commands are: login asks for login and password to jira and stores a session cookie + + session check if the session cookie is still valid sprint S133 get the information about the given sprint @@ -340,7 +375,7 @@ DESCRIPTION rank SP-121 SP-122 ranks SP-121 after SP-122 - kanban 133 returns a Kanban list of the issues in S133 + kanban1304 133 returns a Kanban list of the issues in S133 designated for the 1304 release create 133 create issues for all BIS issues designated as next sprint create 133 BIS-111 @@ -354,6 +389,11 @@ DESCRIPTION move 133 Set the fix version to 133 for all issues that are connected to the BIS issues move 133 SP-452 Set the fix version of SP-452 to 133 + + fix-add BIS-111 13.04.1 Add the fix version of 13.04.1 to the BIS-111 + + port1304 155 returns a list of issues that need to be ported to the release branch + port1304 155 commit returns a list of commits that need to be ported to the release branch eos end end @@ -649,9 +689,157 @@ class ListSprint < LoggedInCommand end # -# Lists the issues in the kanban cycle +# Lists the issues in the kanban cycle by combining information from the BIS project and SP project +# +class ListKanban1304 < LoggedInCommand + def initialize + super + @sprintNumber = InputHelpers.sprint_name(ARGV[1]) + @restrict_to_release = true + end + + def description + return "kanban1304 #{@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) OR (status in (Resolved, Closed) AND fixVersion = #{@sprintNumber}) ORDER BY \"Global Rank\" ASC" + bis_query= "project=BIS AND labels in (\"12.xx_REL\", \"13.04.X\") AND status not in (Resolved, Closed) OR (status in (Resolved, Closed) AND fixVersion = #{@sprintNumber}) ORDER BY \"Global Rank\" ASC" if @restrict_to_release + bis_issues = JiraHelpers.search_full(bis_query, @silent) + enrich_sp_issue_dict(JiraHelpers.retrieve_implementors(bis_issues, @silent)) + + print_issues_table("BIS", bis_issues) + print_unseen_sp_issues_table(sp_issues) + + puts ("=" * 12) + + # Nothing to show + issue_count = bis_issues.length + return "#{issue_count} 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 + @seen_sp_issues = [].to_set + end + + # + # Take those issues that are not yet resolved / closed and add them to the sp issues dict + # + def enrich_sp_issue_dict(implementors) + implementors.each do | issue | + sp = issue.key + next if @sp_issue_dict[sp] + + status = issue.status + @sp_issue_dict[sp] = issue unless issue.resolved_or_closed? + end + end + + def print_unseen_sp_issues_table(full_issues) + unseen_issues = full_issues.select do | issue | + sp = issue.key + !@seen_sp_issues.include?(sp) + end + + return if unseen_issues.length < 1 + + puts ("=" * 12) + puts "SP Missed" + puts ("-" * 12) + header = "%12s\t%12s\t%6s\t%8s\t%8s\t%8s\t%s" % ["Key", "SP", "Time", "Devel", "Tester", "Status", "Summary"] + puts header + subtotal = 0.0 + unseen_issues.each do | issue | + sp = issue.key + + key = "----" + key = issue.implements.key unless issue.implements.nil? + time_spent = issue.time_spent + assignee = issue.assignee + tester = issue.tester + summary = issue.summary + status = issue.status + # Tasks that are resolved can be considered to have 0 time remaining + status = issue.status + row = "%12s\t%12s\t%6s\t%8s\t%8s\t%8s\t%s" % [key, sp, time_spent, assignee, tester, status, summary] + puts row + end + end + + def print_issues_table(title, full_issues) + return if full_issues.length < 1 + + puts ("=" * 12) + puts title + puts ("-" * 12) + header = "%12s\t%12s\t%6s\t%8s\t%8s\t%8s\t%s" % ["Key", "SP", "Time", "Devel", "Tester", "Status", "Summary"] + puts header + subtotal = 0.0 + full_issues.each do | issue | + key = issue.key + summary = issue.summary + parent = issue.fields["parent"] + parent = parent["key"] unless parent.nil? + summary = "#{parent} / #{summary}" unless parent.nil? + + implementedby = [] + issue.implemented_by.each do | sp_issue | + sp = sp_issue.key + # We are only interested in links to issues in the specified sprint + implementedby << sp if @sp_issue_dict[sp] + end + + if implementedby.length < 1 + row = "%12s\t%12s\t%6s\t%8s\t%8s\t%8s\t%s" % [key, "", "", "", "", "", "[Unscheduled] #{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? + next if @seen_sp_issues.include?(sp) + + spfields = spissue.fields + time_spent = spissue.time_spent + assignee = spissue.assignee + tester = spissue.tester + + # Tasks that are resolved can be considered to have 0 time remaining + fix_version = spissue.fix_version + issue_in_different_sprint = fix_version != @sprintNumber + status = spissue.status + if index < 1 + issue_summary = summary + issue_summary = "[#{fix_version}] #{issue_summary}" if issue_in_different_sprint + row = "%12s\t%12s\t%6s\t%8s\t%8s\t%8s\t%s" % [key, sp, time_spent, assignee, tester, status, issue_summary] + else + issue_summary = "\"" + issue_summary = "[#{fix_version}] #{issue_summary}" if issue_in_different_sprint + row = "%12s\t%12s\t%6s\t%8s\t%8s\t%8s\t%s" % ["\"", sp, time_spent, assignee, tester, status, issue_summary] + end + puts row + @seen_sp_issues.add(sp) + end + end + end +end + +# +# Lists the issues in the kanban cycle by referring only to the BIS project # -class ListKanban < LoggedInCommand +class ListKanbanOld < LoggedInCommand def initialize super @sprintNumber = InputHelpers.sprint_name(ARGV[1]) @@ -719,9 +907,14 @@ class PlanSprint < LoggedInCommand swe_issues = JiraHelpers.search_full(swe_query, @silent) enrich_sp_issue_dict(JiraHelpers.retrieve_implementors(swe_issues, @silent)) + ysc_query= "project = YSC AND status not in (Resolved, Closed) AND \"Next Sprint\" = YES ORDER BY \"Global Rank\" ASC" + ysc_issues = JiraHelpers.search_full(ysc_query, @silent) + enrich_sp_issue_dict(JiraHelpers.retrieve_implementors(ysc_issues, @silent)) + print_issues_table("BIS", bis_issues) print_issues_table("CCS", ccs_issues) - print_issues_table("SWE", swe_issues) + print_issues_table("SWE", swe_issues) + print_issues_table("YSC", ysc_issues) print_unseen_sp_issues_table(sp_issues) puts ("=" * 12) @@ -780,6 +973,8 @@ class PlanSprint < LoggedInCommand end def print_issues_table(title, full_issues) + return if full_issues.length < 1 + puts ("=" * 12) puts title puts ("-" * 12) @@ -834,7 +1029,7 @@ class PlanSprint < LoggedInCommand end end @total = @total + subtotal - end + end end # @@ -1190,7 +1385,7 @@ class FinishSprint < LoggedInCommand print " Already Resolved" end - JiraHelpers.set_fix_version(issue.key, @sprintNumber) + JiraHelpers.add_fix_version(issue, @sprintNumber) print ", Fix version #{@sprintNumber}" end @@ -1336,6 +1531,225 @@ class MoveSprint < LoggedInCommand end end +# +# Add a fix version to an issue +# +class FixVersionAdd < LoggedInCommand + def initialize + super + @issue_key = ARGV[1] + @fix_version = ARGV[2] + end + + def description + return "fix-add #{@issue_key} #{@fix_version}" + end + + + def run_logged_in + issue = JiraHelpers.issue(@issue_key) + return "fix [issue key] [fix version]" unless issue + return "#{issue.key} fix versions: #{issue.fix_versions}" unless @fix_version + ans = JiraHelpers.add_fix_version(issue, @fix_version) + puts ans + issue = JiraHelpers.issue(@issue_key) + return "#{issue.key} fix versions: #{issue.fix_versions}" + end +end + +# +# Lists the issues in the sprint that need to be ported to the release branch +# +class ListPort1304 < LoggedInCommand + def initialize + super + @sprintNumber = InputHelpers.sprint_name(ARGV[1]) + end + + def description + return "port1304 #{@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 fixVersion = #{@sprintNumber} AND labels in (\"12.xx_REL\", \"13.04.X\") ORDER BY \"Global Rank\" ASC" + bis_issues = JiraHelpers.search_full(bis_query, @silent) + enrich_sp_issue_dict(JiraHelpers.retrieve_implementors(bis_issues, @silent)) + + self.print_issues_table("BIS", bis_issues) + self.print_unseen_sp_issues_table(sp_issues) + return "Invoke to get commits: port1304 #{@sprintNumber} commits" + 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 + @seen_sp_issues = [].to_set + end + + # + # Take those issues that are not yet resolved / closed and add them to the sp issues dict + # + def enrich_sp_issue_dict(implementors) + implementors.each do | issue | + sp = issue.key + next if @sp_issue_dict[sp] + + status = issue.status + @sp_issue_dict[sp] = issue unless issue.resolved_or_closed? + end + end + + def print_issues_table_old(full_issues) + header = "%8s\t%12s\t%s" % ["Key", "Status", "Commits"] + puts header + time_remaining = 0.0 + full_issues.each do | issue | + key = issue.key + status = issue.status + commits = "" + row = "%8s\t%12s\t%s" % [key, status, commits] + puts row + + end + print " ", ("-" * 27), "\n" + print "#{full_issues.length} issues", "\n" + end + + def print_issues_table(title, full_issues) + return if full_issues.length < 1 + + puts ("=" * 12) + puts title + puts ("-" * 12) + header = "%12s\t%12s\t%6s\t%s" % ["Key", "SP", "Port?", "Summary"] + puts header + full_issues.each do | issue | + key = issue.key + summary = issue.summary + parent = issue.fields["parent"] + parent = parent["key"] unless parent.nil? + summary = "#{parent} / #{summary}" unless parent.nil? + + implementedby = [] + issue.implemented_by.each do | sp_issue | + sp = sp_issue.key + # We are only interested in links to issues in the specified sprint + implementedby << sp if @sp_issue_dict[sp] + end + + if implementedby.length < 1 + row = "%12s\t%12s\t%6s\t%s" % [key, "----", "NO", 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? + next if @seen_sp_issues.include?(sp) + + spfields = spissue.fields + time = spissue.time + + # Tasks that are resolved can be considered to have 0 time remaining + fix_version = spissue.fix_version + issue_in_different_sprint = fix_version != @sprintNumber + status = spissue.status + if index < 1 + issue_summary = summary + issue_summary = "[#{fix_version}] #{issue_summary}" if issue_in_different_sprint + row = "%12s\t%12s\t%6s\t%s" % [key, sp, "YES", issue_summary] + else + issue_summary = "\"" + issue_summary = "[#{fix_version}] #{issue_summary}" if issue_in_different_sprint + row = "%12s\t%12s\t%6s\t%s" % ["\"", sp, "YES", issue_summary] + end + puts row + @seen_sp_issues.add(sp) + end + end + end + + def print_unseen_sp_issues_table(full_issues) + puts ("=" * 12) + puts "Not Ported" + puts ("-" * 12) + header = "%12s\t%12s\t%6s\t%s" % ["Key", "SP", "Port?", "Summary"] + puts header + full_issues.each do | issue | + sp = issue.key + next if @seen_sp_issues.include?(sp) + + key = "----" + key = issue.implements.key unless issue.implements.nil? + time = issue.time + summary = issue.summary + # Tasks that are resolved can be considered to have 0 time remaining + status = issue.status + row = "%12s\t%12s\t%6s\t%s" % [key, sp, "NO", summary] + puts row + end + end + +end + +# +# Lists the commits in the sprint that need to be ported to the stage branch +# +class ListPortCommits1304 < LoggedInCommand + def initialize + super + @sprintNumber = InputHelpers.sprint_name(ARGV[1]) + end + + def description + return "port1304 #{@sprintNumber}" + end + + + def run_logged_in + query = "project=BIS AND fixVersion = #{@sprintNumber} AND labels in (\"12.xx_REL\", \"13.04.X\") 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%s" % ["Key", "Status", "Commits"] + puts header + time_remaining = 0.0 + full_issues.each do | issue | + key = issue.key + status = issue.status + commits = self.list_commits(issue) + row = "%8s\t%12s\t%s" % [key, status, commits] + puts row + + end + print " ", ("-" * 27), "\n" + end + + def list_commits(issue) + key = issue.key + # Restrict outselves to the svn reveion number lines and then print the revision number prefixed with 'r' + commits = GitHelpers.list_commits(key) + commits = commits.split(/\n/).join(',') + return commits + end +end + + def get_command return Help.new if ARGV.length < 1 @@ -1347,12 +1761,14 @@ def get_command when "plan" then PlanSprint.new when "cust" then CustIssues.new when "rank" then ARGV.length > 2 ? RankIssue.new : RankSprint.new - when "kanban" then ListKanban.new + when "kanban1304" then ListKanban1304.new when "create" then CreateSprint.new when "start" then StartSprint.new when "finish" then FinishSprint.new when "move" then ARGV.length > 2 ? MoveIssue.new : MoveSprint.new when "session" then SessionValid.new + when "fix-add" then FixVersionAdd.new + when "port1304" then ARGV.length > 2 ? ListPortCommits1304.new : ListPort1304.new else Help.new end