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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5d5f1f193633dde0eb039525564c5014d6c194ea
4
- data.tar.gz: 2fa236f539408984963b7e54ff0cadc0ceacd9fa
3
+ metadata.gz: cbbaa7c560ccffc684631d28f866e682e8b16e22
4
+ data.tar.gz: 806095be43a2c49ffdeda778ea4e66a78510e006
5
5
  SHA512:
6
- metadata.gz: d57a6e8ba761e0de2f1b9ffeca7f1ce09f98d6448a5c2928d5268cb33cf5c31df226e7e571e024a8d680ff0db088fa9de9fe541a7b51c580383cea408d960aa8
7
- data.tar.gz: a3ec4461bc79d50dd0832ab65f2f6060fb6c561a331e4b1399bbf98a1e5317911c1c6b6c86edd1c4eab460a20b91546c05254a0c5ec5a02e98b3358d1c8f72b4
6
+ metadata.gz: 82151664ad86051f89463bebae844a54081373bd203d98fec2e92899bc4c044bd29aff46f2ee47007d73cd51f19acd399c2e5e858383f97b8ea0a32c39e10fc7
7
+ data.tar.gz: bdd85768fb65763a3dc03269fa715beef2d6c2a7e9c7db571b1e8c4e9a5ace6ce0286378ab1068e9776ef2de216d16343f035e893f500b4abe23d1453cfddc27
data/Gemfile CHANGED
@@ -2,6 +2,7 @@ source 'http://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
5
+ gem 'chronic'
5
6
  gem 'chronic_duration'
6
7
  gem 'commander'
7
8
  gem 'inifile', '~> 3.0'
@@ -9,7 +10,7 @@ gem 'mime-types', '~> 1.25.1'
9
10
  gem 'mime-types-data', '~> 3.2016'
10
11
  gem 'multipart-post'
11
12
  gem 'mustache'
12
- gem "octokit", "~> 4.0"
13
+ # gem "octokit", "~> 4.0"
13
14
  gem 'terminal-table'
14
15
  gem 'vine'
15
16
  gem 'zip'
data/README.md CHANGED
@@ -15,7 +15,7 @@
15
15
 
16
16
  ## Setup
17
17
 
18
- You need to set a few config keys before anyhting really works.
18
+ You need to set a few config keys before anything really works.
19
19
 
20
20
  Globally:
