mongo-jira 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ ODRhOTNkZDY1NjBiNDUwMTg4MmZmN2U2YWNlYTUxNTNiN2EyYjg4Zg==
5
+ data.tar.gz: !binary |-
6
+ YmQ3OGYyMWIxMzcwODFhZjkzNzIzNGQzMDZkMjU1NTljZWU4Yzg3Mg==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ MDBiOTc1MmMxMGQ0N2VlMmQ4MDMzMDgzMzY3MWUzMWJjYzBiMDBhY2VlNzY0
10
+ ZTRhZWE2NjI2ODE4YzczZjZhMWZkNTZkMzM4ZTViMjVlMjg3MTllYzBjNzU2
11
+ ZTY4ZDY1ZTNhZjIwZmIzNmJhODRmZjI2NDVhMDQyNjJjZmZkMjc=
12
+ data.tar.gz: !binary |-
13
+ YzFlZDA0YjhhMTkzZWM0NzU1ODBiYzQxNGFlZGRjYjE4ODFkOTAwOGYxYjM2
14
+ OTBlNGFjOWUwNDdiMWVhMGYwYTU2NzRhZTUxZjI5M2U2NmU2MjAwMjAwNjli
15
+ NGI5YmI3NjgyNGNmNzg5ZWZhMzQ4MDk2MWIzN2RkYjY0YzRiNTc=
@@ -0,0 +1,4 @@
1
+ 4�V��H���*r�h
2
+ ��h�l���X�{z���6��ZP���>���ģ��^"w����-���[?��r�F�w�;Tv��y��2��;l���D����� �>�c�!��Ã����m����
3
+ HO OC�<��4�q������Lҩ��2Z
4
+ �JMD�͛������_��m[S t8#��|��)�2}v�h!�D�h�EҮ]ڲ`�Р:?� ��B.�fY�"M����'�1a����@B�Vxkv݁\� �
Binary file
@@ -0,0 +1,24 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ Gemfile.lock
6
+ coverage
7
+ InstalledFiles
8
+ lib/bundler/man
9
+ pkg
10
+ rdoc
11
+ spec/reports
12
+ test/tmp
13
+ test/version_tmp
14
+ tmp
15
+
16
+ # YARD artifacts
17
+ .yardoc
18
+ _yardoc
19
+ doc/
20
+
21
+ .idea/*
22
+ *#
23
+ .#*
24
+ *~
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in mongo-jira.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,191 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction, and
10
+ distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by the copyright
13
+ owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all other entities
16
+ that control, are controlled by, or are under common control with that entity.
17
+ For the purposes of this definition, "control" means (i) the power, direct or
18
+ indirect, to cause the direction or management of such entity, whether by
19
+ contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
20
+ outstanding shares, or (iii) beneficial ownership of such entity.
21
+
22
+ "You" (or "Your") shall mean an individual or Legal Entity exercising
23
+ permissions granted by this License.
24
+
25
+ "Source" form shall mean the preferred form for making modifications, including
26
+ but not limited to software source code, documentation source, and configuration
27
+ files.
28
+
29
+ "Object" form shall mean any form resulting from mechanical transformation or
30
+ translation of a Source form, including but not limited to compiled object code,
31
+ generated documentation, and conversions to other media types.
32
+
33
+ "Work" shall mean the work of authorship, whether in Source or Object form, made
34
+ available under the License, as indicated by a copyright notice that is included
35
+ in or attached to the work (an example is provided in the Appendix below).
36
+
37
+ "Derivative Works" shall mean any work, whether in Source or Object form, that
38
+ is based on (or derived from) the Work and for which the editorial revisions,
39
+ annotations, elaborations, or other modifications represent, as a whole, an
40
+ original work of authorship. For the purposes of this License, Derivative Works
41
+ shall not include works that remain separable from, or merely link (or bind by
42
+ name) to the interfaces of, the Work and Derivative Works thereof.
43
+
44
+ "Contribution" shall mean any work of authorship, including the original version
45
+ of the Work and any modifications or additions to that Work or Derivative Works
46
+ thereof, that is intentionally submitted to Licensor for inclusion in the Work
47
+ by the copyright owner or by an individual or Legal Entity authorized to submit
48
+ on behalf of the copyright owner. For the purposes of this definition,
49
+ "submitted" means any form of electronic, verbal, or written communication sent
50
+ to the Licensor or its representatives, including but not limited to
51
+ communication on electronic mailing lists, source code control systems, and
52
+ issue tracking systems that are managed by, or on behalf of, the Licensor for
53
+ the purpose of discussing and improving the Work, but excluding communication
54
+ that is conspicuously marked or otherwise designated in writing by the copyright
55
+ owner as "Not a Contribution."
56
+
57
+ "Contributor" shall mean Licensor and any individual or Legal Entity on behalf
58
+ of whom a Contribution has been received by Licensor and subsequently
59
+ incorporated within the Work.
60
+
61
+ 2. Grant of Copyright License.
62
+
63
+ Subject to the terms and conditions of this License, each Contributor hereby
64
+ grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
65
+ irrevocable copyright license to reproduce, prepare Derivative Works of,
66
+ publicly display, publicly perform, sublicense, and distribute the Work and such
67
+ Derivative Works in Source or Object form.
68
+
69
+ 3. Grant of Patent License.
70
+
71
+ Subject to the terms and conditions of this License, each Contributor hereby
72
+ grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
73
+ irrevocable (except as stated in this section) patent license to make, have
74
+ made, use, offer to sell, sell, import, and otherwise transfer the Work, where
75
+ such license applies only to those patent claims licensable by such Contributor
76
+ that are necessarily infringed by their Contribution(s) alone or by combination
77
+ of their Contribution(s) with the Work to which such Contribution(s) was
78
+ submitted. If You institute patent litigation against any entity (including a
79
+ cross-claim or counterclaim in a lawsuit) alleging that the Work or a
80
+ Contribution incorporated within the Work constitutes direct or contributory
81
+ patent infringement, then any patent licenses granted to You under this License
82
+ for that Work shall terminate as of the date such litigation is filed.
83
+
84
+ 4. Redistribution.
85
+
86
+ You may reproduce and distribute copies of the Work or Derivative Works thereof
87
+ in any medium, with or without modifications, and in Source or Object form,
88
+ provided that You meet the following conditions:
89
+
90
+ You must give any other recipients of the Work or Derivative Works a copy of
91
+ this License; and
92
+ You must cause any modified files to carry prominent notices stating that You
93
+ changed the files; and
94
+ You must retain, in the Source form of any Derivative Works that You distribute,
95
+ all copyright, patent, trademark, and attribution notices from the Source form
96
+ of the Work, excluding those notices that do not pertain to any part of the
97
+ Derivative Works; and
98
+ If the Work includes a "NOTICE" text file as part of its distribution, then any
99
+ Derivative Works that You distribute must include a readable copy of the
100
+ attribution notices contained within such NOTICE file, excluding those notices
101
+ that do not pertain to any part of the Derivative Works, in at least one of the
102
+ following places: within a NOTICE text file distributed as part of the
103
+ Derivative Works; within the Source form or documentation, if provided along
104
+ with the Derivative Works; or, within a display generated by the Derivative
105
+ Works, if and wherever such third-party notices normally appear. The contents of
106
+ the NOTICE file are for informational purposes only and do not modify the
107
+ License. You may add Your own attribution notices within Derivative Works that
108
+ You distribute, alongside or as an addendum to the NOTICE text from the Work,
109
+ provided that such additional attribution notices cannot be construed as
110
+ modifying the License.
111
+ You may add Your own copyright statement to Your modifications and may provide
112
+ additional or different license terms and conditions for use, reproduction, or
113
+ distribution of Your modifications, or for any such Derivative Works as a whole,
114
+ provided Your use, reproduction, and distribution of the Work otherwise complies
115
+ with the conditions stated in this License.
116
+
117
+ 5. Submission of Contributions.
118
+
119
+ Unless You explicitly state otherwise, any Contribution intentionally submitted
120
+ for inclusion in the Work by You to the Licensor shall be under the terms and
121
+ conditions of this License, without any additional terms or conditions.
122
+ Notwithstanding the above, nothing herein shall supersede or modify the terms of
123
+ any separate license agreement you may have executed with Licensor regarding
124
+ such Contributions.
125
+
126
+ 6. Trademarks.
127
+
128
+ This License does not grant permission to use the trade names, trademarks,
129
+ service marks, or product names of the Licensor, except as required for
130
+ reasonable and customary use in describing the origin of the Work and
131
+ reproducing the content of the NOTICE file.
132
+
133
+ 7. Disclaimer of Warranty.
134
+
135
+ Unless required by applicable law or agreed to in writing, Licensor provides the
136
+ Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
137
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
138
+ including, without limitation, any warranties or conditions of TITLE,
139
+ NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
140
+ solely responsible for determining the appropriateness of using or
141
+ redistributing the Work and assume any risks associated with Your exercise of
142
+ permissions under this License.
143
+
144
+ 8. Limitation of Liability.
145
+
146
+ In no event and under no legal theory, whether in tort (including negligence),
147
+ contract, or otherwise, unless required by applicable law (such as deliberate
148
+ and grossly negligent acts) or agreed to in writing, shall any Contributor be
149
+ liable to You for damages, including any direct, indirect, special, incidental,
150
+ or consequential damages of any character arising as a result of this License or
151
+ out of the use or inability to use the Work (including but not limited to
152
+ damages for loss of goodwill, work stoppage, computer failure or malfunction, or
153
+ any and all other commercial damages or losses), even if such Contributor has
154
+ been advised of the possibility of such damages.
155
+
156
+ 9. Accepting Warranty or Additional Liability.
157
+
158
+ While redistributing the Work or Derivative Works thereof, You may choose to
159
+ offer, and charge a fee for, acceptance of support, warranty, indemnity, or
160
+ other liability obligations and/or rights consistent with this License. However,
161
+ in accepting such obligations, You may act only on Your own behalf and on Your
162
+ sole responsibility, not on behalf of any other Contributor, and only if You
163
+ agree to indemnify, defend, and hold each Contributor harmless for any liability
164
+ incurred by, or claims asserted against, such Contributor by reason of your
165
+ accepting any such warranty or additional liability.
166
+
167
+ END OF TERMS AND CONDITIONS
168
+
169
+ APPENDIX: How to apply the Apache License to your work
170
+
171
+ To apply the Apache License to your work, attach the following boilerplate
172
+ notice, with the fields enclosed by brackets "[]" replaced with your own
173
+ identifying information. (Don't include the brackets!) The text should be
174
+ enclosed in the appropriate comment syntax for the file format. We also
175
+ recommend that a file or class name and description of purpose be included on
176
+ the same "printed page" as the copyright notice for easier identification within
177
+ third-party archives.
178
+
179
+ Copyright [yyyy] [name of copyright owner]
180
+
181
+ Licensed under the Apache License, Version 2.0 (the "License");
182
+ you may not use this file except in compliance with the License.
183
+ You may obtain a copy of the License at
184
+
185
+ http://www.apache.org/licenses/LICENSE-2.0
186
+
187
+ Unless required by applicable law or agreed to in writing, software
188
+ distributed under the License is distributed on an "AS IS" BASIS,
189
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
190
+ See the License for the specific language governing permissions and
191
+ limitations under the License.
@@ -0,0 +1,44 @@
1
+ mongo-jira
2
+ ==========
3
+
4
+ Simple Jira command line tool
5
+
6
+ This is a stub for a set of very simple command lines tools to access Jira tickets through the
7
+ REST api
8
+
9
+ ## Installation
10
+
11
+ Install it yourself as:
12
+
13
+ $ gem install mongo-jira
14
+
15
+ ## Usage
16
+
17
+ ### Initial configuration
18
+
19
+ Create a file called **.mongo-jira.json** in your home dir (alternatively use the -c flag to set different location). Add the following :
20
+
21
+ {
22
+ "username": "jim.oleary@10gen.com",
23
+ "password": "Base64EncodedPassword==",
24
+ "site": "https://jira.mongodb.org",
25
+ "auth_type": "basic",
26
+ "context_path": ""
27
+ }
28
+
29
+ If you omit the password, then you will be asked for one.
30
+
31
+ Change **<your username>** and **<your password>** ;-)
32
+
33
+ Execute the following command, if your config is correct you should see all your Jira projects:
34
+
35
+ jira test
36
+
37
+
38
+ ## Contributing
39
+
40
+ 1. Fork it
41
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
42
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
43
+ 4. Push to the branch (`git push origin my-new-feature`)
44
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,408 @@
1
+ #!/usr/bin/env ruby
2
+ require "mongo/jira/version"
3
+
4
+ require 'commander/import'
5
+ require 'jira'
6
+ require 'mongo/jira'
7
+ require 'json'
8
+ require 'ruby-progressbar'
9
+
10
+ $config = "#{ENV['HOME']}/.mongo-jira.json"
11
+ $level = 0
12
+ tengen = HighLine::ColorScheme.new do |cs|
13
+ cs[:released] = [:bold, :green]
14
+ cs[:rc] = [:bold, :cyan]
15
+ cs[:error] = [:bold, :red]
16
+ cs[:password] = [:red]
17
+ cs[:dev_only] = [:bold, :reverse]
18
+ cs[:tengen] = [:bold, :green]
19
+ cs[:downloaded] = [:green]
20
+ cs[:p1] = [:bold, :red]
21
+ cs[:p2] = [:red]
22
+ cs[:p3] = [:cyan]
23
+ cs[:p4] = [:green]
24
+ cs[:p4] = [:green]
25
+ end
26
+ HighLine.color_scheme = tengen
27
+
28
+ program :version, '0.0.0'
29
+ program :description, 'Set of JIRA command line tools'
30
+ program(:help, 'Common Errors',<<END)
31
+ error: SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed.
32
+ Try :
33
+
34
+ rvm osx-ssl-certs update all
35
+ END
36
+
37
+ program(:help, 'Reporting Errors',<<END)
38
+ add --trace to the end of the command , re-run and email me the log.
39
+
40
+ END
41
+
42
+ program(:help, 'Supported Versions',<<END)
43
+ I've tested the gem with the following ruby versions:
44
+
45
+ ruby-1.9.2
46
+ ruby-1.9.3
47
+ ruby-2.0.0
48
+
49
+ It does not work on 1.8.7 :
50
+
51
+ activesupport requires Ruby version >= 1.9.3.
52
+
53
+ END
54
+
55
+ global_option('-c', '--creds FILE', 'Load config data for your commands to use') { |file| $config = file }
56
+ global_option('-V', 'increment verbosity level, i.e. -VV for higher debug level') { $level += 1}
57
+
58
+ default_command :projects
59
+
60
+ command :init do |c|
61
+ c.syntax = 'jira init [options]'
62
+ c.summary = 'Run this command to initialize jira mongo gem'
63
+ c.description = 'This command will walk you through the account details that are needed.'
64
+ c.example 'description', 'jira init'
65
+ c.action do
66
+ if Mongo::Jira::Config::check($config)
67
+ exit 1 unless agree("#{$config} exists, <%= color('Overwrite?', RED) %> ")
68
+ end
69
+
70
+ conf = {}
71
+ conf[:username] = prompt('username', 'jim.oleary@10gen.com')
72
+ conf[:password] = ask("password (<%= color('hit ENTER for none', :password) %>) ? ") { |q| q.echo = '*' }
73
+ conf[:site] = prompt('site', 'https://jira.mongodb.org')
74
+ conf[:auth_type] = prompt('auth_type', 'basic')
75
+ conf.delete(:password) if conf[:password].blank?
76
+
77
+ Mongo::Jira::Config::save(conf, $config)
78
+ end
79
+ end
80
+
81
+ command :download do |c|
82
+ c.syntax = 'jira download <ticket>* [options]'
83
+ c.summary = 'download files attached to this ticket'
84
+ c.description=<<END
85
+ Download all the files attached attached to a ticket.
86
+
87
+ If no parameters are supplied then all new attachments will be downloaded. By default the file
88
+ structure is flattened and the files are downloaded to the 'attachment' directory.
89
+
90
+ If the '--force' flag is used then all or files matching a glob are downloaded (regardless of whether they
91
+ are currently downloaded). By default the force pattern is a glob and '*' are prepended and appended to the glob.
92
+ So the following 2 commands are equivalent :
93
+
94
+ $> jira download pr-1111
95
+ $> jira download pr-1111 pr-2222
96
+ $> jira download pr-1111 --force filename
97
+ $> jira download pr-1111 --force '*filename*'
98
+ $> jira download pr-1111 --force '*filename*' --to attachment
99
+
100
+ Multiple patterns can also be supplied:
101
+
102
+ $> jira download pr-1111 --force filename1,file2
103
+ END
104
+ c.example 'download all the new files from pr-1111', 'jira download pr-1111'
105
+ c.option '--dryrun', 'don\'t do the download just print what would happen '
106
+ c.option '-f','--[no-]flatten', 'flatten directory'
107
+ c.option '--silent', 'don\'t display progress'
108
+ c.option '--force [GLOBS]', String, 'force download of matching files'
109
+ c.option '--to LOCATION', String, 'download to LOCATION, defaults to ticket name'
110
+ c.action do |args,options|
111
+ options.default(:dryrun => false, :silent => false, :flatten=>true, :to=>nil)
112
+ if options.force.nil? || options.force.is_a?(TrueClass)
113
+ force = (options.force.nil? ? %w() : %w(*))
114
+ else
115
+ force = options.force.split(/[,\s]/).collect{|g| "*#{g.chomp}*"}
116
+ end
117
+
118
+ tids = args|| %w(.*)
119
+ tids.each do |tid|
120
+ ticket = client.find(tid)
121
+
122
+ unless ticket
123
+ puts "no ticket for #{tid}"
124
+ next
125
+ end
126
+ trace JSON.pretty_generate(ticket.attrs) if ticket
127
+
128
+ #noinspection RubyResolve
129
+ ticket.attachments.each do |attachment|
130
+ trace JSON.pretty_generate(attachment.attrs)
131
+ u = URI.parse(attachment.content)
132
+ parts = u.path.split('/')#[2..-1].join('/')
133
+ to = options.to.nil? ? tid : parts[2]
134
+ trace "to -> #{to}"
135
+ if options.flatten
136
+ target = [to, parts.last]
137
+ else
138
+ target = [to, parts[3..-1]]
139
+ end
140
+ target = File.join(target.flatten)
141
+
142
+ trace target
143
+ size = attachment.attrs['size']
144
+ debug "checking #{target} => #{File.exists?(target)}"
145
+ if !force.any?{|g|File.fnmatch(g, target)} && File.exists?(target) && File.size(target) == size
146
+ say("<%= color('skipping', :downloaded) %> already downloaded '#{target}'")
147
+ next
148
+ end
149
+
150
+ name = target.split(%r{/}).last
151
+ title = name[0..14].ljust(15)
152
+ format = '%T %a |%b>%i|'
153
+ length= 80
154
+
155
+ pb = ProgressBar.create(:format => format,
156
+ :title => title,
157
+ :total => size,
158
+ :throttle_rate => 0.1,
159
+ :length => length)
160
+ pb = DummyProgressBar.new(size) if options.silent
161
+
162
+ progress=0
163
+ if options.dryrun
164
+ pb.format('%T |%b>%i|')
165
+ pb.title = name[0..29].ljust(30)
166
+ pb.progress=size
167
+ else
168
+ client.get(u.path) do |response|
169
+ FileUtils.mkdir_p(File::dirname(target))
170
+ debug u, ' => ', target
171
+ open target, 'w' do |io|
172
+ response.read_body do |chunk|
173
+ progress+=chunk.length
174
+ if progress == size
175
+ pb.format('%T |%b>%i|')
176
+ pb.title = name[0..29].ljust(30)
177
+ end
178
+ pb.progress=progress
179
+ io.write chunk
180
+ end
181
+ end
182
+ end
183
+ end
184
+ end
185
+
186
+ end
187
+ end
188
+ end
189
+
190
+ command :projects do |c|
191
+ c.syntax = 'jira projects <glob> [options]'
192
+ c.summary = 'list all the projects'
193
+ c.description = ''
194
+ c.example 'description', 'command example'
195
+ c.action do |args|
196
+ glob = args.first || '.*'
197
+ client.projects.each do |project|
198
+ say "#{project.key} - #{project.name}" if project.key =~ %r{#{glob}}
199
+ end
200
+ end
201
+ end
202
+
203
+ alias_command :test, :projects
204
+
205
+ command :show do |c|
206
+ c.syntax = 'jira ticket <id>* [options]'
207
+ c.summary = 'view a ticket or list of tickets'
208
+ c.description= <<END
209
+
210
+ $> jira show pr-1111
211
+ $> jira ticket pr-1111
212
+ $> jira show pr-1111 pr-2222
213
+ $> jira show pr-1111 --reverse | less -R
214
+ END
215
+ c.option '--reverse', 'print the comments in reverse order'
216
+ c.action do |args, options|
217
+ (args || %w(.*)).each do |tid|
218
+ ticket = client.find(tid)
219
+ next unless ticket
220
+ trace JSON.pretty_generate(ticket.attrs)
221
+ desc = Proc.new do
222
+ say <<END
223
+ #{header(ticket).join(' - ')}
224
+
225
+ #{('-' * 40)}
226
+
227
+ #{ticket.description}
228
+ END
229
+ end
230
+
231
+ desc.call() unless options.reverse
232
+
233
+ comments = ticket.comment['comments']
234
+ comments.reverse! if options.reverse
235
+ comments.each do |cmnt|
236
+ say <<END
237
+
238
+ #{'=' * 40}
239
+
240
+ #{comment(cmnt).join(' - ')}
241
+
242
+ #{cmnt['body']}
243
+ END
244
+ end
245
+ desc.call() if options.reverse
246
+ end
247
+ end
248
+ end
249
+ alias_command :ticket, :show
250
+
251
+ command :open do |c|
252
+ c.syntax = 'jira open <ids>* [options]'
253
+ c.summary = 'open a ticket'
254
+ c.description = 'open a ticket in the browser'
255
+ c.example 'description', 'jira open pr-1111'
256
+ c.option '--some-switch', 'Some switch that does something'
257
+ c.action do |args|
258
+ (args || %w(.*)).each do |tid|
259
+ ticket = client.find(tid)
260
+ if ticket
261
+ trace JSON.pretty_generate(ticket.attrs)
262
+ %x{open 'https://jira.mongodb.org/browse/#{tid}'}
263
+ end
264
+ end
265
+ end
266
+ end
267
+
268
+ command :query do |c|
269
+ c.syntax = 'jira query <jql> [options]'
270
+ c.summary = 'run a jql query '
271
+ c.description =<<END
272
+ Use JQL syntax to find tickets.
273
+
274
+ Sample queries :
275
+ $> jira query 'project = CS AND labels = fs AND (Assignee is NULL) AND status = Open ORDER BY priority DESC' -> commercial support, no owner
276
+ $> jira query 'project = CS AND (Assignee is NULL) AND status = Open ORDER BY priority DESC' -> commercial support, no owner
277
+ $> jira query 'project in (CS, MMSSUPPORT) AND (Assignee is NULL) AND status = Open ORDER BY priority DESC' -> commercial support, no owner
278
+ $> jira query 'label = "fs"' -> find issues resolved by ME
279
+ $> jira query 'status WAS "Resolved" BY currentUser()' -> find issues resolved by ME
280
+ $> jira query 'assignee = currentUser() OR owner = currentUser() ' -> find issues resolved by ME
281
+ $> jira query 'owner = currentUser() AND status in (open , "waiting for customer") '
282
+ $> jira query 'assignee is EMPTY AND status in ("waiting for customer") ORDER BY updated DESC '
283
+ END
284
+ c.example 'description', 'command example'
285
+ c.action do |args|
286
+ q = args.join(' ')
287
+ debug ":find '#{q}'"
288
+ client.jql(q).each do |ticket|
289
+ trace JSON.pretty_generate(ticket.attrs)
290
+ say header(ticket).join(' - ')
291
+ end
292
+ end
293
+ end
294
+
295
+ command :find do |c|
296
+ c.syntax = 'jira find <key words>'
297
+ c.summary = 'find ticket with keywords matching the following fields "summary description comment"'
298
+ c.description =<<END
299
+ Find tickets by keywords
300
+
301
+ Sample find:
302
+ $> jira find Test aggregation framework # match any of the words
303
+ $> jira find 'Test aggregation framework' # match any of the words
304
+ $> jira find '"Test aggregation framework"' # match exact phrase
305
+ END
306
+ c.action do |args|
307
+ pattern = args.join(' ')
308
+ q = %w{ summary description comment}.collect { |field| "#{field} ~ '#{pattern}'" }.join(' OR ')
309
+ debug ":find '#{q}'"
310
+ client.jql(q).each do |ticket|
311
+ trace JSON.pretty_generate(ticket.attrs)
312
+ say header(ticket).join(' - ')
313
+ end
314
+ end
315
+ end
316
+
317
+ def debug(*args)
318
+ puts args.join if $level > 0
319
+ end
320
+
321
+ def trace(*args)
322
+ puts args.join if $level > 1
323
+ end
324
+
325
+ def client(config=$config)
326
+ $jira ||= begin
327
+ jira = Mongo::Jira::Main.new(config)
328
+ jira.password = ask('Password: ') { |q| q.echo = '*' } unless jira.password?
329
+ jira
330
+ rescue Mongo::Jira::ConfigException
331
+ say("\n <%= color('UNEXPECTED ERROR', :error) %>\n\n conf file not found :: <%= color(\"#{config}\", RED) %>\n")
332
+ command(:help).run('init')
333
+ abort ''
334
+ end
335
+ end
336
+
337
+ # @param [Object] default
338
+ def prompt(name, default=nil)
339
+ ask("#{name} (<%= color(\"#{default}\", RED) %>) ? ", lambda { |v| v.blank? ? default : v })
340
+ end
341
+
342
+ def header(ticket)
343
+ [ticket.key.ljust(15) ,ticket.summary , fix_versions(ticket), priority(ticket)].compact
344
+ end
345
+
346
+ def comment(comment)
347
+ hdr = "#{comment['author']['displayName']} (#{comment['author']['name']})"
348
+ hdr = "<%= color(\"#{hdr}\", :tengen) %>" if comment['author']['emailAddress'] =~ /@10gen.com$/
349
+
350
+ trace JSON.pretty_generate(comment)
351
+ dev_only = case (comment['visibility']||{})['value']
352
+ when /developer/i
353
+ "<%= color('Dev Only', :dev_only) %>"
354
+ when /customer/i
355
+ "<%= color('Customer Only', :dev_only) %>"
356
+ else
357
+ nil
358
+ end
359
+
360
+ [hdr , DateTime.parse(comment['updated']).strftime('%D %T'), dev_only].compact
361
+ end
362
+
363
+ def fix_versions(ticket)
364
+ if ticket.try(:fixVersions)
365
+ trace "fixVersions #{JSON.pretty_generate(ticket.fixVersions)}"
366
+ unless ticket.fixVersions.compact.empty?
367
+ labels = ticket.fixVersions.collect do |v|
368
+ c = case v['name']
369
+ when /^\d+\.(0|2|4|6|8)/
370
+ ':released'
371
+ when /^\d+\.(1|3|5|7|9)/
372
+ ':rc'
373
+ else
374
+ ':error'
375
+ end
376
+ "<%= color(\"#{v['name']}\", #{c}) %>"
377
+ end
378
+ labels.join(',')
379
+ end
380
+ else
381
+ nil
382
+ end
383
+ end
384
+
385
+ def priority(ticket)
386
+ if ticket.respond_to?(:priority)
387
+ trace "priority #{JSON.pretty_generate(ticket.priority.attrs)}"
388
+ c = case ticket.priority.attrs['name']
389
+ when /p1$/i ; ':p1'
390
+ when /p2$/i ; ':p2'
391
+ when /p3$/i ; ':p3'
392
+ when /p4$/i ; ':p4'
393
+ when /p5$/i ; ':p5'
394
+ else
395
+ ':error'
396
+ end
397
+ "<%= color(\"#{ticket.priority.attrs['name']}\", #{c}) %>"
398
+ end
399
+ end
400
+
401
+ class DummyProgressBar
402
+ def initialize(s)
403
+ @size = s
404
+ end
405
+ def method_missing(meth, *args)
406
+ print '.' if meth.to_s == 'progress=' && args[0] == @size
407
+ end
408
+ end
@@ -0,0 +1,53 @@
1
+ require "mongo/jira/version"
2
+
3
+ require 'mongo/jira/config'
4
+ require 'mongo/jira/patch'
5
+ module Mongo
6
+
7
+ #noinspection ALL
8
+ module Jira
9
+ class Main
10
+
11
+ attr_accessor :filename, :jira, :config
12
+
13
+ def initialize(file)
14
+ @filename= file
15
+ end
16
+
17
+ def find(tid)
18
+ jira.Issue.find(tid)
19
+ rescue JIRA::HTTPError => e
20
+ raise e if e.code == '401' || e.code == '403'
21
+ nil
22
+ end
23
+
24
+ def jql(qry)
25
+ JIRA::Resource::Issue.jql(jira,qry)
26
+ end
27
+
28
+ def projects
29
+ jira.Project.all
30
+ end
31
+
32
+ def jira
33
+ @jira ||= JIRA::Client.new(config.config)
34
+ end
35
+
36
+ def password?
37
+ config.password?
38
+ end
39
+ def password=(pw)
40
+ config.password=pw
41
+ end
42
+
43
+ def config
44
+ @config ||= Mongo::Jira::Config.new(filename)
45
+ end
46
+
47
+ def method_missing(m, *args, &block)
48
+ jira.send(m, *args, &block)
49
+ end
50
+
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,109 @@
1
+ require 'openssl'
2
+ begin
3
+ require 'Base64s'
4
+ rescue LoadError
5
+ module Base64
6
+ def decode64(str)
7
+ str.unpack('m0').first
8
+ end
9
+ def encode64(bin)
10
+ [bin].pack('m0')
11
+ end
12
+ end
13
+ end
14
+ require 'digest'
15
+ require 'mongo/jira/config_exception'
16
+
17
+ module Mongo
18
+ module Jira
19
+ class Config
20
+
21
+ CFG_FILE="#{ENV['HOME']}/.mongo_jira.json"
22
+
23
+ # check the config file
24
+ #
25
+ # @example Save to default location and specific location.
26
+ # Mongo::Jira::Config::check()
27
+ # Mongo::Jira::Config::check("/tmp/.jira.json")
28
+ #
29
+ # @param [ String ] filename The config file path. It defaults to "#{ENV['HOME']}/.mongo_jira.json"
30
+ #
31
+ # @return [ true, false ] If the config is ok.
32
+ #
33
+ # @since 0.0.1
34
+ def self.check(filename=CFG_FILE)
35
+ File::exists?(filename)
36
+ end
37
+
38
+ def self.save(config, filename)
39
+ cpy = config.clone
40
+ if config[:password]
41
+ cpy[:password] = Mongo::Jira::Config::encrypt('password',cpy[:password])
42
+ else
43
+ cpy.delete(:password)
44
+ end
45
+ cpy[:context_path] ||= ''
46
+
47
+ File.open(filename , 'w') {|f| f.write(JSON.pretty_generate(cpy)) }
48
+ end
49
+
50
+ def self.load(filename=CFG_FILE)
51
+ Config.new(filename)
52
+ end
53
+
54
+ attr_accessor :filename , :config
55
+ def initialize(file)
56
+ raise ConfigException , "file not found #{file}" unless File::exists?(file)
57
+ @filename=file
58
+ @config = JSON.parse( IO.read(filename) )
59
+ config.symbolize_keys!()
60
+ config[:auth_type] = (config[:auth_type] || :basic).to_sym
61
+ config[:context_path] = ''
62
+ config[:password] = Mongo::Jira::Config::decrypt("password", config[:password]) if config[:password]
63
+ end
64
+
65
+ # save the config file
66
+ #
67
+ # @example write the config file
68
+ # Mongo::Jira::Config::check()
69
+ # Mongo::Jira::Config::check("/tmp/.jira.json")
70
+ #
71
+ # @param [ String ] filename The config file path. It defaults to "#{ENV['HOME']}/.mongo_jira.json"
72
+ #
73
+ # @return [ true, false ] If the config is ok.
74
+ #
75
+ # @since 0.0.1
76
+ def save
77
+ Mongo::Jira::Config::save(config,filename)
78
+ end
79
+
80
+ def password?
81
+ config[:password]
82
+ end
83
+
84
+ def password=(pw)
85
+ config[:password]=pw
86
+ end
87
+
88
+ def self.encrypt(key, value)
89
+ cipher = OpenSSL::Cipher::Cipher.new("des3")
90
+ cipher.encrypt # Call this before setting key or iv
91
+ cipher.key = key * 10
92
+ cipher.iv = '12345678'
93
+ text = cipher.update(value)
94
+ text << cipher.final
95
+ Base64.encode64(text).chomp
96
+ end
97
+
98
+ def self.decrypt(key, data)
99
+ cipher = OpenSSL::Cipher::Cipher.new("des3")
100
+ cipher.decrypt
101
+ cipher.key = key * 10
102
+ cipher.iv = '12345678'
103
+ plaintext = cipher.update(Base64.decode64(data))
104
+ plaintext << cipher.final
105
+ end
106
+
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,5 @@
1
+ module Mongo
2
+ module Jira
3
+ class ConfigException < StandardError; end
4
+ end
5
+ end
@@ -0,0 +1,35 @@
1
+ require 'jira'
2
+ ## monkey patch for progress bar
3
+ module JIRA
4
+ class RequestClient
5
+
6
+ # Returns the response if the request was successful (HTTP::2xx) and
7
+ # raises a JIRA::HTTPError if it was not successful, with the response
8
+ # attached.
9
+
10
+ def request(*args,&block)
11
+ response = make_request(*args,&block)
12
+ raise HTTPError.new(response) unless response.kind_of?(Net::HTTPSuccess)
13
+ response
14
+ end
15
+ end
16
+
17
+ class HttpClient < RequestClient
18
+
19
+ def make_request(http_method, path, body='', headers={}, &block)
20
+ request = Net::HTTP.const_get(http_method.to_s.capitalize).new(path, headers)
21
+ request.body = body unless body.nil?
22
+ request.basic_auth(@options[:username], @options[:password])
23
+ basic_auth_http_conn.request(request, &block)
24
+ end
25
+ end
26
+
27
+ class Client
28
+ def get(path, headers = {}, &block)
29
+ request(:get, path, nil, merge_default_headers(headers), &block)
30
+ end
31
+ def request(http_method, path, body = '', headers={}, &block)
32
+ @request_client.request(http_method, path, body, headers, &block)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,5 @@
1
+ module Mongo
2
+ module Jira
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'mongo/jira/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = 'mongo-jira'
8
+ s.version = Mongo::Jira::VERSION #'0.0.0'
9
+ s.platform = Gem::Platform::RUBY
10
+ s.authors = ['Jim O\'Leary']
11
+ s.email = %q{jim.oleary@gmail.com}
12
+ s.homepage = %q{http://rubygems.org/gems/mongo_jira}
13
+ s.summary = %q{Simple Mongo Jira command line tools}
14
+ s.description = %q{Easy/Simple Jira command line tool}
15
+ s.rubyforge_project = 'mongo_jira'
16
+ s.signing_key = "#{ENV['HOME']}/.ssh/gem-private_key.pem"
17
+ s.cert_chain = ["#{ENV['HOME']}/.ssh/gem-public_cert.pem"]
18
+
19
+
20
+ s.files = `git ls-files`.split("\n")
21
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
22
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
23
+ s.require_paths = ["lib"]
24
+
25
+
26
+ s.add_dependency 'jira-ruby'#, ['>= 0.1.2']
27
+ s.add_dependency 'commander'#, ['>= 4.1.5']
28
+ s.add_dependency 'ruby-progressbar'#, ['>= 1.2.0']
29
+ end
@@ -0,0 +1,9 @@
1
+ require 'test/unit'
2
+ require 'mongo_jira'
3
+
4
+ class Mongo::JiraTest < Test::Unit::TestCase
5
+ # add some tests
6
+ # def test_spanish_hello
7
+ # assert_equal "hola mundo", Hola.hi("spanish")
8
+ # end
9
+ end
metadata ADDED
@@ -0,0 +1,129 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mongo-jira
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Jim O'Leary
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain:
11
+ - !binary |-
12
+ LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURlRENDQW1DZ0F3SUJB
13
+ Z0lCQVRBTkJna3Foa2lHOXcwQkFRVUZBREJCTVJNd0VRWURWUVFEREFwcWFX
14
+ MHUKYjJ4bFlYSjVNUlV3RXdZS0NaSW1pWlB5TEdRQkdSWUZaMjFoYVd3eEV6
15
+ QVJCZ29Ka2lhSmsvSXNaQUVaRmdOagpiMjB3SGhjTk1UTXdPREl3TVRJMU5U
16
+ VXpXaGNOTVRRd09ESXdNVEkxTlRVeldqQkJNUk13RVFZRFZRUUREQXBxCmFX
17
+ MHViMnhsWVhKNU1SVXdFd1lLQ1pJbWlaUHlMR1FCR1JZRloyMWhhV3d4RXpB
18
+ UkJnb0praWFKay9Jc1pBRVoKRmdOamIyMHdnZ0VpTUEwR0NTcUdTSWIzRFFF
19
+ QkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDMHpZczdrOFcyQmx3bQo0aHVLeWJh
20
+ dk9qTzl0NVdDaDZEbkJ0WWh6OTJ4NUhSS1lGWXJTSlNzOFJiWjYvVFQ3SEUx
21
+ bm1pRmNwV3hUNEpCCnVneVMrMnplWVBJeVNMck1qT25XNFZEaXp2Z0tFM2li
22
+ V3UvZC9pa1NZRXZldTl4Zkx1RjlzekVrODl1RTdwYy8Kc0JxdXc5dXAwZ3Zw
23
+ aU9wZ0E3Q01VU3hmajRLU2ZQNmtoR1k4eUtHUUtoalYvNDBSdkxFKzB5Q1gx
24
+ REZRam8xNQpBQ1BqUVBCWjFoWHVZd0M4TkpKSzlKY0h0ZitpT0JWZng5Q1NP
25
+ S2ZpbGdsMzZiZWgwZ0hibFJmOWFFVWExR0ZTCm5YN0pWcDI1SUs1K3VxQll6
26
+ UFJtVWprNDBwZUFQWnNyWmVyd1B1NzZzUzR0N1d3N3FzVGlnbHdQeFZPS2Zw
27
+ TnQKS3JFck1hYWZBZ01CQUFHamV6QjVNQWtHQTFVZEV3UUNNQUF3Q3dZRFZS
28
+ MFBCQVFEQWdTd01CMEdBMVVkRGdRVwpCQlRnbUM0dFpEeGZOQzRldGhJMDkw
29
+ WGFUSmdoSkRBZkJnTlZIUkVFR0RBV2dSUnFhVzB1YjJ4bFlYSjVRR2R0CllX
30
+ bHNMbU52YlRBZkJnTlZIUklFR0RBV2dSUnFhVzB1YjJ4bFlYSjVRR2R0WVds
31
+ c0xtTnZiVEFOQmdrcWhraUcKOXcwQkFRVUZBQU9DQVFFQUVRdlJJRVVqOU5r
32
+ RUJtV09mdWYyMHhraHVLSkhIbCt2b3F3SXNtMEFITWpiMWIzSgpRYUhoOVdC
33
+ QTBJSzgySTk2dmlIOVBTZUlLOE5oM1hKOUxWQk80OXFtemovL3FkN0lTNVpk
34
+ TW1vcHZMWFIrVU93CnFtZi9yRlYwOWZaWnFHMmdUS3VhMDdMOC9LYXlpNHJZ
35
+ ZXZKUytJcGlJemZ0Si9mOEhSbHJGQ1NGaDdSK1gzZFMKc3drR05ORi9ZZGJl
36
+ MXdicFBBMWc2cS9OeVZVWlpYbzBjek5KR1hUZFZtWGJPY3pQY1dOTTV2S3Bq
37
+ bjhKN2pXbApUTHdTWGw3OHNxTjZPRktESjk2R1N4TXJuWTl0MGZRTzJrTXd5
38
+ QkRtZ0g0My9vVWFuSjhoMDZ6djFlckdwY3VjClJTR2VCbW9ycjBaWnBmQ1Bv
39
+ S0FMU3VXc2xBa1pXTy9XUnZQZENRPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUt
40
+ LS0tLQo=
41
+ date: 2013-08-20 00:00:00.000000000 Z
42
+ dependencies:
43
+ - !ruby/object:Gem::Dependency
44
+ name: jira-ruby
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ! '>='
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ type: :runtime
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ! '>='
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ - !ruby/object:Gem::Dependency
58
+ name: commander
59
+ requirement: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ! '>='
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ type: :runtime
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ - !ruby/object:Gem::Dependency
72
+ name: ruby-progressbar
73
+ requirement: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ type: :runtime
79
+ prerelease: false
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ! '>='
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ description: Easy/Simple Jira command line tool
86
+ email: jim.oleary@gmail.com
87
+ executables:
88
+ - jira
89
+ extensions: []
90
+ extra_rdoc_files: []
91
+ files:
92
+ - .gitignore
93
+ - Gemfile
94
+ - LICENSE
95
+ - README.md
96
+ - Rakefile
97
+ - bin/jira
98
+ - lib/mongo/jira.rb
99
+ - lib/mongo/jira/config.rb
100
+ - lib/mongo/jira/config_exception.rb
101
+ - lib/mongo/jira/patch.rb
102
+ - lib/mongo/jira/version.rb
103
+ - mongo-jira.gemspec
104
+ - test/test_jira_mongo.rb
105
+ homepage: http://rubygems.org/gems/mongo_jira
106
+ licenses: []
107
+ metadata: {}
108
+ post_install_message:
109
+ rdoc_options: []
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ! '>='
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ required_rubygems_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ! '>='
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ requirements: []
123
+ rubyforge_project: mongo_jira
124
+ rubygems_version: 2.0.3
125
+ signing_key:
126
+ specification_version: 4
127
+ summary: Simple Mongo Jira command line tools
128
+ test_files:
129
+ - test/test_jira_mongo.rb
Binary file