jiraMule 0.1.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
|