jiraMule 0.1.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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, :Testing, :InProgress, :Todo],
62
+ :order => [:Done, :CodeReview, :InProgress, :Todo],
61
63
  },
62
64
  :columns => {
63
- :Done => [%{status = 'Pending Release'}],
64
- :Testing => [%{status = Testing}],
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 => [%{(status = Open OR},
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 => [%{(status = Released OR status = "Not Needed - Closed")}],
87
- :InProgress => [%{(status = "In Progress" OR},
88
- %{status = "In Dev" OR},
89
- %{status = "Pending Release" OR},
90
- %{status = "In QA" OR},
91
- %{status = "Integration QA" OR},
92
- %{status = "In Design")},
93
- ],
94
- :Todo => [%{(status = Open OR},
95
- %{status = Reopened OR},
96
- %{status = "On Deck" OR},
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 => [%{(status = Open OR},
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 = 4 + (columns.count * 3); # 2 on left, 2 on right, 3 for each internal
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-Z]+-)?(\d+)/) do |m|
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 #{request.body}"
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('issue/createmeta')
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(project)
193
- verbose "Fetching statuses for #{project}"
194
- get('project/' + project + '/statuses')
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