jira-cli 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +18 -28
- data/bin/jira +3 -1
- data/lib/jira.rb +12 -6
- data/lib/jira/api.rb +10 -5
- data/lib/jira/auth_api.rb +11 -0
- data/lib/jira/command.rb +21 -1
- data/lib/jira/commands/all.rb +2 -2
- data/lib/jira/commands/assign.rb +2 -2
- data/lib/jira/commands/comment.rb +8 -98
- data/lib/jira/commands/comment/add.rb +46 -0
- data/lib/jira/commands/comment/delete.rb +80 -0
- data/lib/jira/commands/comment/list.rb +69 -0
- data/lib/jira/commands/comment/update.rb +88 -0
- data/lib/jira/commands/delete.rb +0 -2
- data/lib/jira/commands/describe.rb +0 -2
- data/lib/jira/commands/install.rb +39 -17
- data/lib/jira/commands/link.rb +4 -3
- data/lib/jira/commands/log.rb +7 -72
- data/lib/jira/commands/log/add.rb +50 -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/new.rb +4 -6
- data/lib/jira/commands/rename.rb +0 -2
- data/lib/jira/commands/sprint.rb +3 -3
- data/lib/jira/commands/tickets.rb +0 -2
- data/lib/jira/commands/transition.rb +0 -2
- data/lib/jira/commands/vote.rb +5 -107
- 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/watch.rb +5 -108
- 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/constants.rb +1 -1
- data/lib/jira/core.rb +9 -1
- data/lib/jira/sprint_api.rb +2 -18
- metadata +17 -5
- data/lib/jira/commands/commit.rb +0 -14
- data/lib/jira/legacy_api.rb +0 -102
- data/lib/jira/mixins.rb +0 -56
@@ -0,0 +1,69 @@
|
|
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
|
+
if comments.empty?
|
23
|
+
puts "Ticket #{ticket} has no comments."
|
24
|
+
return
|
25
|
+
end
|
26
|
+
render_table(header, rows)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
attr_accessor :comment
|
32
|
+
|
33
|
+
def header
|
34
|
+
[ 'Author', 'Updated At', 'Body' ]
|
35
|
+
end
|
36
|
+
|
37
|
+
def rows
|
38
|
+
rows = []
|
39
|
+
comments.each do |comment|
|
40
|
+
self.comment = comment
|
41
|
+
rows << row
|
42
|
+
end
|
43
|
+
rows
|
44
|
+
end
|
45
|
+
|
46
|
+
def row
|
47
|
+
[ author, updated_at, body ]
|
48
|
+
end
|
49
|
+
|
50
|
+
def author
|
51
|
+
comment['updateAuthor']['displayName']
|
52
|
+
end
|
53
|
+
|
54
|
+
def updated_at
|
55
|
+
Jira::Format.time(Time.parse(comment['updated']))
|
56
|
+
end
|
57
|
+
|
58
|
+
def body
|
59
|
+
comment['body']
|
60
|
+
end
|
61
|
+
|
62
|
+
def comments
|
63
|
+
@comments ||= api.get("issue/#{ticket}/comment")['comments']
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Jira
|
2
|
+
class Comment < Thor
|
3
|
+
|
4
|
+
desc 'update', 'Update a comment to the input ticket'
|
5
|
+
def update(ticket=Jira::Core.ticket)
|
6
|
+
Command::Comment::Update.new(ticket).run
|
7
|
+
end
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
module Command
|
12
|
+
module Comment
|
13
|
+
class Update < 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.patch endpoint,
|
24
|
+
params: params,
|
25
|
+
success: on_success,
|
26
|
+
failure: on_failure
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def params
|
32
|
+
{ body: body }
|
33
|
+
end
|
34
|
+
|
35
|
+
def comments?
|
36
|
+
if json.empty?
|
37
|
+
puts "Ticket #{ticket} has no comments."
|
38
|
+
return false
|
39
|
+
end
|
40
|
+
true
|
41
|
+
end
|
42
|
+
|
43
|
+
def endpoint
|
44
|
+
"issue/#{ticket}/comment/#{to_update['id']}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def on_success
|
48
|
+
->{
|
49
|
+
puts "Successfully updated comment originally from"\
|
50
|
+
" #{to_update['updateAuthor']['displayName']}."
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
def on_failure
|
55
|
+
->{ puts "No comment updated." }
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_update
|
59
|
+
@to_update ||= comments[
|
60
|
+
io.select("Select a comment to update:", comments.keys)
|
61
|
+
]
|
62
|
+
end
|
63
|
+
|
64
|
+
def comments
|
65
|
+
@comments ||= (
|
66
|
+
comments = {}
|
67
|
+
json.each do |comment|
|
68
|
+
comments[description_for(comment)] = comment
|
69
|
+
end
|
70
|
+
comments
|
71
|
+
)
|
72
|
+
end
|
73
|
+
|
74
|
+
def description_for(comment)
|
75
|
+
author = comment['updateAuthor']['displayName']
|
76
|
+
updated_at = Jira::Format.time(Time.parse(comment['updated']))
|
77
|
+
body = comment['body'].gsub("\r\n|\r|\n", ";")
|
78
|
+
truncate("#{author} @ #{updated_at}: #{body}", 160)
|
79
|
+
end
|
80
|
+
|
81
|
+
def json
|
82
|
+
@json ||= api.get("issue/#{ticket}/comment")['comments']
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
data/lib/jira/commands/delete.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require_relative '../command'
|
2
|
-
|
3
1
|
module Jira
|
4
2
|
class CLI < Thor
|
5
3
|
|
@@ -15,35 +13,53 @@ module Jira
|
|
15
13
|
|
16
14
|
def run
|
17
15
|
io.say('Please enter your JIRA information.')
|
18
|
-
inifile[:global] =
|
16
|
+
inifile[:global] = base_params
|
17
|
+
inifile.write # Do this now because cookie authentication uses api calls
|
18
|
+
|
19
|
+
inifile.delete_section("cookie") if inifile.has_section?("cookie")
|
20
|
+
case authentication
|
21
|
+
when "basic"
|
22
|
+
inifile[:global][:password] = password
|
23
|
+
when "token"
|
24
|
+
inifile[:global][:token] = token
|
25
|
+
when "cookie"
|
26
|
+
response = cookie
|
27
|
+
inifile[:cookie] = {}
|
28
|
+
inifile[:cookie][:name] = response['name']
|
29
|
+
inifile[:cookie][:value] = response['value']
|
30
|
+
end
|
19
31
|
inifile.write
|
20
32
|
end
|
21
33
|
|
22
34
|
private
|
23
35
|
|
24
|
-
def
|
25
|
-
|
36
|
+
def base_params
|
37
|
+
{
|
26
38
|
url: url,
|
27
39
|
username: username,
|
28
40
|
}
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
41
|
+
end
|
42
|
+
|
43
|
+
def session_params
|
44
|
+
{
|
45
|
+
username: username,
|
46
|
+
password: password
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
def authentication
|
51
|
+
@authentication ||= io.select(
|
52
|
+
"Select an authentication type:",
|
53
|
+
["basic", "cookie", "token"]
|
54
|
+
)
|
39
55
|
end
|
40
56
|
|
41
57
|
def url
|
42
|
-
io.ask("JIRA URL:")
|
58
|
+
@url ||= io.ask("JIRA URL:")
|
43
59
|
end
|
44
60
|
|
45
61
|
def username
|
46
|
-
io.ask("JIRA username:")
|
62
|
+
@username ||= io.ask("JIRA username:")
|
47
63
|
end
|
48
64
|
|
49
65
|
def password
|
@@ -54,6 +70,12 @@ module Jira
|
|
54
70
|
io.ask("JIRA token:")
|
55
71
|
end
|
56
72
|
|
73
|
+
def cookie
|
74
|
+
response = auth_api.post('session', params: session_params)
|
75
|
+
return {} unless response['errorMessages'].nil?
|
76
|
+
response['session']
|
77
|
+
end
|
78
|
+
|
57
79
|
def inifile
|
58
80
|
@inifile ||= IniFile.new(
|
59
81
|
comment: '#',
|
data/lib/jira/commands/link.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require_relative '../command'
|
2
|
-
|
3
1
|
module Jira
|
4
2
|
class CLI < Thor
|
5
3
|
|
@@ -69,7 +67,10 @@ module Jira
|
|
69
67
|
end
|
70
68
|
|
71
69
|
def on_success
|
72
|
-
->{
|
70
|
+
->{
|
71
|
+
puts "Successfully linked ticket #{ticket} to"\
|
72
|
+
" ticket #{outward_ticket}."
|
73
|
+
}
|
73
74
|
end
|
74
75
|
|
75
76
|
def on_failure
|
data/lib/jira/commands/log.rb
CHANGED
@@ -1,78 +1,13 @@
|
|
1
|
+
require 'jira/commands/log/add'
|
2
|
+
require 'jira/commands/log/delete'
|
3
|
+
require 'jira/commands/log/list'
|
4
|
+
require 'jira/commands/log/update'
|
5
|
+
|
1
6
|
module Jira
|
2
7
|
class CLI < Thor
|
3
8
|
|
4
|
-
desc
|
5
|
-
|
6
|
-
time_spent = self.io.ask("Time spent on ticket #{ticket}:")
|
7
|
-
self.api.post("issue/#{ticket}/worklog", { timeSpent: time_spent }) do |json|
|
8
|
-
puts "Successfully logged #{time_spent} on ticket #{ticket}."
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
desc "logd", "Deletes work against the input ticket"
|
13
|
-
def logd(ticket=Jira::Core.ticket)
|
14
|
-
logs(ticket) if self.io.yes?("List worklogs for ticket #{ticket}?")
|
15
|
-
|
16
|
-
index = self.get_type_of_index("worklog", "delete")
|
17
|
-
puts "No worklog deleted." and return if index < 0
|
18
|
-
|
19
|
-
self.api.get("issue/#{ticket}/worklog") do |json|
|
20
|
-
worklogs = json['worklogs']
|
21
|
-
if index < worklogs.count
|
22
|
-
id = worklogs[index]['id']
|
23
|
-
time_spent = worklogs[index]['timeSpent']
|
24
|
-
self.api.delete("issue/#{ticket}/worklog/#{id}") do |json|
|
25
|
-
puts "Successfully deleted #{time_spent} on ticket #{ticket}"
|
26
|
-
return
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
puts "No worklog deleted."
|
31
|
-
end
|
32
|
-
|
33
|
-
desc "logs", "Lists work against the input ticket"
|
34
|
-
def logs(ticket=Jira::Core.ticket)
|
35
|
-
self.api.get("issue/#{ticket}/worklog") do |json|
|
36
|
-
worklogs = json['worklogs']
|
37
|
-
if worklogs.count > 0
|
38
|
-
worklogs.each do |worklog|
|
39
|
-
author = worklog['author']['displayName']
|
40
|
-
time = Time.parse(worklog['updated'])
|
41
|
-
time_spent = worklog['timeSpent']
|
42
|
-
|
43
|
-
printf "[%2d]", worklogs.index(worklog)
|
44
|
-
puts " #{Jira::Format.user(author)} @ "\
|
45
|
-
"#{Jira::Format.time(time)}:\n"\
|
46
|
-
"#{Jira::Format.comment(time_spent)}"
|
47
|
-
end
|
48
|
-
else
|
49
|
-
puts "There are no worklogs on ticket #{ticket}"
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
desc "logu", "Updates work against the input ticket"
|
55
|
-
def logu(ticket=Jira::Core.ticket)
|
56
|
-
logs(ticket) if self.io.yes?("List worklogs for ticket #{ticket}?")
|
57
|
-
|
58
|
-
index = self.get_type_of_index("worklog", "update")
|
59
|
-
puts "No worklog updated." and return if index < 0
|
60
|
-
|
61
|
-
time_spent = self.io.ask("Time spent on #{ticket}:").strip
|
62
|
-
puts "No worklog updated." and return if time_spent.empty?
|
63
|
-
|
64
|
-
self.api.get("issue/#{ticket}/worklog") do |json|
|
65
|
-
worklogs = json['worklogs']
|
66
|
-
if index < worklogs.count
|
67
|
-
id = worklogs[index]['id']
|
68
|
-
self.api.put("issue/#{ticket}/worklog/#{id}", { timeSpent: time_spent }) do |json|
|
69
|
-
puts "Successfully updated #{time_spent} on ticket #{ticket}."
|
70
|
-
return
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
puts "No worklog updated."
|
75
|
-
end
|
9
|
+
desc 'log <command>', 'Commands for logging operations in JIRA'
|
10
|
+
subcommand 'log', Log
|
76
11
|
|
77
12
|
end
|
78
13
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Jira
|
2
|
+
class Log < Thor
|
3
|
+
|
4
|
+
desc 'add', 'Logs work against the input ticket'
|
5
|
+
def add(ticket=Jira::Core.ticket)
|
6
|
+
Command::Log::Add.new(ticket).run
|
7
|
+
end
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
module Command
|
12
|
+
module Log
|
13
|
+
class Add < Base
|
14
|
+
|
15
|
+
attr_accessor :ticket
|
16
|
+
|
17
|
+
def initialize(ticket)
|
18
|
+
self.ticket = ticket
|
19
|
+
end
|
20
|
+
|
21
|
+
def run
|
22
|
+
return if time_spent.empty?
|
23
|
+
api.post "issue/#{ticket}/worklog",
|
24
|
+
params: params,
|
25
|
+
success: on_success,
|
26
|
+
failure: on_failure
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def params
|
32
|
+
{ timeSpent: time_spent }
|
33
|
+
end
|
34
|
+
|
35
|
+
def time_spent
|
36
|
+
@time_spent ||= io.ask("Time spent on ticket #{ticket}:")
|
37
|
+
end
|
38
|
+
|
39
|
+
def on_success
|
40
|
+
->{ puts "Successfully logged #{time_spent} on ticket #{ticket}." }
|
41
|
+
end
|
42
|
+
|
43
|
+
def on_failure
|
44
|
+
->{ puts "No work was logged on ticket #{ticket}." }
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Jira
|
2
|
+
class Log < Thor
|
3
|
+
|
4
|
+
desc 'delete', 'Deletes logged work against the input ticket'
|
5
|
+
def delete(ticket=Jira::Core.ticket)
|
6
|
+
Command::Log::Delete.new(ticket).run
|
7
|
+
end
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
module Command
|
12
|
+
module Log
|
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 logs?
|
23
|
+
api.delete endpoint,
|
24
|
+
success: on_success,
|
25
|
+
failure: on_failure
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def logs?
|
31
|
+
if json.empty?
|
32
|
+
puts "Ticket #{ticket} has no work logged."
|
33
|
+
return false
|
34
|
+
end
|
35
|
+
true
|
36
|
+
end
|
37
|
+
|
38
|
+
def endpoint
|
39
|
+
"issue/#{ticket}/worklog/#{to_delete['id']}"
|
40
|
+
end
|
41
|
+
|
42
|
+
def on_success
|
43
|
+
->{ puts "Successfully deleted #{to_delete['timeSpent']}." }
|
44
|
+
end
|
45
|
+
|
46
|
+
def on_failure
|
47
|
+
->{ puts "No logged work deleted." }
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_delete
|
51
|
+
@to_delete ||= logs[
|
52
|
+
io.select("Select a worklog to delete:", logs.keys)
|
53
|
+
]
|
54
|
+
end
|
55
|
+
|
56
|
+
def logs
|
57
|
+
@logs ||= (
|
58
|
+
logs = {}
|
59
|
+
json.each do |log|
|
60
|
+
logs[description_for(log)] = log
|
61
|
+
end
|
62
|
+
logs
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
def description_for(log)
|
67
|
+
author = log['updateAuthor']['displayName']
|
68
|
+
updated_at = Jira::Format.time(Time.parse(log['updated']))
|
69
|
+
time_spent = log['timeSpent']
|
70
|
+
"#{author} @ #{updated_at}: #{time_spent}"
|
71
|
+
end
|
72
|
+
|
73
|
+
def json
|
74
|
+
@json ||= api.get("issue/#{ticket}/worklog")['worklogs']
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|