ruby-jira-cli 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +47 -0
- data/bin/jira +26 -0
- data/lib/jira/api.rb +76 -0
- data/lib/jira/auth_api.rb +11 -0
- data/lib/jira/command.rb +60 -0
- data/lib/jira/commands/all.rb +72 -0
- data/lib/jira/commands/assign.rb +65 -0
- data/lib/jira/commands/attachments.rb +45 -0
- data/lib/jira/commands/checkout.rb +87 -0
- data/lib/jira/commands/comment/add.rb +52 -0
- data/lib/jira/commands/comment/delete.rb +80 -0
- data/lib/jira/commands/comment/list.rb +72 -0
- data/lib/jira/commands/comment/update.rb +88 -0
- data/lib/jira/commands/comment.rb +14 -0
- data/lib/jira/commands/delete.rb +92 -0
- data/lib/jira/commands/describe.rb +64 -0
- data/lib/jira/commands/install.rb +121 -0
- data/lib/jira/commands/link.rb +94 -0
- data/lib/jira/commands/log/add.rb +52 -0
- data/lib/jira/commands/log/delete.rb +80 -0
- data/lib/jira/commands/log/list.rb +69 -0
- data/lib/jira/commands/log/update.rb +89 -0
- data/lib/jira/commands/log.rb +13 -0
- data/lib/jira/commands/new.rb +174 -0
- data/lib/jira/commands/rename.rb +53 -0
- data/lib/jira/commands/sprint.rb +109 -0
- data/lib/jira/commands/tickets.rb +55 -0
- data/lib/jira/commands/transition.rb +97 -0
- data/lib/jira/commands/version.rb +10 -0
- data/lib/jira/commands/vote/add.rb +43 -0
- data/lib/jira/commands/vote/delete.rb +46 -0
- data/lib/jira/commands/vote/list.rb +59 -0
- data/lib/jira/commands/vote.rb +12 -0
- data/lib/jira/commands/watch/add.rb +43 -0
- data/lib/jira/commands/watch/delete.rb +46 -0
- data/lib/jira/commands/watch/list.rb +59 -0
- data/lib/jira/commands/watch.rb +12 -0
- data/lib/jira/constants.rb +7 -0
- data/lib/jira/core.rb +101 -0
- data/lib/jira/exceptions.rb +3 -0
- data/lib/jira/format.rb +78 -0
- data/lib/jira/sprint_api.rb +33 -0
- data/lib/jira.rb +19 -0
- metadata +202 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b229a4bc96644bd3e819673aa12a382530c9ed74
|
4
|
+
data.tar.gz: 0273cb796258b0e39599830647b9dedff8519479
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e14df53c00913ae286f0cd053f4f8d92adbb8526f2271d4201e217b72ba166e1a967796988c38b4851d40b0da1797f14a2609b8dcf4aee041aa3932ca6c8cee5
|
7
|
+
data.tar.gz: 6ee3d17a550cf5f974440e032af391edd58b5e108c4f60e610c96958e7a9c4890c4807086151350ec1fd05e60459f48e003324bd984e302ce88881f55423dede
|
data/README.md
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# JIRA CLI
|
2
|
+
|
3
|
+
Ruby gem CLI tool used to manage JIRA workflows leveraging git
|
4
|
+
|
5
|
+
* * *
|
6
|
+
|
7
|
+
### Available Commands
|
8
|
+
|
9
|
+
jira all # Describes all local branches that match JIRA ticketing syntax
|
10
|
+
jira assign # Assign a ticket to a user
|
11
|
+
jira attachments # View ticket attachments
|
12
|
+
jira checkout <ticket> # Checks out a ticket from JIRA in the git branch
|
13
|
+
jira comment <command> # Commands for comment operations in JIRA
|
14
|
+
jira delete # Deletes a ticket in JIRA and the git branch
|
15
|
+
jira describe # Describes the input ticket
|
16
|
+
jira help [COMMAND] # Describe available commands or one specific command
|
17
|
+
jira install # Guides the user through JIRA CLI installation
|
18
|
+
jira link # Creates a link between two tickets in JIRA
|
19
|
+
jira log <command> # Commands for logging operations in JIRA
|
20
|
+
jira new # Creates a new ticket in JIRA and checks out the git branch
|
21
|
+
jira rename # Updates the summary of the input ticket
|
22
|
+
jira sprint # Lists sprint info
|
23
|
+
jira tickets [jql] # List the in progress tickets of the input username (or jql)
|
24
|
+
jira transition # Transitions the input ticket to the next state
|
25
|
+
jira version # Displays the version
|
26
|
+
jira vote <command> # Commands for voting operations in JIRA
|
27
|
+
jira watch <command> # Commands for watching tickets in JIRA
|
28
|
+
|
29
|
+
### Gem Installation
|
30
|
+
|
31
|
+
Rubygems:
|
32
|
+
|
33
|
+
gem install ruby-jira-cli
|
34
|
+
|
35
|
+
Manually:
|
36
|
+
|
37
|
+
git clone git@github.com:ajmyers01/jira-cli.git
|
38
|
+
cd jira-cli
|
39
|
+
./scripts/install
|
40
|
+
|
41
|
+
### Project Installation
|
42
|
+
|
43
|
+
In order to use this tool, you'll need to run the installation script in the
|
44
|
+
git repository that you're managing via JIRA.
|
45
|
+
|
46
|
+
cd path/to/jira/repo
|
47
|
+
jira install
|
data/bin/jira
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'jira'
|
4
|
+
begin
|
5
|
+
Jira::CLI.start
|
6
|
+
File.delete(Jira::Core.rescue_cookie_path) if File.exist?(Jira::Core.rescue_cookie_path)
|
7
|
+
rescue Faraday::Error, UnauthorizedException
|
8
|
+
if Jira::CLI.new.try_install_cookie
|
9
|
+
if File.read(Jira::Core.rescue_cookie_path).count('r') < 3
|
10
|
+
puts "Re-running: jira #{ARGV.join(' ')}"
|
11
|
+
Process.waitpid(
|
12
|
+
Process.fork do
|
13
|
+
Process.exec("jira #{ARGV.join(' ')}")
|
14
|
+
end
|
15
|
+
)
|
16
|
+
exit
|
17
|
+
end
|
18
|
+
end
|
19
|
+
puts "JIRA failed connect, you may need to rerun 'jira install'"
|
20
|
+
rescue GitException
|
21
|
+
puts "JIRA commands can only be run within a git repository."
|
22
|
+
rescue InstallationException
|
23
|
+
puts "Please run #{Jira::Format.summary('jira install')} before "\
|
24
|
+
"running this command."
|
25
|
+
rescue Interrupt
|
26
|
+
end
|
data/lib/jira/api.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
module Jira
|
2
|
+
class API
|
3
|
+
|
4
|
+
def get(path, options={})
|
5
|
+
response = client.get(path, options[:params] || {}, headers)
|
6
|
+
process(response, options)
|
7
|
+
end
|
8
|
+
|
9
|
+
def post(path, options={})
|
10
|
+
response = client.post(path, options[:params] || {}, headers)
|
11
|
+
process(response, options)
|
12
|
+
end
|
13
|
+
|
14
|
+
def patch(path, options={})
|
15
|
+
response = client.put(path, options[:params] || {}, headers)
|
16
|
+
process(response, options)
|
17
|
+
end
|
18
|
+
|
19
|
+
def delete(path, options={})
|
20
|
+
response = client.delete(path, options[:params] || {}, headers)
|
21
|
+
process(response, options)
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
|
26
|
+
def process(response, options)
|
27
|
+
raise UnauthorizedException if response.status == 401
|
28
|
+
body = response.body || {}
|
29
|
+
json = (body if body.class == Hash) || {}
|
30
|
+
if response.success? && json['errorMessages'].nil?
|
31
|
+
respond_to(options[:success], body)
|
32
|
+
else
|
33
|
+
puts json['errorMessages'].join('. ') unless json['errorMessages'].nil?
|
34
|
+
respond_to(options[:failure], body)
|
35
|
+
end
|
36
|
+
body
|
37
|
+
end
|
38
|
+
|
39
|
+
def respond_to(block, body)
|
40
|
+
return if block.nil?
|
41
|
+
case block.arity
|
42
|
+
when 0
|
43
|
+
block.call
|
44
|
+
when 1
|
45
|
+
block.call(body)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def client
|
50
|
+
@client ||= Faraday.new(endpoint) do |faraday|
|
51
|
+
faraday.request :basic_auth, Jira::Core.username, Jira::Core.password unless Jira::Core.password.nil?
|
52
|
+
faraday.request :token_auth, Jira::Core.token unless Jira::Core.token.nil?
|
53
|
+
faraday.request :json
|
54
|
+
faraday.response :json
|
55
|
+
faraday.adapter :net_http
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def endpoint
|
60
|
+
"#{Jira::Core.url}/rest/api/2"
|
61
|
+
end
|
62
|
+
|
63
|
+
def headers
|
64
|
+
{ 'Content-Type' => 'application/json' }.merge(cookies)
|
65
|
+
end
|
66
|
+
|
67
|
+
def cookies
|
68
|
+
cookie = Jira::Core.cookie
|
69
|
+
unless cookie.empty?
|
70
|
+
return { 'cookie' => "#{cookie[:name]}=#{cookie[:value]}" }
|
71
|
+
end
|
72
|
+
{}
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
data/lib/jira/command.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
# external dependencies
|
2
|
+
require 'time'
|
3
|
+
|
4
|
+
# internal dependencies
|
5
|
+
require 'jira/api'
|
6
|
+
require 'jira/sprint_api'
|
7
|
+
require 'jira/auth_api'
|
8
|
+
require 'jira/core'
|
9
|
+
|
10
|
+
module Jira
|
11
|
+
module Command
|
12
|
+
class Base
|
13
|
+
|
14
|
+
def run
|
15
|
+
raise NotImplementedError
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
def api
|
21
|
+
@api ||= Jira::API.new
|
22
|
+
end
|
23
|
+
|
24
|
+
def auth_api
|
25
|
+
@auth_api ||= Jira::AuthAPI.new
|
26
|
+
end
|
27
|
+
|
28
|
+
# TODO: Move this to relevant subcommand Base
|
29
|
+
def body(text=nil)
|
30
|
+
@body ||= (
|
31
|
+
comment = text || io.ask("Leave a comment for ticket #{ticket}:", default: 'Empty comment').strip
|
32
|
+
comment = comment.gsub(/\@[a-zA-Z]+/, '[~\0]') || comment
|
33
|
+
comment.gsub('[~@', '[~') || comment
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
def sprint_api
|
38
|
+
@sprint_api ||= Jira::SprintAPI.new
|
39
|
+
end
|
40
|
+
|
41
|
+
def io
|
42
|
+
@io ||= TTY::Prompt.new
|
43
|
+
end
|
44
|
+
|
45
|
+
def render_table(header, rows)
|
46
|
+
puts TTY::Table.new(header, rows).render(:unicode, padding: [0, 1], multiline: true)
|
47
|
+
end
|
48
|
+
|
49
|
+
def truncate(string, limit=80)
|
50
|
+
return string if string.length < limit
|
51
|
+
string[0..limit-3] + '...'
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# load commands
|
59
|
+
commands_directory = File.join(File.dirname(__FILE__), 'commands', '*.rb')
|
60
|
+
Dir[commands_directory].each { |file| require file }
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Jira
|
2
|
+
class CLI < Thor
|
3
|
+
|
4
|
+
desc "all", "Describes all local branches that match JIRA ticketing syntax"
|
5
|
+
def all
|
6
|
+
Command::All.new.run
|
7
|
+
end
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
module Command
|
12
|
+
class All < Base
|
13
|
+
|
14
|
+
def run
|
15
|
+
if tickets.empty?
|
16
|
+
puts 'No tickets'
|
17
|
+
return
|
18
|
+
end
|
19
|
+
return if json.empty?
|
20
|
+
return unless errors.empty?
|
21
|
+
render_table(header, rows)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def header
|
27
|
+
[ 'Ticket', 'Assignee', 'Status', 'Summary' ]
|
28
|
+
end
|
29
|
+
|
30
|
+
def rows
|
31
|
+
json['issues'].map do |issue|
|
32
|
+
[
|
33
|
+
issue['key'],
|
34
|
+
issue['fields']['assignee']['name'] || 'Unassigned',
|
35
|
+
issue['fields']['status']['name'] || 'Unknown',
|
36
|
+
truncate(issue['fields']['summary'], 45)
|
37
|
+
]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def errors
|
42
|
+
@errors ||= (json['errorMessages'] || []).join('. ')
|
43
|
+
end
|
44
|
+
|
45
|
+
def json
|
46
|
+
@json ||= api.get "search", params: params
|
47
|
+
end
|
48
|
+
|
49
|
+
def params
|
50
|
+
{
|
51
|
+
jql: "key in (#{tickets.join(',')})"
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
def tickets
|
56
|
+
@tickets ||= (
|
57
|
+
tickets = []
|
58
|
+
branches.each do |branch|
|
59
|
+
ticket = branch.delete('*').strip
|
60
|
+
tickets << ticket if Jira::Core.ticket?(ticket, false)
|
61
|
+
end
|
62
|
+
tickets
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
def branches
|
67
|
+
`git branch`.strip.split("\n")
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Jira
|
2
|
+
class CLI < Thor
|
3
|
+
|
4
|
+
desc "assign", "Assign a ticket to a user"
|
5
|
+
method_option :assignee, aliases: "-a", type: :string, default: nil, lazy_default: "auto", banner: "ASSIGNEE"
|
6
|
+
def assign(ticket=Jira::Core.ticket)
|
7
|
+
Command::Assign.new(ticket, options).run
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
module Command
|
13
|
+
class Assign < Base
|
14
|
+
|
15
|
+
attr_accessor :ticket, :options
|
16
|
+
|
17
|
+
def initialize(ticket, options={})
|
18
|
+
self.ticket = ticket
|
19
|
+
self.options = options
|
20
|
+
end
|
21
|
+
|
22
|
+
def run
|
23
|
+
api.patch path,
|
24
|
+
params: params,
|
25
|
+
success: on_success,
|
26
|
+
failure: on_failure
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def on_success
|
32
|
+
-> do
|
33
|
+
puts "Ticket #{ticket} assigned to #{name}."
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def on_failure
|
38
|
+
->(json) do
|
39
|
+
message = (json['errors'] || {})['assignee']
|
40
|
+
puts message || "Ticket #{ticket} was not assigned."
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def path
|
45
|
+
"issue/#{ticket}/assignee"
|
46
|
+
end
|
47
|
+
|
48
|
+
def params
|
49
|
+
{ name: assignee }
|
50
|
+
end
|
51
|
+
|
52
|
+
def name
|
53
|
+
assignee == '-1' ? 'default user' : "'#{assignee}'"
|
54
|
+
end
|
55
|
+
|
56
|
+
def assignee
|
57
|
+
@assignee ||= (
|
58
|
+
assignee = options['assignee'] || io.ask('Assignee?', default: 'auto')
|
59
|
+
assignee == 'auto' ? '-1' : assignee
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Jira
|
2
|
+
class CLI < Thor
|
3
|
+
|
4
|
+
desc "attachments", "View ticket attachments"
|
5
|
+
def attachments(ticket=Jira::Core.ticket)
|
6
|
+
Command::Attachments.new(ticket).run
|
7
|
+
end
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
module Command
|
12
|
+
class Attachments < Base
|
13
|
+
|
14
|
+
attr_accessor :ticket
|
15
|
+
|
16
|
+
def initialize(ticket)
|
17
|
+
self.ticket = ticket
|
18
|
+
end
|
19
|
+
|
20
|
+
def run
|
21
|
+
return if ticket.empty?
|
22
|
+
return if metadata.empty?
|
23
|
+
return if metadata['fields'].nil?
|
24
|
+
|
25
|
+
attachments=metadata['fields']['attachment']
|
26
|
+
if !attachments.nil? and attachments.count > 0
|
27
|
+
attachments.each do |attachment|
|
28
|
+
name=attachment['filename']
|
29
|
+
url=attachment['content']
|
30
|
+
|
31
|
+
puts "#{Jira::Format.user(name)} #{url}"
|
32
|
+
end
|
33
|
+
else
|
34
|
+
puts "No attachments found for ticket #{ticket}."
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def metadata
|
41
|
+
@metadata ||= api.get("issue/#{ticket}")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Jira
|
2
|
+
class CLI < Thor
|
3
|
+
|
4
|
+
desc "checkout <ticket>", "Checks out a ticket from JIRA in the git branch"
|
5
|
+
method_option :remote, aliases: "-r", type: :string, default: nil, lazy_default: "", banner: "REMOTE"
|
6
|
+
def checkout(ticket)
|
7
|
+
Command::Checkout.new(ticket, options).run
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
module Command
|
13
|
+
class Checkout < Base
|
14
|
+
|
15
|
+
attr_accessor :ticket, :options
|
16
|
+
|
17
|
+
def initialize(ticket, options)
|
18
|
+
self.ticket = ticket
|
19
|
+
self.options = options
|
20
|
+
end
|
21
|
+
|
22
|
+
def run
|
23
|
+
return unless Jira::Core.ticket?(ticket)
|
24
|
+
return if metadata.empty?
|
25
|
+
unless metadata['errorMessages'].nil?
|
26
|
+
on_failure
|
27
|
+
return
|
28
|
+
end
|
29
|
+
unless remote?
|
30
|
+
on_failure
|
31
|
+
return
|
32
|
+
end
|
33
|
+
|
34
|
+
create_branch unless branches.include?(ticket)
|
35
|
+
checkout_branch
|
36
|
+
reset_branch unless branches.include?(ticket)
|
37
|
+
on_success
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def on_success
|
43
|
+
puts "Ticket #{ticket} checked out."
|
44
|
+
end
|
45
|
+
|
46
|
+
def on_failure
|
47
|
+
puts "No ticket checked out."
|
48
|
+
end
|
49
|
+
|
50
|
+
def branches
|
51
|
+
@branches ||= `git branch --list 2> /dev/null`.split(' ')
|
52
|
+
@branches.delete("*")
|
53
|
+
@branches
|
54
|
+
end
|
55
|
+
|
56
|
+
def create_branch
|
57
|
+
`git branch #{ticket} 2> /dev/null`
|
58
|
+
end
|
59
|
+
|
60
|
+
def checkout_branch
|
61
|
+
`git checkout #{ticket} 2> /dev/null`
|
62
|
+
end
|
63
|
+
|
64
|
+
def metadata
|
65
|
+
@metadata ||= api.get("issue/#{ticket}")
|
66
|
+
end
|
67
|
+
|
68
|
+
def remote
|
69
|
+
@remote ||= options['remote'] || io.select('Remote?', remotes)
|
70
|
+
end
|
71
|
+
|
72
|
+
def remotes
|
73
|
+
@remotes ||= `git remote 2> /dev/null`.split(' ')
|
74
|
+
end
|
75
|
+
|
76
|
+
def remote?
|
77
|
+
return true if remotes.include?(remote)
|
78
|
+
false
|
79
|
+
end
|
80
|
+
|
81
|
+
def reset_branch
|
82
|
+
`git reset --hard #{remote} 2> /dev/null`
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Jira
|
2
|
+
class Comment < Thor
|
3
|
+
|
4
|
+
desc 'add', 'Add a comment to the input ticket'
|
5
|
+
method_option :text, aliases: "-t", type: :string, default: nil, lazy_default: "", banner: "TEXT"
|
6
|
+
def add(ticket=Jira::Core.ticket)
|
7
|
+
Command::Comment::Add.new(ticket, options).run
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
|
12
|
+
module Command
|
13
|
+
module Comment
|
14
|
+
class Add < Base
|
15
|
+
|
16
|
+
attr_accessor :ticket, :options
|
17
|
+
|
18
|
+
def initialize(ticket, options)
|
19
|
+
self.ticket = ticket
|
20
|
+
self.options = options
|
21
|
+
end
|
22
|
+
|
23
|
+
def run
|
24
|
+
return if text.empty?
|
25
|
+
api.post "issue/#{ticket}/comment",
|
26
|
+
params: params,
|
27
|
+
success: on_success,
|
28
|
+
failure: on_failure
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def params
|
34
|
+
{ body: text }
|
35
|
+
end
|
36
|
+
|
37
|
+
def text
|
38
|
+
body(options['text'])
|
39
|
+
end
|
40
|
+
|
41
|
+
def on_success
|
42
|
+
->{ puts "Successfully posted your comment." }
|
43
|
+
end
|
44
|
+
|
45
|
+
def on_failure
|
46
|
+
->{ puts "No comment posted." }
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Jira
|
2
|
+
class Comment < Thor
|
3
|
+
|
4
|
+
desc 'delete', 'Delete a comment to the input ticket'
|
5
|
+
def delete(ticket=Jira::Core.ticket)
|
6
|
+
Command::Comment::Delete.new(ticket).run
|
7
|
+
end
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
module Command
|
12
|
+
module Comment
|
13
|
+
class Delete < Base
|
14
|
+
|
15
|
+
attr_accessor :ticket
|
16
|
+
|
17
|
+
def initialize(ticket)
|
18
|
+
self.ticket = ticket
|
19
|
+
end
|
20
|
+
|
21
|
+
def run
|
22
|
+
return unless comments?
|
23
|
+
api.delete endpoint,
|
24
|
+
success: on_success,
|
25
|
+
failure: on_failure
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def comments?
|
31
|
+
if json.empty?
|
32
|
+
puts "Ticket #{ticket} has no comments."
|
33
|
+
return false
|
34
|
+
end
|
35
|
+
true
|
36
|
+
end
|
37
|
+
|
38
|
+
def endpoint
|
39
|
+
"issue/#{ticket}/comment/#{to_delete['id']}"
|
40
|
+
end
|
41
|
+
|
42
|
+
def on_success
|
43
|
+
->{ puts "Successfully deleted comment from #{to_delete['updateAuthor']['displayName']}" }
|
44
|
+
end
|
45
|
+
|
46
|
+
def on_failure
|
47
|
+
->{ puts "No comment deleted." }
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_delete
|
51
|
+
@to_delete ||= comments[
|
52
|
+
io.select("Select a comment to delete:", comments.keys)
|
53
|
+
]
|
54
|
+
end
|
55
|
+
|
56
|
+
def comments
|
57
|
+
@comments ||= (
|
58
|
+
comments = {}
|
59
|
+
json.each do |comment|
|
60
|
+
comments[description_for(comment)] = comment
|
61
|
+
end
|
62
|
+
comments
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
def description_for(comment)
|
67
|
+
author = comment['updateAuthor']['displayName']
|
68
|
+
updated_at = Jira::Format.time(Time.parse(comment['updated']))
|
69
|
+
body = comment['body'].split.join(" ")
|
70
|
+
truncate("#{author} @ #{updated_at}: #{body}", 160)
|
71
|
+
end
|
72
|
+
|
73
|
+
def json
|
74
|
+
@json ||= api.get("issue/#{ticket}/comment")['comments'] || {}
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Jira
|
2
|
+
class Comment < Thor
|
3
|
+
|
4
|
+
desc 'list', 'Lists the comments of the input ticket'
|
5
|
+
def list(ticket=Jira::Core.ticket)
|
6
|
+
Command::Comment::List.new(ticket).run
|
7
|
+
end
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
module Command
|
12
|
+
module Comment
|
13
|
+
class List < Base
|
14
|
+
|
15
|
+
attr_accessor :ticket
|
16
|
+
|
17
|
+
def initialize(ticket)
|
18
|
+
self.ticket = ticket
|
19
|
+
end
|
20
|
+
|
21
|
+
def run
|
22
|
+
return if comments.nil?
|
23
|
+
|
24
|
+
if comments.empty?
|
25
|
+
puts "Ticket #{ticket} has no comments."
|
26
|
+
return
|
27
|
+
end
|
28
|
+
render_table(header, rows)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
attr_accessor :comment
|
34
|
+
|
35
|
+
def header
|
36
|
+
[ 'Author', 'Updated At', 'Body' ]
|
37
|
+
end
|
38
|
+
|
39
|
+
def rows
|
40
|
+
rows = []
|
41
|
+
comments.each do |comment|
|
42
|
+
self.comment = comment
|
43
|
+
rows << row
|
44
|
+
end
|
45
|
+
rows
|
46
|
+
end
|
47
|
+
|
48
|
+
def row
|
49
|
+
[ author, updated_at, body ]
|
50
|
+
end
|
51
|
+
|
52
|
+
def author
|
53
|
+
comment['updateAuthor']['displayName']
|
54
|
+
end
|
55
|
+
|
56
|
+
def updated_at
|
57
|
+
Jira::Format.time(Time.parse(comment['updated']))
|
58
|
+
end
|
59
|
+
|
60
|
+
def body
|
61
|
+
body = comment['body'].gsub("\r\n|\r|\n", ";")
|
62
|
+
truncate(body, 45)
|
63
|
+
end
|
64
|
+
|
65
|
+
def comments
|
66
|
+
@comments ||= api.get("issue/#{ticket}/comment")['comments']
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|