jiraMule 0.1.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +2 -1
- data/README.md +2 -2
- data/Rakefile +20 -3
- data/TODO.taskpaper +0 -1
- data/jiraMule.gemspec +4 -2
- data/lib/JiraMule.rb +4 -2
- data/lib/JiraMule/StyleLoader.rb +323 -0
- data/lib/JiraMule/commands.rb +11 -0
- data/lib/{jiraMule → JiraMule}/commands/kanban.rb +30 -40
- data/lib/JiraMule/commands/open.rb +27 -0
- data/lib/JiraMule/commands/query.rb +74 -0
- data/lib/JiraMule/commands/statuses.rb +30 -0
- data/lib/{jiraMule → JiraMule}/jiraUtils.rb +19 -10
- data/lib/JiraMule/renderMagic.rb +30 -0
- data/lib/jiraMule/commands/goto.rb +0 -55
- data/lib/jiraMule/commands/logWork.rb +12 -8
- data/lib/jiraMule/http.rb +1 -1
- data/lib/jiraMule/version.rb +1 -1
- metadata +72 -46
- data/lib/JiraMule/Tempo.rb +0 -75
- data/lib/JiraMule/commands/githubImport.rb +0 -46
- data/lib/JiraMule/commands/timesheet.rb +0 -116
- data/lib/jiraMule/commands.rb +0 -20
- data/lib/jiraMule/commands/progress.rb +0 -68
- data/lib/jiraMule/commands/query.rb +0 -80
- data/lib/jiraMule/commands/release.rb +0 -44
- data/lib/jiraMule/commands/testReady.rb +0 -68
@@ -1,46 +0,0 @@
|
|
1
|
-
require 'octokit'
|
2
|
-
|
3
|
-
command 'github import' do |c|
|
4
|
-
c.syntax = %{jm github import <issue id>}
|
5
|
-
c.summary = %{Import a Github issue into Jira}
|
6
|
-
|
7
|
-
c.action do |args, options|
|
8
|
-
|
9
|
-
iid = args.shift
|
10
|
-
|
11
|
-
okey = $cfg['github.token']
|
12
|
-
if okey.nil? then
|
13
|
-
say_error "Missing github token!"
|
14
|
-
exit 2
|
15
|
-
end
|
16
|
-
|
17
|
-
oc = Octokit::Client.new(:access_token => okey)
|
18
|
-
if oc.nil? then
|
19
|
-
exit 3
|
20
|
-
end
|
21
|
-
oc.login
|
22
|
-
|
23
|
-
repo = oc.repo $cfg['github.project']
|
24
|
-
rel = repo.rels[:issues]
|
25
|
-
gissue = rel.get(:uri=>{:number=>iid})
|
26
|
-
if gissue.status != 200 then
|
27
|
-
say_error "Failed to get issue: #{gissue}"
|
28
|
-
exit 2
|
29
|
-
end
|
30
|
-
gissue = gissue.data
|
31
|
-
|
32
|
-
jira = JiraMule::JiraUtils.new(args, options)
|
33
|
-
# Create Issue
|
34
|
-
it = jira.checkIssueType
|
35
|
-
|
36
|
-
jissue = jira.createIssue(it.first[:name], gissue[:title], gissue[:body])
|
37
|
-
jira.verbose "Created #{jissue[:key]}"
|
38
|
-
|
39
|
-
# Link
|
40
|
-
jira.linkTo(jissue[:key], gissue[:html_url], gissue[:title])
|
41
|
-
|
42
|
-
end
|
43
|
-
end
|
44
|
-
alias_command :ghi, 'github import'
|
45
|
-
|
46
|
-
# vim: set ai et sw=2 ts=2 :
|
@@ -1,116 +0,0 @@
|
|
1
|
-
require 'chronic_duration'
|
2
|
-
require 'date'
|
3
|
-
require 'terminal-table'
|
4
|
-
require 'vine'
|
5
|
-
require 'JiraMule/jiraUtils'
|
6
|
-
require 'JiraMule/Tempo'
|
7
|
-
|
8
|
-
command :timesheet do |c|
|
9
|
-
c.syntax = 'jm timesheet [options]'
|
10
|
-
c.summary = 'Show work done this week'
|
11
|
-
c.description = %{Show the work done this week
|
12
|
-
}
|
13
|
-
c.example 'Show work done this week', 'jm timesheet'
|
14
|
-
c.example 'Show work done for project', 'jm timesheet --project DEV'
|
15
|
-
c.example 'Show work done for projects', 'jm timesheet --project DEV,PROD'
|
16
|
-
c.example 'Show work done for keys', 'jm timesheet 12 PROD-15 99 STG-6'
|
17
|
-
|
18
|
-
c.option '--project PROJECTS', Array, 'Limit results to specific projects'
|
19
|
-
|
20
|
-
#c.option '--prev COUNT', Integer, 'Look at previous weeks'
|
21
|
-
c.option '--starts_on DAY', String, 'Which day does the week start on'
|
22
|
-
|
23
|
-
c.action do |args, options|
|
24
|
-
options.default :starts_on => 'Mon'
|
25
|
-
|
26
|
-
jira = JiraMule::JiraUtils.new
|
27
|
-
tempo = JiraMule::Tempo.new
|
28
|
-
|
29
|
-
#Days Of Week
|
30
|
-
dows = [:Sun,:Mon,:Tue,:Wed,:Thu,:Fri,:Sat]
|
31
|
-
dayShift = dows.index{|i| options.starts_on.downcase.start_with? i.to_s.downcase}
|
32
|
-
workweek = dows.rotate dayShift
|
33
|
-
|
34
|
-
dayTo = Date.today
|
35
|
-
dayFrom = dayTo
|
36
|
-
while not dayFrom.wday == dayShift do
|
37
|
-
dayFrom = dayFrom.prev_day
|
38
|
-
end
|
39
|
-
while not dayTo.wday == (7-dayShift) do
|
40
|
-
dayTo = dayTo.next_day
|
41
|
-
end
|
42
|
-
|
43
|
-
# Get keys to get worklogs from
|
44
|
-
keys = jira.expandKeys(args)
|
45
|
-
if keys.empty? then
|
46
|
-
query = %{worklogAuthor = #{jira.username}}
|
47
|
-
query << %{ AND worklogDate >= #{dayFrom.iso8601}}
|
48
|
-
|
49
|
-
if options.project and not options.project.empty? then
|
50
|
-
query << ' AND ('
|
51
|
-
query << options.project.map{|p| %{project="#{p}"}}.join(' OR ')
|
52
|
-
query << ')'
|
53
|
-
end
|
54
|
-
|
55
|
-
keys = jira.getIssues(query, ['key']).map{|k| k[:key]}
|
56
|
-
end
|
57
|
-
jira.printVars(:keys=>keys) if $cfg['tool.verbose']
|
58
|
-
|
59
|
-
wls = tempo.workLogs(jira.username, dayFrom.iso8601, dayTo.iso8601)
|
60
|
-
|
61
|
-
# filter out entries not in keys.
|
62
|
-
selected = wls.select{|i| keys.include? i.access('issue.key')}
|
63
|
-
|
64
|
-
# build table; each row is a key. each column is hours worked in SSMTWTF
|
65
|
-
# multiple passes.
|
66
|
-
# 1: build hash by issue, each value is a hash by week day.
|
67
|
-
hrows = {}
|
68
|
-
selected.each do |isu|
|
69
|
-
k = isu.access('issue.key')
|
70
|
-
hrows[k] = {} unless hrows.has_key? k
|
71
|
-
ds = DateTime.parse(isu[:dateStarted]).to_date
|
72
|
-
dow = dows[ds.wday]
|
73
|
-
hrows[k][dow] = 0 unless hrows[k].has_key?(dow)
|
74
|
-
hrows[k][dow] += isu[:timeSpentSeconds]
|
75
|
-
end
|
76
|
-
|
77
|
-
totals = Hash[ *(workweek.map{|i| [i,0]}.flatten) ]
|
78
|
-
# 2: reshape into Array or Arrays.
|
79
|
-
rows = hrows.to_a.map do |row|
|
80
|
-
# row[0] is key, want to keep that.
|
81
|
-
# row[1] is a hash we want to sort and flatten.
|
82
|
-
row[1] = workweek.map do |d|
|
83
|
-
s = row[1][d]
|
84
|
-
if s.nil? then
|
85
|
-
nil
|
86
|
-
else
|
87
|
-
totals[d] += s
|
88
|
-
ChronicDuration.output(s, :format=>:short)
|
89
|
-
end
|
90
|
-
end
|
91
|
-
row.flatten
|
92
|
-
end
|
93
|
-
|
94
|
-
trow = workweek.map do |d|
|
95
|
-
s = totals[d]
|
96
|
-
if s.nil? then
|
97
|
-
nil
|
98
|
-
else
|
99
|
-
ChronicDuration.output(s, :format=>:short)
|
100
|
-
end
|
101
|
-
end
|
102
|
-
trow.insert(0, 'TOTALS:')
|
103
|
-
|
104
|
-
hdr = workweek.insert(0, :Key)
|
105
|
-
table = Terminal::Table.new do |t|
|
106
|
-
t.headings = hdr
|
107
|
-
t.rows = rows
|
108
|
-
t.add_separator
|
109
|
-
t.add_row trow
|
110
|
-
end
|
111
|
-
puts table
|
112
|
-
end
|
113
|
-
end
|
114
|
-
alias_command :ts, :timesheet
|
115
|
-
|
116
|
-
# vim: set sw=2 ts=2 :
|
data/lib/jiraMule/commands.rb
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
require 'JiraMule/commands/assign'
|
2
|
-
require 'JiraMule/commands/attach'
|
3
|
-
require 'JiraMule/commands/config'
|
4
|
-
require 'JiraMule/commands/githubImport'
|
5
|
-
require 'JiraMule/commands/goto'
|
6
|
-
require 'JiraMule/commands/kanban'
|
7
|
-
require 'JiraMule/commands/link'
|
8
|
-
require 'JiraMule/commands/logWork'
|
9
|
-
require 'JiraMule/commands/next'
|
10
|
-
require 'JiraMule/commands/progress'
|
11
|
-
require 'JiraMule/commands/query'
|
12
|
-
require 'JiraMule/commands/timesheet'
|
13
|
-
|
14
|
-
#require 'JiraMule/commands/release'
|
15
|
-
#require 'JiraMule/commands/testReady'
|
16
|
-
# difference between testReady and release is two things:
|
17
|
-
# 1. testReady also assigns
|
18
|
-
# 2. release also transitions
|
19
|
-
#
|
20
|
-
# So, merge all that into one command and have the parts be --options; maybe
|
@@ -1,68 +0,0 @@
|
|
1
|
-
require 'date'
|
2
|
-
require 'terminal-table'
|
3
|
-
require 'vine'
|
4
|
-
require 'JiraMule/jiraUtils'
|
5
|
-
|
6
|
-
command :progress do |c|
|
7
|
-
c.syntax = 'jm progress [options] [<key>]'
|
8
|
-
c.summary = 'Show progress on issues'
|
9
|
-
c.description = %{}
|
10
|
-
# Show only overdue (today > duedate)
|
11
|
-
# Show only unstarted (timespent == 0)
|
12
|
-
# Show only Started (timespent > 0)
|
13
|
-
c.option '-s', '--status STATUSES', Array, %{Which status to limit to. (default is "In Progress")}
|
14
|
-
|
15
|
-
c.example 'Show how current project is going', %{jm progress}
|
16
|
-
c.example 'Show how work on task 5 is going', %{jm progress 5}
|
17
|
-
|
18
|
-
c.action do |args, options|
|
19
|
-
options.default :status=>[]
|
20
|
-
|
21
|
-
jira = JiraMule::JiraUtils.new(args, options)
|
22
|
-
keys = jira.expandKeys(args)
|
23
|
-
if keys.empty? and options.status.empty? then
|
24
|
-
options.status << 'In Progress'
|
25
|
-
end
|
26
|
-
|
27
|
-
query = []
|
28
|
-
query << %{assignee = #{jira.username}}
|
29
|
-
query << %{project = #{jira.project}} if keys.empty?
|
30
|
-
unless keys.empty? then
|
31
|
-
query << '(' + keys.map{|k| "key=#{k}"}.join(' OR ') + ')'
|
32
|
-
end
|
33
|
-
unless options.status.empty? then
|
34
|
-
query << '(' + options.status.map{|s| %{status="#{s}"}}.join(' OR ') + ')'
|
35
|
-
end
|
36
|
-
query = query.join(' AND ')
|
37
|
-
|
38
|
-
#jira.printVars(:q=>query)
|
39
|
-
progresses = jira.getIssues(query, ['key', 'workratio', 'aggregatetimespent',
|
40
|
-
'duedate', 'aggregatetimeoriginalestimate'])
|
41
|
-
|
42
|
-
rows = progresses.map do |issue|
|
43
|
-
estimate = (issue.access('fields.aggregatetimeoriginalestimate') or 0)/3600.0
|
44
|
-
progress = (issue.access('fields.aggregatetimespent') or 0)/3600.0
|
45
|
-
due = issue.access('fields.duedate')
|
46
|
-
percent = issue.access('fields.workratio')
|
47
|
-
if percent < 0 then
|
48
|
-
estimate = progress if estimate == 0
|
49
|
-
percent = (progress / estimate * 100).floor
|
50
|
-
end
|
51
|
-
ret = [ issue[:key], "%.2f"%[estimate], "%.2f"%[progress],
|
52
|
-
%{#{"%.1f"%[percent]}%}, due ]
|
53
|
-
if progress > estimate or (not due.nil? and Date.new >= Date.parse(due)) then
|
54
|
-
ret.map!{|v| HighLine.color((v or ''), :bold)}
|
55
|
-
end
|
56
|
-
ret
|
57
|
-
end.sort{|a,b| a[0].sub(/^\D+(\d+)$/,'\1').to_i <=> b[0].sub(/^\D+(\d+)$/,'\1').to_i }
|
58
|
-
|
59
|
-
tbl = Terminal::Table.new :headings=>[:key, :estimated, :progress, :percent, :due], :rows=>rows
|
60
|
-
tbl.align_column(1, :right)
|
61
|
-
tbl.align_column(2, :right)
|
62
|
-
tbl.align_column(3, :right)
|
63
|
-
puts tbl
|
64
|
-
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
# vim: set sw=2 ts=2 :
|
@@ -1,80 +0,0 @@
|
|
1
|
-
require 'terminal-table'
|
2
|
-
require 'mustache'
|
3
|
-
|
4
|
-
command :query do |c|
|
5
|
-
c.syntax = 'jm query [options] query'
|
6
|
-
c.summary = 'Get the keys from a jira query'
|
7
|
-
c.description = 'Run a query. '
|
8
|
-
c.example 'Get Open issues and dump everything', %{jm query status=Open --fields "" --json}
|
9
|
-
c.option '--[no-]raw', 'Do not prefix query with project and assignee'
|
10
|
-
c.option '--[no-]json', 'Output json reply instead of summary'
|
11
|
-
c.option '--fields FIELDS', Array, 'Which fields to return.'
|
12
|
-
c.option '--all_fields', 'Return all fields'
|
13
|
-
c.option '--format STYLE', String, 'Format for keys'
|
14
|
-
c.option '--style STYLE', String, 'Which style to use'
|
15
|
-
|
16
|
-
c.action do |args, options|
|
17
|
-
options.default :json => false, :all_fields => false, :style => 'basic'
|
18
|
-
|
19
|
-
allOfThem = {
|
20
|
-
:basic => {
|
21
|
-
:fields => [:key, :summary],
|
22
|
-
:format => %{{{key}} {{summary}}},
|
23
|
-
},
|
24
|
-
:info => {
|
25
|
-
:fields => [:key, :description, :assignee, :reporter, :priority, :issuetype,
|
26
|
-
:status, :resolution, :votes, :watches],
|
27
|
-
:format => %{{{key}}
|
28
|
-
Reporter: {{reporter.displayName}}
|
29
|
-
Assignee: {{assignee.displayName}}
|
30
|
-
Type: {{issuetype.name}} ({{priority.name}})
|
31
|
-
Status: {{status.name}} (Resolution: {{resolution.name}})
|
32
|
-
Watches: {{watches.watchCount}} Votes: {{votes.votes}}
|
33
|
-
Description: {{description}}
|
34
|
-
}
|
35
|
-
},
|
36
|
-
}
|
37
|
-
|
38
|
-
theStyle = allOfThem[options.style.to_sym]
|
39
|
-
theStyle[:fields] = options.fields if options.fields
|
40
|
-
|
41
|
-
jira = JiraMule::JiraUtils.new(args, options)
|
42
|
-
args.unshift("assignee = #{jira.username} AND") unless options.raw
|
43
|
-
args.unshift("project = #{jira.project} AND") unless options.raw
|
44
|
-
q = args.join(' ')
|
45
|
-
if options.all_fields then
|
46
|
-
issues = jira.getIssues(q, [])
|
47
|
-
else
|
48
|
-
issues = jira.getIssues(q, theStyle[:fields])
|
49
|
-
end
|
50
|
-
if options.json then
|
51
|
-
puts JSON.dump(issues)
|
52
|
-
else
|
53
|
-
|
54
|
-
format = theStyle[:format]
|
55
|
-
keys = issues.map do |issue|
|
56
|
-
Mustache.render(format, issue.merge(issue[:fields]))
|
57
|
-
end
|
58
|
-
keys.each {|k| puts k}
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
# headers = [:key]
|
64
|
-
# rows = issues.map do |item|
|
65
|
-
# rw = [item[:key]]
|
66
|
-
# item[:fields].each_pair do |fname, fvalue|
|
67
|
-
# headers << fname unless headers.include? fname
|
68
|
-
# rw << fvalue
|
69
|
-
# end
|
70
|
-
# rw
|
71
|
-
# end
|
72
|
-
#
|
73
|
-
# puts Terminal::Table.new :headings => headers, :rows=>rows
|
74
|
-
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
78
|
-
alias_command :q, :query
|
79
|
-
|
80
|
-
# vim: set sw=2 ts=2 :
|
@@ -1,44 +0,0 @@
|
|
1
|
-
|
2
|
-
command :release do |c|
|
3
|
-
c.syntax = 'jm release [options]'
|
4
|
-
c.summary = 'Little tool for releasing a version in Jira'
|
5
|
-
c.description = ''
|
6
|
-
c.example 'description', 'command example'
|
7
|
-
c.option '--some-switch', 'Some switch that does something'
|
8
|
-
c.action do |args, options|
|
9
|
-
# Do something or c.when_called Jira::Commands::Release
|
10
|
-
|
11
|
-
version = GitUtils.getVersion
|
12
|
-
newver = ask("\033[1m=?\033[0m Enter the version you want to release (#{version}) ")
|
13
|
-
version = newver unless newver == ''
|
14
|
-
|
15
|
-
project = $cfg['jira.project']
|
16
|
-
jira = JiraUtils.new(args, options)
|
17
|
-
|
18
|
-
jira.createVersion(project, version)
|
19
|
-
|
20
|
-
### Find all unreleased issues
|
21
|
-
query ="assignee = #{jira.username} AND project = #{project} AND (status = Resolved OR status = Closed) AND fixVersion = EMPTY"
|
22
|
-
keys = jira.getIssues(query).map {|item| item['key'] }
|
23
|
-
printVars({:keys=>keys})
|
24
|
-
|
25
|
-
### Mark issues as fixed by version
|
26
|
-
updt = { 'fixVersions'=>[{'add'=>{'name'=>version}}] }
|
27
|
-
jira.updateKeys(keys, updt)
|
28
|
-
|
29
|
-
### This is old process residue. So should consider removing
|
30
|
-
if $cfg['jira.alsoClose'] == true
|
31
|
-
puts "Also closing." if options.verbose
|
32
|
-
query = "assignee = #{jira.username} AND project = #{project} AND status = Resolved AND fixVersion != EMPTY"
|
33
|
-
keys = jira.getIssues(query).map {|item| item['key'] }
|
34
|
-
printVars({:Rkeys=>keys})
|
35
|
-
|
36
|
-
if !keys.empty?
|
37
|
-
jira.transition(keys, 'Closed')
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
# vim: set sw=2 ts=2 :
|
@@ -1,68 +0,0 @@
|
|
1
|
-
|
2
|
-
def printErr(msg)
|
3
|
-
$stdout.print("\033[1m=!\033[0m ")
|
4
|
-
$stdout.print(msg)
|
5
|
-
$stdout.print("\n")
|
6
|
-
end
|
7
|
-
|
8
|
-
command :testReady do |c|
|
9
|
-
c.syntax = 'jm testReady [options] [version]'
|
10
|
-
c.summary = 'Little tool for setting the fix version on testable issues'
|
11
|
-
c.description = %{On all issues in the Testing state, set the fix version and optionally reassign.
|
12
|
-
}
|
13
|
-
c.example 'Set the release version to the latest git tag', %{jm testReady}
|
14
|
-
c.example 'Set the release version to "v2.0"', %{jm testReady v2.0}
|
15
|
-
c.example 'Also reassign to the default', %{jm testReady -r v2.0}
|
16
|
-
c.example 'Also reassign to BOB', %{jm testReady v2.0 --assign BOB}
|
17
|
-
c.option '-r', '--[no-]reassign', 'Also reassign to Default'
|
18
|
-
c.option '-a', '--assign USER', 'Assign to USER'
|
19
|
-
c.action do |args, options|
|
20
|
-
options.default :reassign => false
|
21
|
-
|
22
|
-
if args[0].nil? then
|
23
|
-
version = GitUtils.getVersion
|
24
|
-
newver = ask("\033[1m=?\033[0m Enter the version you want to release (#{version}) ")
|
25
|
-
version = newver unless newver == ''
|
26
|
-
else
|
27
|
-
version = args[0]
|
28
|
-
end
|
29
|
-
|
30
|
-
jira = JiraUtils.new(args, options)
|
31
|
-
project = jira.project
|
32
|
-
|
33
|
-
if !options.assign.nil? then
|
34
|
-
users = jira.checkUser(options.assign)
|
35
|
-
if users.length > 1 then
|
36
|
-
printErr "User name '#{options.assign}' is ambigious."
|
37
|
-
printVars('Could be'=>users)
|
38
|
-
exit 4
|
39
|
-
end
|
40
|
-
if users.length <= 0 then
|
41
|
-
printErr "No users match #{options.assign}"
|
42
|
-
exit 4
|
43
|
-
end
|
44
|
-
options.assign = users[0]
|
45
|
-
printVars(:assign=>options.assign)
|
46
|
-
end
|
47
|
-
|
48
|
-
jira.createVersion(project, version)
|
49
|
-
|
50
|
-
### Find all unreleased issues
|
51
|
-
query ="assignee = #{jira.username} AND project = #{project} AND status = Testing"
|
52
|
-
keys = jira.getIssues(query).map {|item| item['key'] }
|
53
|
-
printVars({:keys=>keys})
|
54
|
-
|
55
|
-
### Mark issues as fixed by version
|
56
|
-
updt = { 'fixVersions'=>[{'add'=>{'name'=>version}}] }
|
57
|
-
## assign to '-1' to have Jira automatically assign it
|
58
|
-
updt['assignee'] = [{'set'=>{'name'=>'-1'}}] if options.reassign
|
59
|
-
updt['assignee'] = [{'set'=>{'name'=>options.assign}}] if options.assign
|
60
|
-
|
61
|
-
printVars(:update=>updt) if options.verbose
|
62
|
-
|
63
|
-
jira.updateKeys(keys, updt)
|
64
|
-
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
# vim: set sw=2 ts=2 :
|