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.
@@ -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