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 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 :