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
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'JiraMule/commands/assign'
|
2
|
+
require 'JiraMule/commands/attach'
|
3
|
+
require 'JiraMule/commands/config'
|
4
|
+
require 'JiraMule/commands/goto'
|
5
|
+
require 'JiraMule/commands/kanban'
|
6
|
+
require 'JiraMule/commands/link'
|
7
|
+
require 'JiraMule/commands/logWork'
|
8
|
+
require 'JiraMule/commands/next'
|
9
|
+
require 'JiraMule/commands/open'
|
10
|
+
require 'JiraMule/commands/query'
|
11
|
+
require 'JiraMule/commands/statuses'
|
@@ -3,6 +3,8 @@ require 'mustache'
|
|
3
3
|
require 'yaml'
|
4
4
|
require 'JiraMule/jiraUtils'
|
5
5
|
|
6
|
+
# This takes the result of multiple queries and displays it.
|
7
|
+
# `query` does this for a single.
|
6
8
|
command :kanban do |c|
|
7
9
|
extra_columns = []
|
8
10
|
c.syntax = 'jm query [options] kanban'
|
@@ -57,20 +59,20 @@ Formatting is done with Mustash.
|
|
57
59
|
:format => {
|
58
60
|
:heading => "#### {{column}}",
|
59
61
|
:item => "- {{key}} {{summary}}",
|
60
|
-
:order => [:Done, :
|
62
|
+
:order => [:Done, :CodeReview, :InProgress, :Todo],
|
61
63
|
},
|
62
64
|
:columns => {
|
63
|
-
:Done => [%{
|
64
|
-
|
65
|
+
:Done => [%{(},
|
66
|
+
%{status = "Dev Complete" OR},
|
67
|
+
%{status = "Closed" OR},
|
68
|
+
%{status = "Ready for QA" OR},
|
69
|
+
%{status = "Ready for Release to Production" OR},
|
70
|
+
%{status = "Testing/QA"},
|
71
|
+
%{)}
|
72
|
+
],
|
73
|
+
:CodeReview => [%{status = "Code Review"}],
|
65
74
|
:InProgress => [%{status = "In Progress"}],
|
66
|
-
:Todo => [%{
|
67
|
-
%{status = Reopened OR},
|
68
|
-
%{status = "On Deck" OR},
|
69
|
-
%{status = "Waiting Estimation Approval" OR},
|
70
|
-
%{status = "Reopened" OR},
|
71
|
-
%{status = "Testing (Signoff)" OR},
|
72
|
-
%{status = "Testing (Review)" OR},
|
73
|
-
%{status = "Testing - Bug Found")}],
|
75
|
+
:Todo => [ %{status = "On Deck"}],
|
74
76
|
},
|
75
77
|
|
76
78
|
},
|
@@ -79,32 +81,21 @@ Formatting is done with Mustash.
|
|
79
81
|
:format => {
|
80
82
|
:heading => "{{column}}",
|
81
83
|
:item => "{{key}}\n {{summary}}",
|
82
|
-
:order => [:Todo, :InProgress, :Done],
|
84
|
+
:order => [:Todo, :InProgress, :CodeReview, :Done],
|
83
85
|
:usetable => true
|
84
86
|
},
|
85
87
|
:columns => {
|
86
|
-
:Done => [%{(
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
:
|
95
|
-
|
96
|
-
|
97
|
-
%{status = "Waiting Estimation Approval" OR},
|
98
|
-
%{status = "Reopened" OR},
|
99
|
-
%{status = "Testing (Signoff)" OR},
|
100
|
-
%{status = "Testing (Review)" OR},
|
101
|
-
%{status = "Testing - Bug Found" OR},
|
102
|
-
%{status = "Backlog" OR},
|
103
|
-
%{status = "Ready For Dev" OR},
|
104
|
-
%{status = "Ready For QA" OR},
|
105
|
-
%{status = "To Do" OR},
|
106
|
-
%{status = "Release Package")},
|
107
|
-
],
|
88
|
+
:Done => [%{(},
|
89
|
+
%{status = "Dev Complete" OR},
|
90
|
+
%{status = "Closed" OR},
|
91
|
+
%{status = "Ready for QA" OR},
|
92
|
+
%{status = "Ready for Release to Production" OR},
|
93
|
+
%{status = "Testing/QA"},
|
94
|
+
%{)}
|
95
|
+
],
|
96
|
+
:InProgress => [%{status = "In Progress"}],
|
97
|
+
:Todo => [ %{(status = "On Deck")} ],
|
98
|
+
:CodeReview => [%{(status = "Code Review")}],
|
108
99
|
},
|
109
100
|
},
|
110
101
|
:taskpaper => {
|
@@ -115,8 +106,7 @@ Formatting is done with Mustash.
|
|
115
106
|
},
|
116
107
|
:columns => {
|
117
108
|
:InProgress => %{status = "In Progress"},
|
118
|
-
:Todo => [%{
|
119
|
-
%{status = "Testing - Bug Found")}],
|
109
|
+
:Todo => [%{status = "On Deck"}],
|
120
110
|
}
|
121
111
|
},
|
122
112
|
}
|
@@ -173,7 +163,7 @@ Formatting is done with Mustash.
|
|
173
163
|
qBase = []
|
174
164
|
qBase.unshift("assignee = #{jira.username} AND") unless options.raw
|
175
165
|
qBase.unshift("project = #{jira.project} AND") unless options.raw
|
176
|
-
qBase.unshift('(' + args.join(' ') + ') AND') unless args.empty?
|
166
|
+
qBase.unshift('(' + args.join(' ') + ') AND') unless args.empty?
|
177
167
|
|
178
168
|
results = {}
|
179
169
|
columns.each_pair do |name, query|
|
@@ -194,7 +184,7 @@ Formatting is done with Mustash.
|
|
194
184
|
cW = -1 if cW == 0
|
195
185
|
cWR = cW
|
196
186
|
if format[:usetable] and cW > 0 then
|
197
|
-
borders =
|
187
|
+
borders = 5 + (columns.count * 3); # 2 on left, 3 on right, 3 for each internal
|
198
188
|
cW = (cW - borders) / columns.count
|
199
189
|
cWR = cW + ((cW - borders) % columns.count)
|
200
190
|
end
|
@@ -206,9 +196,9 @@ Formatting is done with Mustash.
|
|
206
196
|
line = Mustache.render(format[:item], issue.merge(issue[:fields]))
|
207
197
|
#### Trim length?
|
208
198
|
if format[:order].last == name
|
209
|
-
line[0..cWR]
|
199
|
+
line = line.split("\n").map{|l| l[0..cWR]}.join("\n")
|
210
200
|
else
|
211
|
-
line[0..cW]
|
201
|
+
line = line.split("\n").map{|l| l[0..cW]}.join("\n")
|
212
202
|
end
|
213
203
|
end
|
214
204
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
|
3
|
+
command :open do |c|
|
4
|
+
c.syntax = 'jm open [options] <keys...> '
|
5
|
+
c.summary = 'Open issues in web browser'
|
6
|
+
|
7
|
+
c.action do |args, options|
|
8
|
+
jira = JiraMule::JiraUtils.new(args, options)
|
9
|
+
keys = jira.expandKeys(args)
|
10
|
+
jira.printVars(:key=>keys)
|
11
|
+
urls = keys.map{|key| "#{$cfg['net.url']}/browse/#{key}"}
|
12
|
+
|
13
|
+
ocmd = $cfg['open-url.cmd']
|
14
|
+
if ocmd.nil? then
|
15
|
+
say_error "No open command!"
|
16
|
+
exit 2
|
17
|
+
end
|
18
|
+
ocmd = ocmd.shellsplit
|
19
|
+
urls.each do |url|
|
20
|
+
dcmd = ocmd + [url]
|
21
|
+
say "R: #{dcmd.join(' ')}" if $cfg['tool.verbose']
|
22
|
+
system(*dcmd) unless $cfg['tool.dry']
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# vim: set ai et sw=2 ts=2 :
|
@@ -0,0 +1,74 @@
|
|
1
|
+
|
2
|
+
command :query do |c|
|
3
|
+
c.syntax = 'jm query [options] query'
|
4
|
+
c.summary = 'Get the keys from a jira query'
|
5
|
+
c.description = 'Run a JQL query. '
|
6
|
+
c.example 'Get all Open issues and dump everything', %{jm query status=Open --all_fields --json}
|
7
|
+
c.example 'Show info about an issue', %{jm query --style info BUG-24}
|
8
|
+
c.example 'Show info about an issue', %{jm info BUG-24}
|
9
|
+
|
10
|
+
c.option '--style STYLE', String, 'Which output style to use'
|
11
|
+
|
12
|
+
c.option '--[no-]json', 'Output json reply instead of styled output'
|
13
|
+
|
14
|
+
c.option '--fields FIELDS', Array, 'Which fields to return.'
|
15
|
+
c.option '--all_fields', 'Return all fields'
|
16
|
+
|
17
|
+
c.option '--[no-]prefix', %{Use the style's query prefix}
|
18
|
+
c.option '--[no-]suffix', %{Use the style's query suffix}
|
19
|
+
|
20
|
+
c.option '-d', '--dump', 'Dump the style to STDOUT as yaml'
|
21
|
+
c.option '-p', '--print', 'Show actual query being used'
|
22
|
+
|
23
|
+
c.action do |args, options|
|
24
|
+
options.default(
|
25
|
+
:json => false,
|
26
|
+
:all_fields => false,
|
27
|
+
:style => 'basic',
|
28
|
+
:prefix => true,
|
29
|
+
:suffix => true,
|
30
|
+
)
|
31
|
+
|
32
|
+
theStyle = JiraMule::Style.fetch(options.style).dup
|
33
|
+
if theStyle.nil? then
|
34
|
+
say_error "No style \"#{options.style}\""
|
35
|
+
say_error "Try one of: #{JiraMule::Style.list.join(', ')}"
|
36
|
+
exit 2
|
37
|
+
end
|
38
|
+
#### look for command line overrides
|
39
|
+
fields = theStyle.fields
|
40
|
+
fields = options.fields if options.fields
|
41
|
+
fields = [] if options.all_fields
|
42
|
+
|
43
|
+
if options.dump then
|
44
|
+
puts theStyle.to_yaml
|
45
|
+
exit 0
|
46
|
+
end
|
47
|
+
|
48
|
+
jira = JiraMule::JiraUtils.new(args, options)
|
49
|
+
if args.count == 1 and not args.first.include?('=') then
|
50
|
+
args = ["key=#{jira.expandKeys([args.first]).first}"]
|
51
|
+
end
|
52
|
+
opts = {}
|
53
|
+
opts[:noprefix] = true unless options.prefix
|
54
|
+
opts[:nosuffix] = true unless options.suffix
|
55
|
+
args.push(opts) unless opts.empty?
|
56
|
+
q = theStyle.build_query(*args)
|
57
|
+
puts q if options.print
|
58
|
+
issues = jira.getIssues(q, fields)
|
59
|
+
|
60
|
+
if options.json then
|
61
|
+
puts JSON.dump(issues)
|
62
|
+
else
|
63
|
+
puts theStyle.apply(issues)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
alias_command :q, :query
|
68
|
+
alias_command :info, :query, '--style', 'info'
|
69
|
+
alias_command :progress, :query, '--style', 'progress'
|
70
|
+
alias_command :todo, :query, '--style', 'todo'
|
71
|
+
alias_command :comments, :query, '--style', 'comments'
|
72
|
+
alias_command :c, :query, '--style', 'comments'
|
73
|
+
|
74
|
+
# vim: set sw=2 ts=2 :
|
@@ -0,0 +1,30 @@
|
|
1
|
+
|
2
|
+
command :statuses do |c|
|
3
|
+
c.syntax = 'jm statuses <project>'
|
4
|
+
c.summary = 'Get all of the status states for a project'
|
5
|
+
c.description = 'Get all of the status states for a project'
|
6
|
+
c.option '--with_type', "Include and groups statuses with issure type"
|
7
|
+
|
8
|
+
c.action do |args, options|
|
9
|
+
options.default :json=> false, :with_type=>false
|
10
|
+
|
11
|
+
jira = JiraMule::JiraUtils.new(args, options)
|
12
|
+
prj = args.shift
|
13
|
+
ret = jira.statusesFor(prj)
|
14
|
+
|
15
|
+
if options.with_type then
|
16
|
+
res = ret.map{|sts| {:name=>sts[:name], :statuses=>(sts[:statuses] or []).map{|sst| sst[:name]}}}
|
17
|
+
pp res
|
18
|
+
else
|
19
|
+
res = ret.map{|sts| (sts[:statuses] or []).map{|sst| sst[:name]}}.flatten.uniq
|
20
|
+
if options.json then
|
21
|
+
puts res.to_json
|
22
|
+
else
|
23
|
+
say res.join(', ')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# vim: set sw=2 ts=2 :
|
@@ -2,6 +2,7 @@ require 'uri'
|
|
2
2
|
require 'net/http'
|
3
3
|
require 'net/http/post/multipart'
|
4
4
|
require 'json'
|
5
|
+
require 'yaml'
|
5
6
|
require 'date'
|
6
7
|
require 'pp'
|
7
8
|
require 'mime/types'
|
@@ -49,7 +50,7 @@ module JiraMule
|
|
49
50
|
# So on project APP, from %w{1 56 BUG-78} you get %w{APP-1 APP-56 BUG-78}
|
50
51
|
def expandKeys(keys)
|
51
52
|
return keys.map do |k|
|
52
|
-
k.match(/([a-zA-
|
53
|
+
k.match(/([a-zA-Z0-9]+-)?(\d+)/) do |m|
|
53
54
|
if m[1].nil? then
|
54
55
|
"#{project}-#{m[2]}"
|
55
56
|
else
|
@@ -94,7 +95,8 @@ module JiraMule
|
|
94
95
|
def getIssues(query, fields=[ 'key', 'summary' ])
|
95
96
|
verbose "Get keys: #{query}"
|
96
97
|
data = post('search', {:jql=>query, :fields=>fields})
|
97
|
-
data[:issues]
|
98
|
+
return data[:issues] if data.kind_of?(Hash) and data.has_key?(:issues)
|
99
|
+
[]
|
98
100
|
end
|
99
101
|
|
100
102
|
##
|
@@ -112,7 +114,7 @@ module JiraMule
|
|
112
114
|
# Create a new version for release.
|
113
115
|
# TODO: test this.
|
114
116
|
def createVersion(project, version)
|
115
|
-
verbose "Creating #{
|
117
|
+
verbose "Creating #{version} in #{project}"
|
116
118
|
unless $cfg['tool.dry'] then
|
117
119
|
data = post('version', {
|
118
120
|
'name' => version,
|
@@ -136,15 +138,21 @@ module JiraMule
|
|
136
138
|
# +description+:: Full details.
|
137
139
|
def createIssue(type, summary, description)
|
138
140
|
verbose "Creating #{type} issue for #{summary}"
|
141
|
+
|
142
|
+
customfields = {}
|
143
|
+
cust = $cfg['customfields.create']
|
144
|
+
customfields = YAML.load(cust, 'cfg->customfields.create') unless cust.nil?
|
145
|
+
verbose " With custom fields: #{cust.to_json}" unless cust.nil?
|
146
|
+
|
139
147
|
unless $cfg['tool.dry'] then
|
140
148
|
post('issue', {
|
141
|
-
:fields=>{
|
149
|
+
:fields=>customfields.merge({
|
142
150
|
:issuetype=>{:name=>type},
|
143
151
|
:project=>{:key=>project},
|
144
152
|
:summary=>summary,
|
145
153
|
:description=>description,
|
146
154
|
:labels=>['auto-imported'],
|
147
|
-
}
|
155
|
+
})
|
148
156
|
})
|
149
157
|
else
|
150
158
|
{:key=>'_'}
|
@@ -155,7 +163,7 @@ module JiraMule
|
|
155
163
|
# Check this issue type in current project
|
156
164
|
def checkIssueType(type='bug')
|
157
165
|
rt = Regexp.new(type.gsub(/\s+/, '[-_\s]*'), Regexp::IGNORECASE)
|
158
|
-
cmeta = get(
|
166
|
+
cmeta = get("issue/createmeta?projectKeys=#{project}")
|
159
167
|
prj = cmeta[:projects].select{|p| p[:key] == project}.first
|
160
168
|
prj[:issuetypes].select{|it| it[:name] =~ rt}
|
161
169
|
end
|
@@ -189,9 +197,10 @@ module JiraMule
|
|
189
197
|
|
190
198
|
# Get the status for a project
|
191
199
|
# +project+:: The project to fetch status from
|
192
|
-
def statusesFor(
|
193
|
-
|
194
|
-
|
200
|
+
def statusesFor(prj=nil)
|
201
|
+
prj = project if prj.nil?
|
202
|
+
verbose "Fetching statuses for #{prj}"
|
203
|
+
get('project/' + prj + '/statuses')
|
195
204
|
end
|
196
205
|
|
197
206
|
# Assign issues to a user
|
@@ -219,7 +228,7 @@ module JiraMule
|
|
219
228
|
}
|
220
229
|
body[:started] = on.to_time.strftime('%FT%T.%3N%z') unless on.nil?
|
221
230
|
|
222
|
-
verbose "Logging #{timespent} of work to #{key} with note \"#{notes}\""
|
231
|
+
verbose "Logging #{timespent} of work on #{body[:started] or 'now'} to #{key} with note \"#{notes}\""
|
223
232
|
post('issue/' + key + '/worklog', body) unless $cfg['tool.dry']
|
224
233
|
end
|
225
234
|
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'mustache'
|
2
|
+
|
3
|
+
module JiraMule
|
4
|
+
class IssueRender < Mustache
|
5
|
+
def initialize(hsh)
|
6
|
+
hsh.each_pair do |k,v|
|
7
|
+
self.class.send(:define_method, k.to_sym) {v}
|
8
|
+
end
|
9
|
+
@issue = hsh
|
10
|
+
self.class.send(:define_method, :issue) {@issue}
|
11
|
+
end
|
12
|
+
|
13
|
+
# We're not doing HTML, so never escape.
|
14
|
+
def escapeHTML(str)
|
15
|
+
str
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.render(tmpl, issue, custom_tags={})
|
19
|
+
r = self.new(issue.dup)
|
20
|
+
custom_tags.each_pair do |name, blk|
|
21
|
+
r[name.to_sym] = lambda do
|
22
|
+
blk.call(issue.dup)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
r.render(tmpl)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# vim: set ai et sw=2 ts=2 :
|
@@ -107,60 +107,5 @@ So these need to be added to your config.
|
|
107
107
|
end
|
108
108
|
alias_command :move, :goto
|
109
109
|
|
110
|
-
command :mapGoto do |c|
|
111
|
-
c.syntax = 'jm mapGoto [options]'
|
112
|
-
c.summary = 'Attempt to build a goto map'
|
113
|
-
c.option '--dot', %{Output a dot graph}
|
114
|
-
c.description = %{
|
115
|
-
This command is incomplete. The goal here is to auto-build the transision maps
|
116
|
-
for multi-step gotos.
|
117
|
-
|
118
|
-
Right now it is just dumping stuff.
|
119
|
-
|
120
|
-
}
|
121
|
-
c.action do |args, options|
|
122
|
-
options.defaults :dot => true
|
123
|
-
jira = JiraMule::JiraUtils.new(args, options)
|
124
|
-
|
125
|
-
# Get all of the states that issues can be in.
|
126
|
-
# Try to find an actual issue in each state, and load the next transitions from
|
127
|
-
# it.
|
128
|
-
#
|
129
|
-
types = jira.statusesFor(jira.project)
|
130
|
-
|
131
|
-
# There is only one workflow for all types it seems.
|
132
|
-
|
133
|
-
# We just need the names, so we'll merge down.
|
134
|
-
statusNames = {}
|
135
|
-
|
136
|
-
types.each do |type|
|
137
|
-
statuses = type[:statuses]
|
138
|
-
next if statuses.nil?
|
139
|
-
next if statuses.empty?
|
140
|
-
statuses.each {|status| statusNames[ status[:name] ] = 1}
|
141
|
-
end
|
142
|
-
|
143
|
-
puts "digraph #{jira.project} {"
|
144
|
-
statusNames.each_key do |status|
|
145
|
-
#puts " #{status}"
|
146
|
-
query = %{project = #{jira.project} AND status = "#{status}"}
|
147
|
-
issues = jira.getIssues(query, ["key"])
|
148
|
-
if issues.empty? then
|
149
|
-
#?
|
150
|
-
puts %{/* Cannot find transitions for #{status} */}
|
151
|
-
else
|
152
|
-
key = issues.first[:key]
|
153
|
-
# get transisitons.
|
154
|
-
trans = jira.transitionsFor(key)
|
155
|
-
trans.each do |tr|
|
156
|
-
puts %{ "#{status}" -> "#{tr[:to][:name]}" [label="#{tr[:name]}"]} # [#{tr[:id]}]"]}
|
157
|
-
end
|
158
|
-
end
|
159
|
-
end
|
160
|
-
puts "}"
|
161
|
-
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
110
|
# vim: set sw=2 ts=2 :
|
166
111
|
|