21
21
  ```
@@ -33,5 +33,5 @@ jm config jira.project <Project ID>
33
33
 
34
34
  #### Jira API Docs
35
35
 
36
- [https://docs.atlassian.com/jira/REST/6.4.7/][]
36
+ [https://docs.atlassian.com/jira/REST/7.2.4/][]
37
37
 
data/Rakefile CHANGED
@@ -42,14 +42,31 @@ namespace :git do
42
42
  end
43
43
 
44
44
  task :gemit do
45
- mrt=Bundler::GemHelper.gemspec.version
46
- sh %{git checkout v#{mrt}}
45
+ sh %{git checkout #{tagName}}
47
46
  Rake::Task[:build].invoke
48
- Rake::Task[:bob].invoke
47
+ # Rake::Task[:bob].invoke
49
48
  Rake::Task['push:gem'].invoke
50
49
  sh %{git checkout develop}
51
50
  end
52
51
 
52
+ namespace :release do
53
+ desc "Open a release branch"
54
+ task :open, [:version] do |t, args|
55
+ sh %{git checkout master}
56
+ sh %{git checkout -b release/v#{args[:version]}}
57
+ # - update version.rb
58
+ sh %{git commit lib/JiraMule/version.rb}
59
+ end
60
+
61
+ desc "Close a release branch"
62
+ task :close, [:version] do |t, args|
63
+ sh %{git checkout develop && git merge release/v#{args[:version]}}
64
+ sh %{git checkout master && git merge release/v#{args[:version]}}
65
+ sh %{git tag "v#{args[:version]}" -m "Release v#{args[:version]}"}
66
+ sh %{git branch -d release/v#{args[:version]}}
67
+ end
68
+ end
69
+
53
70
  namespace :push do
54
71
  desc 'Push gem up to RubyGems'
55
72
  task :gem do
data/TODO.taskpaper CHANGED
@@ -1,6 +1,5 @@
1
1
 
2
2
  New commands:
3
3
  - 'comment' for adding a comment. If no '-m' then open $EDITOR
4
- - 'assign' Assign to another user.
5
4
 
6
5
 
data/jiraMule.gemspec CHANGED
@@ -25,6 +25,7 @@ already. Rather this is specific to things I need.
25
25
  s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
26
26
  s.require_paths = ['lib']
27
27
 
28
+ s.add_runtime_dependency('chronic', '~> 0.10.2')
28
29
  s.add_runtime_dependency('chronic_duration', '~> 0.10.6')
29
30
  s.add_runtime_dependency('commander', '~> 4.4.0')
30
31
  s.add_runtime_dependency('inifile', '~> 3.0')
@@ -32,12 +33,13 @@ already. Rather this is specific to things I need.
32
33
  s.add_runtime_dependency('mime-types-data', '~> 3.2016')
33
34
  s.add_runtime_dependency('multipart-post', '~> 2.0.0')
34
35
  s.add_runtime_dependency('mustache', '~> 1.0')
36
+ s.add_runtime_dependency('octokit', '~> 4.0')
35
37
  s.add_runtime_dependency('terminal-table', '~> 1.4.5')
36
38
  s.add_runtime_dependency('vine', '~> 0.2')
37
39
  s.add_runtime_dependency('zip', '~> 2.0.0')
38
40
 
39
- s.add_development_dependency('bundler', '~> 1.12.0')
40
- s.add_development_dependency('rspec', '~> 3.2')
41
+ s.add_development_dependency('bundler')
42
+ s.add_development_dependency('rspec')
41
43
  s.add_development_dependency('rake')
42
44
  end
43
45
 
data/lib/JiraMule.rb CHANGED
@@ -1,7 +1,9 @@
1
1
  require 'JiraMule/version'
2
2
  require 'JiraMule/Config'
3
- require 'JiraMule/jiraUtils'
3
+
4
4
  require 'JiraMule/gitUtils'
5
- require 'JiraMule/Tempo'
5
+ require 'JiraMule/jiraUtils'
6
+ require 'JiraMule/renderMagic'
7
+ require 'JiraMule/StyleLoader'
6
8
 
7
9
  require 'JiraMule/commands'
@@ -0,0 +1,323 @@
1
+
2
+
3
+ module JiraMule
4
+ # Style is a formula for formatting the results of a query.
5
+ # This currently only works with single queries.
6
+ class Style
7
+ def initialize(name, &block)
8
+ @name = name.to_sym
9
+ @fields = [:key, :summary]
10
+ @header = [:key, :summary]
11
+ @footer = nil
12
+ @format_type = :strings
13
+ @format = %{{{key}} {{summary}}}
14
+
15
+ @custom_tags = {}
16
+
17
+ @prefix_query = nil
18
+ @default_query = nil
19
+ @suffix_query = nil
20
+
21
+ @bolden_tag = :bolden
22
+
23
+ block.call(self) if block_given?
24
+ end
25
+
26
+ ######################################################
27
+ def self.add(style, &block)
28
+ @@styles = {} unless defined?(@@styles)
29
+
30
+ if style.kind_of?(String) or style.kind_of?(Symbol) then
31
+ style = Style.new(style, &block)
32
+ end
33
+
34
+ @@styles[style.name] = style
35
+ end
36
+
37
+ def self.fetch(name)
38
+ @@styles[name.to_sym]
39
+ end
40
+
41
+ def self.list
42
+ @@styles.keys
43
+ end
44
+
45
+ ######################################################
46
+ # Apply this style to Issues
47
+ # @param issues [Array] Issues from the Jira query to format
48
+ # @return formatted string
49
+ def apply(issues)
50
+ if @format_type == :strings then
51
+ keys = issues.map do |issue|
52
+ fmt = @format
53
+ fmt = fmt.join(' ') if fmt.kind_of? Array
54
+ res = JiraMule::IssueRender.render(fmt, issue.merge(issue[:fields]), @custom_tags)
55
+ bolden(issue, res)
56
+ end
57
+ (@header or '').to_s + keys.join("\n") + (@footer or '').to_s
58
+
59
+ elsif [:table, :table_rows, :table_columns].include? @format_type then
60
+ @format = [@format] unless @format.kind_of? Array
61
+ rows = issues.map do |issue|
62
+ issue = issue.merge(issue[:fields])
63
+ @format.map do |col|
64
+ if col.kind_of? Hash then
65
+ col = col.dup
66
+ str = col[:value] or ""
67
+ res = JiraMule::IssueRender.render(str, issue, @custom_tags)
68
+ col[:value] = bolden(issue, res)
69
+ col
70
+ else
71
+ res = JiraMule::IssueRender.render(col, issue, @custom_tags)
72
+ bolden(issue, res)
73
+ end
74
+ end
75
+ end
76
+ if @format_type == :table_columns then
77
+ rows = rows.transpose
78
+ end
79
+ header = (@header or [])
80
+ header = [header] unless header.kind_of? Array
81
+ Terminal::Table.new :headings => header, :rows=>rows
82
+ end
83
+ end
84
+
85
+ # If this issue should be bolded or not.
86
+ def bolden(issue, row, color=:bold)
87
+ bld = issue[@bolden_tag]
88
+ bld = @custom_tags[@bolden_tag] if bld.nil?
89
+ bld = bld.call(issue.dup) if bld.kind_of? Proc
90
+ # ? truthy other than Ruby default?
91
+ return row unless bld
92
+ if row.kind_of? Array then
93
+ row.map{|r| HighLine.color(r, color)}
94
+ elsif row.kind_of? Hash then
95
+ hsh={}
96
+ row.each_pair{|k,v| hsh[k] = HighLine.color(v, color)}
97
+ else
98
+ HighLine.color(row.to_s, color)
99
+ end
100
+ end
101
+
102
+ # TODO: Dump method that outputs Ruby
103
+
104
+ # Build a query based on this Style and other bits from command line
105
+ # @param args [Array<String>] Other bits of JQL to use instead of default_query
106
+ def build_query(*args)
107
+ opts = {}
108
+ opts = args.pop if args.last.kind_of? Hash
109
+
110
+ # Add the default query, if there is one
111
+ if not @default_query.nil? then
112
+ # make sure there is an AND if working with a cmdline supplied part
113
+ args.push('AND') unless args.empty?
114
+ case @default_query
115
+ when Array
116
+ args.push @default_query.join(' AND ')
117
+ when Proc
118
+ args.push @default_query.call()
119
+ else
120
+ args.push @default_query.to_s
121
+ end
122
+ end
123
+
124
+ # Get prefix as a String.
125
+ case @prefix_query
126
+ when Array
127
+ prefix = @prefix_query.join(' AND ') + ' AND'
128
+ when Proc
129
+ prefix = @prefix_query.call()
130
+ else
131
+ prefix = @prefix_query.to_s
132
+ end
133
+ args.unshift(prefix) unless opts.has_key? :noprefix
134
+
135
+ # Get suffix as a String.
136
+ case @suffix_query
137
+ when Array
138
+ suffix = 'AND ' + @suffix_query.join(' AND ')
139
+ when Proc
140
+ suffix = @suffix_query.call()
141
+ else
142
+ suffix = @suffix_query.to_s
143
+ end
144
+ args.push(suffix) unless opts.has_key? :nosuffix
145
+
146
+ args.flatten.compact.join(' ')
147
+ end
148
+
149
+ # May need to split this into two classes. One that is the above methods
150
+ # and one that is the below methods. The below one is used just for the
151
+ # construction of a Style. While the above is the usage of a style.
152
+ #
153
+ # Maybe the above are in a Module, that is included as part of fetch?
154
+ ######################################################
155
+
156
+ attr_accessor :prefix_query, :suffix_query, :default_query
157
+ attr_accessor :header
158
+
159
+ def name
160
+ @name
161
+ end
162
+
163
+ # takes a single flat array of key names.
164
+ def fields(*args)
165
+ return @fields if args.empty?
166
+ @fields = args.flatten.compact.map{|i| i.to_sym}
167
+ end
168
+ alias_method :fields=, :fields
169
+
170
+ FORMAT_TYPES = [:strings, :table_rows, :table_columns, :table].freeze
171
+ def format_type(type)
172
+ return @format_type if type.nil?
173
+ raise "Unknown format type: \"#{type}\"" unless FORMAT_TYPES.include? type
174
+ @format_type = type
175
+ end
176
+ alias_method :format_type=, :format_type
177
+
178
+ def format(*args)
179
+ return @format if args.empty?
180
+ args.flatten! if args.kind_of? Array
181
+ @format = args
182
+ end
183
+ alias_method :format=, :format
184
+
185
+ # Create a custom tag for formatted output
186
+ def add_tag(name, &block)
187
+ @custom_tags[name.to_sym] = block
188
+ end
189
+
190
+ end
191
+
192
+ ##############################################################################
193
+ ##############################################################################
194
+ ##############################################################################
195
+
196
+ Style.add(:basic) do |s|
197
+ s.fields [:key, :summary]
198
+ s.format %{{{key}} {{summary}}}
199
+ s.header = nil
200
+ end
201
+
202
+ Style.add(:pr) do |s|
203
+ s.fields [:key, :summary]
204
+ s.format %{{{summary}} ({{key}})}
205
+ s.header = nil
206
+ end
207
+
208
+ Style.add(:info) do |s|
209
+ s.fields [:key, :summary, :description, :assignee, :reporter, :priority,
210
+ :issuetype, :status, :resolution, :votes, :watches]
211
+ s.header = nil
212
+ s.format %{ Key: {{key}}
213
+ Summary: {{summary}}
214
+ Reporter: {{reporter.displayName}}
215
+ Assignee: {{assignee.displayName}}
216
+ Type: {{issuetype.name}} ({{priority.name}})
217
+ Status: {{status.name}} (Resolution: {{resolution.name}})
218
+ Watches: {{watches.watchCount}} Votes: {{votes.votes}}
219
+ Description: {{description}}
220
+ }
221
+ end
222
+
223
+ Style.add(:test_table) do |s|
224
+ s.fields [:key, :assignee]
225
+ s.format_type :table_columns
226
+ s.header = nil
227
+ s.format [%{{{key}}}, %{{{assignee.displayName}}}]
228
+ end
229
+
230
+ Style.add(:progress) do |s|
231
+ s.fields [:key, :workratio, :aggregatetimespent, :duedate,
232
+ :aggregatetimeoriginalestimate]
233
+ s.format_type :table_rows
234
+ s.header = [:key, :estimated, :progress, :percent, :due]
235
+ s.format [%{{{key}}},
236
+ {:value=>%{{{estimate}}},:alignment=>:right},
237
+ {:value=>%{{{progress}}},:alignment=>:right},
238
+ {:value=>%{{{percent}}%},:alignment=>:right},
239
+ {:value=>%{{{duedate}}},:alignment=>:center},
240
+ ]
241
+ # Use lambda when there is logic that needs to be deferred.
242
+ s.prefix_query = lambda do
243
+ r = []
244
+ r << %{assignee = #{$cfg['user.name']}} unless $cfg['user.name'].nil?
245
+ prjs = $cfg['jira.project']
246
+ unless prjs.nil? then
247
+ r << '(' + prjs.split(' ').map{|prj| %{project = #{prj}}}.join(' OR ') + ')'
248
+ end
249
+ r.join(' AND ') + ' AND'
250
+ end
251
+ s.default_query = %{(status = "In Progress" OR status = "In Design/Estimation")}
252
+ s.suffix_query = %{ORDER BY Rank}
253
+
254
+ s.add_tag(:bolden) do |issue|
255
+ estimate = (issue[:aggregatetimeoriginalestimate] or 0)
256
+ progress = (issue[:aggregatetimespent] or 0)
257
+ due = issue[:duedate]
258
+ progress > estimate or (not due.nil? and Date.new >= Date.parse(due))
259
+ end
260
+ s.add_tag(:estimate) do |issue|
261
+ "%.2f"%[(issue[:aggregatetimeoriginalestimate] or 0) / 3600.0]
262
+ end
263
+ s.add_tag(:progress) do |issue|
264
+ "%.2f"%[(issue[:aggregatetimespent] or 0) / 3600.0]
265
+ end
266
+ s.add_tag(:percent) do |issue|
267
+ percent = issue[:workratio]
268
+ if percent < 0 then
269
+ estimate = (issue[:aggregatetimeoriginalestimate] or 0) / 3600.0
270
+ if estimate > 0 then
271
+ progress = (issue[:aggregatetimespent] or 0) / 3600.0
272
+ percent = (progress / estimate * 100).floor
273
+ else
274
+ percent = 100 # XXX ??? what is this line doing? why is it here?
275
+ end
276
+ end
277
+ if percent > 1000 then
278
+ ">1000"
279
+ else
280
+ "%.1f"%[percent]
281
+ end
282
+ end
283
+ end
284
+
285
+ Style.add(:todo) do |s|
286
+ s.fields [:key, :summary]
287
+ s.header = "## Todo\n"
288
+ s.format %{- {{fixkey}}\t{{summary}}}
289
+
290
+ # Use lambda when there is logic that needs to be deferred.
291
+ s.prefix_query = lambda do
292
+ r = []
293
+ r << %{assignee = #{$cfg['user.name']}} unless $cfg['user.name'].nil?
294
+ prjs = $cfg['jira.project']
295
+ unless prjs.nil? then
296
+ r << '(' + prjs.split(' ').map{|prj| %{project = #{prj}}}.join(' OR ') + ')'
297
+ end
298
+ r.join(' AND ') + ' AND'
299
+ end
300
+ s.default_query = '(' + [
301
+ %{status = "On Deck"},
302
+ ].join(' OR ') + ')'
303
+ s.suffix_query = %{ORDER BY Rank}
304
+
305
+ s.add_tag(:fixkey) do |issue|
306
+ "%-10s" % issue[:key]
307
+ end
308
+ end
309
+
310
+
311
+ Style.add(:comments) do |s|
312
+ s.fields [:key, :comment]
313
+ s.header = nil
314
+ s.format %{{{#comment.comments}}
315
+ ---------------------------------------------
316
+ > {{author.displayName}} {{created}} {{editied}}
317
+ {{body}}
318
+ {{/comment.comments}}
319
+ }
320
+ end
321
+
322
+ end
323
+ # vim: set ai et sw=2 ts=2 :