jira-cli 0.1.6 → 0.2.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 +14 -3
- data/lib/jira/api.rb +3 -3
- data/lib/jira/commands/assign.rb +17 -0
- data/lib/jira/commands/browse.rb +4 -4
- data/lib/jira/commands/clipboard.rb +4 -4
- data/lib/jira/commands/comment.rb +77 -11
- data/lib/jira/commands/delete.rb +46 -0
- data/lib/jira/commands/describe.rb +7 -2
- data/lib/jira/commands/install.rb +13 -10
- data/lib/jira/commands/log.rb +67 -2
- data/lib/jira/commands/new.rb +14 -9
- data/lib/jira/commands/tickets.rb +22 -0
- data/lib/jira/commands/vote.rb +41 -0
- data/lib/jira/commands/watch.rb +41 -0
- data/lib/jira/constants.rb +1 -1
- data/lib/jira/core.rb +8 -27
- data/lib/jira/io.rb +2 -2
- data/lib/jira/mixins.rb +14 -0
- metadata +28 -4
- data/lib/jira/commands/sprint.rb +0 -37
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7f1bab7c3cccb17ac4d522d2e93b12c6a6a14b5b
|
4
|
+
data.tar.gz: edcffe6e4b98d10c1a2932754a5f71c6c906cb2d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ce61e0e3b2cdf2956d0fccfa2fc9234bc76054d391e72d463dd50aa40e55014179001bb6652b0d3f5166d880459245dee29c974be6585f20efa67948ac3b587e
|
7
|
+
data.tar.gz: 87a09fbb58365dd5400f59ac41ee08796439283c89571804be096ca35300005b8b5a62b15567841a915c0225bc879c91cb6fd197a685d5173e8cc4762df39b43
|
data/README.md
CHANGED
@@ -11,20 +11,31 @@ Ruby gem CLI tool used to manage JIRA workflows leveraging git
|
|
11
11
|
### Available Commands
|
12
12
|
|
13
13
|
jira all # Describes all local branches that match JIRA ticketing syntax
|
14
|
+
jira assign # Assign a ticket to a user
|
14
15
|
jira attachments # View ticket attachments
|
15
|
-
jira browse # Opens the current input ticket in your browser
|
16
16
|
jira comment # Add a comment to the input ticket
|
17
|
+
jira commentd # Delete a comment to the input ticket
|
17
18
|
jira comments # Lists the comments of the input ticket
|
19
|
+
jira commentu # Update a comment to the input ticket
|
18
20
|
jira commit # Commits uncommitted work with the ticket name and summary.
|
19
|
-
jira
|
21
|
+
jira delete # Deletes a ticket in JIRA and the git branch
|
20
22
|
jira describe # Describes the input ticket
|
21
23
|
jira install # Guides the user through JIRA installation
|
22
24
|
jira log # Logs work against the input ticket
|
25
|
+
jira logd # Deletes work against the input ticket
|
26
|
+
jira logs # Lists work against the input ticket
|
27
|
+
jira logu # Updates work against the input ticket
|
23
28
|
jira new # Creates a new ticket in JIRA and checks out the git branch
|
24
29
|
jira rename # Updates the summary of the input ticket
|
25
|
-
jira
|
30
|
+
jira tickets # List the tickets of the input username
|
26
31
|
jira transition # Transitions the input ticket to the next state
|
32
|
+
jira unvote # Unvote against the input ticket
|
33
|
+
jira unwatch # Unwatch against the input ticket
|
27
34
|
jira version # Displays the version
|
35
|
+
jira vote # Vote against the input ticket
|
36
|
+
jira votes # List the votes of the input ticket
|
37
|
+
jira watch # Watch against the input ticket
|
38
|
+
jira watchers # List the watchers of the input ticket
|
28
39
|
jira help [COMMAND] # Describe available commands or one specific command
|
29
40
|
|
30
41
|
### Gem Installation
|
data/lib/jira/api.rb
CHANGED
@@ -27,13 +27,13 @@ module Jira
|
|
27
27
|
protected
|
28
28
|
|
29
29
|
#
|
30
|
-
# Defines the API GET, POST, PUT interaction methods
|
30
|
+
# Defines the API DELETE, GET, POST, PUT interaction methods
|
31
31
|
#
|
32
32
|
def define_actions
|
33
33
|
#
|
34
34
|
# def method(path, params={})
|
35
35
|
#
|
36
|
-
# Issue an API GET, POST, or PUT request and return parse JSON
|
36
|
+
# Issue an API DELETE, GET, POST, or PUT request and return parse JSON
|
37
37
|
#
|
38
38
|
# @param path [String] API path
|
39
39
|
# @param params [Hash] params to send
|
@@ -42,7 +42,7 @@ module Jira
|
|
42
42
|
#
|
43
43
|
# @return [JSON] parased API response
|
44
44
|
#
|
45
|
-
[:get, :post, :put].each do |method|
|
45
|
+
[:delete, :get, :post, :put].each do |method|
|
46
46
|
self.class.send(:define_method, method) do |path, params=nil, verbose=true, &block|
|
47
47
|
params = params.to_json if !params.nil?
|
48
48
|
response = @client.send(
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Jira
|
2
|
+
class CLI < Thor
|
3
|
+
|
4
|
+
desc "assign", "Assign a ticket to a user"
|
5
|
+
def assign(ticket=Jira::Core.ticket)
|
6
|
+
# determine assignee
|
7
|
+
assignee = self.io.ask("Assignee (default auto)").strip
|
8
|
+
assignee = "-1" if assignee.empty? # automatic assignee is used
|
9
|
+
|
10
|
+
self.api.put("issue/#{ticket}/assignee", { name: assignee }) do |json|
|
11
|
+
return
|
12
|
+
end
|
13
|
+
puts "Ticket #{ticket} not assigned."
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
data/lib/jira/commands/browse.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
module Jira
|
2
2
|
class CLI < Thor
|
3
3
|
|
4
|
-
desc "browse", "Opens the current input ticket in your browser"
|
5
|
-
def browse(ticket=Jira::Core.ticket)
|
6
|
-
|
7
|
-
end
|
4
|
+
#desc "browse", "Opens the current input ticket in your browser"
|
5
|
+
#def browse(ticket=Jira::Core.ticket)
|
6
|
+
# system("open #{Jira::Core.url}/browse/#{ticket}")
|
7
|
+
#end
|
8
8
|
|
9
9
|
end
|
10
10
|
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
module Jira
|
2
2
|
class CLI < Thor
|
3
3
|
|
4
|
-
desc "clipboard", "Copies the url of the JIRA ticket into the clipboard"
|
5
|
-
def clipboard(ticket=Jira::Core.ticket)
|
6
|
-
|
7
|
-
end
|
4
|
+
#desc "clipboard", "Copies the url of the JIRA ticket into the clipboard"
|
5
|
+
#def clipboard(ticket=Jira::Core.ticket)
|
6
|
+
# `echo #{Jira::Core.url}/browse/#{ticket} | pbcopy`
|
7
|
+
#end
|
8
8
|
|
9
9
|
end
|
10
10
|
end
|
@@ -3,15 +3,19 @@ module Jira
|
|
3
3
|
|
4
4
|
desc "comment", "Add a comment to the input ticket"
|
5
5
|
def comment(ticket=Jira::Core.ticket)
|
6
|
-
comment = self.
|
7
|
-
if comment.
|
8
|
-
|
9
|
-
|
10
|
-
comment.
|
11
|
-
|
12
|
-
puts "Successfully posted your comment."
|
13
|
-
end
|
6
|
+
comment = self.get_comment_body(ticket)
|
7
|
+
puts "No comment posted." and return if comment.empty?
|
8
|
+
|
9
|
+
self.api.post("issue/#{ticket}/comment", { body: comment }) do |json|
|
10
|
+
puts "Successfully posted your comment."
|
11
|
+
return
|
14
12
|
end
|
13
|
+
puts "No comment posted."
|
14
|
+
end
|
15
|
+
|
16
|
+
desc "commentd", "Delete a comment to the input ticket"
|
17
|
+
def commentd(ticket=Jira::Core.ticket)
|
18
|
+
self.comment_delete(ticket)
|
15
19
|
end
|
16
20
|
|
17
21
|
desc "comments", "Lists the comments of the input ticket"
|
@@ -21,18 +25,80 @@ module Jira
|
|
21
25
|
if comments.count > 0
|
22
26
|
comments.each do |comment|
|
23
27
|
author = comment['author']['displayName']
|
24
|
-
time = Time.parse(comment['
|
28
|
+
time = Time.parse(comment['updated'])
|
25
29
|
body = comment['body']
|
26
30
|
|
27
|
-
|
31
|
+
printf "[%2d]", comments.index(comment)
|
32
|
+
puts " #{Jira::Format.user(author)} @ "\
|
28
33
|
"#{Jira::Format.time(time)}:\n"\
|
29
34
|
"#{Jira::Format.comment(body)}"
|
30
35
|
end
|
31
36
|
else
|
32
|
-
puts "There are no comments on
|
37
|
+
puts "There are no comments on ticket #{ticket}."
|
33
38
|
end
|
34
39
|
end
|
35
40
|
end
|
36
41
|
|
42
|
+
desc "commentu", "Update a comment to the input ticket"
|
43
|
+
def commentu(ticket=Jira::Core.ticket)
|
44
|
+
self.comment_update(ticket)
|
45
|
+
end
|
46
|
+
|
47
|
+
protected
|
48
|
+
|
49
|
+
def comment_delete(ticket)
|
50
|
+
comments(ticket) if self.io.agree("List comments for ticket #{ticket}")
|
51
|
+
|
52
|
+
index = self.get_type_of_index("comment", "delete")
|
53
|
+
puts "No comment deleted." and return if index < 0
|
54
|
+
|
55
|
+
self.api.get("issue/#{ticket}") do |json|
|
56
|
+
comments = json['fields']['comment']['comments']
|
57
|
+
if index < comments.count
|
58
|
+
id = comments[index]['id']
|
59
|
+
self.api.delete("issue/#{ticket}/comment/#{id}") do |json|
|
60
|
+
puts "Successfully deleted your comment."
|
61
|
+
return
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
puts "No comment deleted."
|
66
|
+
end
|
67
|
+
|
68
|
+
def comment_update(ticket)
|
69
|
+
comments(ticket) if self.io.agree("List comments for ticket #{ticket}")
|
70
|
+
|
71
|
+
index = self.get_type_of_index("comment", "update")
|
72
|
+
puts "No comment updated." and return if index < 0
|
73
|
+
|
74
|
+
comment = self.get_comment_body(ticket)
|
75
|
+
puts "No comment updated." and return if comment.empty?
|
76
|
+
|
77
|
+
self.api.get("issue/#{ticket}") do |json|
|
78
|
+
comments = json['fields']['comment']['comments']
|
79
|
+
id = comments[index]['id']
|
80
|
+
self.api.put("issue/#{ticket}/comment/#{id}", { body: comment }) do |json|
|
81
|
+
puts "Successfully updated your comment."
|
82
|
+
return
|
83
|
+
end
|
84
|
+
end
|
85
|
+
puts "No comment updated."
|
86
|
+
end
|
87
|
+
|
88
|
+
#
|
89
|
+
# Prompts the user for a comment body, strips it, then
|
90
|
+
# returns a substituted version of it
|
91
|
+
#
|
92
|
+
# @return comment [String] asked comment body
|
93
|
+
#
|
94
|
+
def get_comment_body(ticket)
|
95
|
+
comment = self.io.ask("Leave a comment for ticket #{ticket}").strip
|
96
|
+
temp = comment.gsub(/\@[a-zA-Z]+/,'[~\0]')
|
97
|
+
temp = comment if temp.nil?
|
98
|
+
temp = temp.gsub('[~@','[~')
|
99
|
+
comment = temp if !temp.nil?
|
100
|
+
comment
|
101
|
+
end
|
102
|
+
|
37
103
|
end
|
38
104
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Jira
|
2
|
+
class CLI < Thor
|
3
|
+
|
4
|
+
desc "delete", "Deletes a ticket in JIRA and the git branch"
|
5
|
+
option :force, type: :boolean, default: false
|
6
|
+
def delete(ticket=Jira::Core.ticket)
|
7
|
+
force = options[:force]
|
8
|
+
self.api.get("issue/#{ticket}") do |json|
|
9
|
+
issue_type = json['fields']['issuetype']
|
10
|
+
if !issue_type['subtask']
|
11
|
+
if !json['fields']['subtasks'].empty?
|
12
|
+
force = self.io.agree("Delete all sub-tasks for ticket #{ticket}")
|
13
|
+
if !force
|
14
|
+
puts "No ticket deleted."
|
15
|
+
return
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
self.api.delete("issue/#{ticket}?deleteSubtasks=#{force}") do |json|
|
21
|
+
branches = `git branch --list 2> /dev/null`.split(' ')
|
22
|
+
branches.delete("*")
|
23
|
+
branches.delete("#{ticket}")
|
24
|
+
create_branch = self.io.agree("Create branch") if branches.count > 1
|
25
|
+
if branches.count == 1 or create_branch
|
26
|
+
puts "Creating a new branch."
|
27
|
+
new_branch = self.io.ask("Branch").strip
|
28
|
+
new_branch.delete!(" ")
|
29
|
+
if new_branch.empty?
|
30
|
+
puts "No ticket deleted."
|
31
|
+
return
|
32
|
+
end
|
33
|
+
`git branch #{new_branch} 2> /dev/null`
|
34
|
+
branches << new_branch
|
35
|
+
end
|
36
|
+
chosen_branch = self.io.choose("Select a branch", branches)
|
37
|
+
`git checkout #{chosen_branch} 2> /dev/null`
|
38
|
+
`git branch -D #{ticket} 2> /dev/null`
|
39
|
+
return
|
40
|
+
end
|
41
|
+
end
|
42
|
+
puts "No ticket deleted."
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -63,11 +63,16 @@ module Jira
|
|
63
63
|
self.api.get("issue/#{ticket}", nil, verbose) do |json|
|
64
64
|
summary = json['fields']['summary']
|
65
65
|
status = json['fields']['status']['name']
|
66
|
-
assignee =
|
66
|
+
assignee = "nil"
|
67
|
+
if json['fields'].has_key?("assignee")
|
68
|
+
if !json['fields']['assignee'].nil?
|
69
|
+
assignee = json['fields']['assignee']['name']
|
70
|
+
end
|
71
|
+
end
|
67
72
|
description = describe ? "\n" + json['fields']['description'].to_s : ""
|
68
73
|
|
69
74
|
return Jira::Format.ticket(ticket) +
|
70
|
-
(star ? Jira::Format.star : " ") + " " +
|
75
|
+
(star ? Jira::Format.star : " ") + " \t" +
|
71
76
|
("(" + Jira::Format.user(assignee) + ")").ljust(20) +
|
72
77
|
Jira::Format.status(status).ljust(26) +
|
73
78
|
Jira::Format.summary(summary) +
|
@@ -8,16 +8,19 @@ module Jira
|
|
8
8
|
desc "install", "Guides the user through JIRA installation"
|
9
9
|
def install
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
11
|
+
inifile = IniFile.new(:comment => '#', :encoding => 'UTF-8', :filename => Jira::Core.cli_path)
|
12
|
+
|
13
|
+
url = self.io.ask("Enter your JIRA URL")
|
14
|
+
|
15
|
+
username = self.io.ask("Enter your JIRA username")
|
16
|
+
password = self.io.ask("Enter your JIRA password", password: true)
|
17
|
+
|
18
|
+
inifile[:global] = {
|
19
|
+
url: url,
|
20
|
+
username: username,
|
21
|
+
password: password
|
22
|
+
}
|
23
|
+
inifile.write
|
21
24
|
|
22
25
|
Jira::Core.send(:discard_memoized)
|
23
26
|
end
|
data/lib/jira/commands/log.rb
CHANGED
@@ -3,11 +3,76 @@ module Jira
|
|
3
3
|
|
4
4
|
desc "log", "Logs work against the input ticket"
|
5
5
|
def log(ticket=Jira::Core.ticket)
|
6
|
-
time_spent = self.io.ask("Time spent on #{ticket}")
|
6
|
+
time_spent = self.io.ask("Time spent on ticket #{ticket}")
|
7
7
|
self.api.post("issue/#{ticket}/worklog", { timeSpent: time_spent }) do |json|
|
8
|
-
puts "Successfully logged #{time_spent} on #{ticket}."
|
8
|
+
puts "Successfully logged #{time_spent} on ticket #{ticket}."
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
|
+
desc "logd", "Deletes work against the input ticket"
|
13
|
+
def logd(ticket=Jira::Core.ticket)
|
14
|
+
logs(ticket) if self.io.agree("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.agree("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
|
76
|
+
|
12
77
|
end
|
13
78
|
end
|
data/lib/jira/commands/new.rb
CHANGED
@@ -2,7 +2,7 @@ module Jira
|
|
2
2
|
class CLI < Thor
|
3
3
|
|
4
4
|
desc "new", "Creates a new ticket in JIRA and checks out the git branch"
|
5
|
-
def new
|
5
|
+
def new(ticket = Jira::Core.ticket)
|
6
6
|
self.api.get("issue/createmeta") do |meta|
|
7
7
|
# determine project
|
8
8
|
project = self.select_project(meta)
|
@@ -10,7 +10,12 @@ module Jira
|
|
10
10
|
|
11
11
|
# determine issue type
|
12
12
|
issue_type = self.select_issue_type(project)
|
13
|
-
return if issue_type.
|
13
|
+
return if issue_type.nil?
|
14
|
+
|
15
|
+
# determine parent (ticket)
|
16
|
+
parent = nil
|
17
|
+
parent = ticket if issue_type['subtask']
|
18
|
+
return if !parent.nil? and !Jira::Core.ticket?(parent)
|
14
19
|
|
15
20
|
# determine summary and description
|
16
21
|
summary = self.io.ask("Summary")
|
@@ -20,18 +25,19 @@ module Jira
|
|
20
25
|
params = {
|
21
26
|
fields: {
|
22
27
|
project: { id: project[:id] },
|
23
|
-
issuetype: { id: issue_type },
|
28
|
+
issuetype: { id: issue_type['id'] },
|
24
29
|
summary: summary,
|
25
30
|
description: description
|
26
31
|
}
|
27
32
|
}
|
33
|
+
params[:fields][:parent] = { key: parent } if !parent.nil?
|
28
34
|
|
29
35
|
# post issue to server
|
30
36
|
self.api.post("issue", params) do |json|
|
31
37
|
ticket = json['key']
|
32
|
-
self.
|
33
|
-
|
34
|
-
|
38
|
+
if self.io.agree("Assign")
|
39
|
+
self.assign(ticket)
|
40
|
+
end
|
35
41
|
if self.io.agree("Create branch")
|
36
42
|
`git branch #{ticket} 2> /dev/null`
|
37
43
|
if self.io.agree("Check-out branch")
|
@@ -75,16 +81,15 @@ module Jira
|
|
75
81
|
#
|
76
82
|
# @param project_data [Hash] project metadata
|
77
83
|
#
|
78
|
-
# @return [
|
84
|
+
# @return [Hash] selected issue type
|
79
85
|
#
|
80
86
|
def select_issue_type(project_data)
|
81
87
|
issue_types = {}
|
82
88
|
project_data[:issues].each do |issue_type|
|
83
|
-
issue_types[issue_type['name']] = issue_type
|
89
|
+
issue_types[issue_type['name']] = issue_type
|
84
90
|
end
|
85
91
|
issue_types['Cancel'] = nil
|
86
92
|
choice = self.io.choose("Select an issue type", issue_types.keys)
|
87
|
-
return '' if choice == 'Cancel'
|
88
93
|
return issue_types[choice]
|
89
94
|
end
|
90
95
|
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Jira
|
2
|
+
class CLI < Thor
|
3
|
+
|
4
|
+
desc "tickets", "List the tickets of the input username"
|
5
|
+
def tickets(username=Jira::Core.username)
|
6
|
+
self.api.get("search?jql=assignee=#{username}") do |json|
|
7
|
+
issues = json['issues']
|
8
|
+
if issues.count > 0
|
9
|
+
issues.each do |issue|
|
10
|
+
ticket = issue['key']
|
11
|
+
|
12
|
+
printf "[%2d]", issues.index(issue)
|
13
|
+
puts " #{Jira::Format.ticket(ticket)}"
|
14
|
+
end
|
15
|
+
else
|
16
|
+
puts "There are no tickets for username #{username}."
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Jira
|
2
|
+
class CLI < Thor
|
3
|
+
|
4
|
+
desc "vote", "Vote against the input ticket"
|
5
|
+
def vote(ticket=Jira::Core.ticket)
|
6
|
+
self.api.post("issue/#{ticket}/votes", Jira::Core.username) do |json|
|
7
|
+
puts "Successfully voted against ticket #{ticket}"
|
8
|
+
return
|
9
|
+
end
|
10
|
+
puts "No vote."
|
11
|
+
end
|
12
|
+
|
13
|
+
desc "votes", "List the votes of the input ticket"
|
14
|
+
def votes(ticket=Jira::Core.ticket)
|
15
|
+
self.api.get("issue/#{ticket}/votes") do |json|
|
16
|
+
voters = json['voters']
|
17
|
+
if voters.count > 0
|
18
|
+
voters.each do |voter|
|
19
|
+
displayName = voter['displayName']
|
20
|
+
|
21
|
+
printf "[%2d]", voters.index(voter)
|
22
|
+
puts " #{Jira::Format.user(displayName)}"
|
23
|
+
end
|
24
|
+
else
|
25
|
+
puts "There are no votes on ticket #{ticket}."
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
desc "unvote", "Unvote against the input ticket"
|
31
|
+
def unvote(ticket=Jira::Core.ticket)
|
32
|
+
username = Jira::Core.username
|
33
|
+
self.api.delete("issue/#{ticket}/votes?username=#{username}") do |json|
|
34
|
+
puts "Successfully unvoted against ticket #{ticket}"
|
35
|
+
return
|
36
|
+
end
|
37
|
+
puts "No unvote."
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Jira
|
2
|
+
class CLI < Thor
|
3
|
+
|
4
|
+
desc "watch", "Watch against the input ticket"
|
5
|
+
def watch(ticket=Jira::Core.ticket)
|
6
|
+
self.api.post("issue/#{ticket}/watchers", Jira::Core.username) do |json|
|
7
|
+
puts "Successfully watched against ticket #{ticket}"
|
8
|
+
return
|
9
|
+
end
|
10
|
+
puts "No watch."
|
11
|
+
end
|
12
|
+
|
13
|
+
desc "watchers", "List the watchers of the input ticket"
|
14
|
+
def watchers(ticket=Jira::Core.ticket)
|
15
|
+
self.api.get("issue/#{ticket}/watchers") do |json|
|
16
|
+
watchers = json['watchers']
|
17
|
+
if watchers.count > 0
|
18
|
+
watchers.each do |watcher|
|
19
|
+
displayName = watcher['displayName']
|
20
|
+
|
21
|
+
printf "[%2d]", watchers.index(watcher)
|
22
|
+
puts " #{Jira::Format.user(displayName)}"
|
23
|
+
end
|
24
|
+
else
|
25
|
+
puts "There are no watchers on ticket #{ticket}."
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
desc "unwatch", "Unwatch against the input ticket"
|
31
|
+
def unwatch(ticket=Jira::Core.ticket)
|
32
|
+
username = Jira::Core.username
|
33
|
+
self.api.delete("issue/#{ticket}/watchers?username=#{username}") do |json|
|
34
|
+
puts "Successfully unwatched against ticket #{ticket}"
|
35
|
+
return
|
36
|
+
end
|
37
|
+
puts "No unwatch."
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
data/lib/jira/constants.rb
CHANGED
data/lib/jira/core.rb
CHANGED
@@ -7,7 +7,6 @@ module Jira
|
|
7
7
|
#
|
8
8
|
def setup
|
9
9
|
self.url
|
10
|
-
self.auth
|
11
10
|
end
|
12
11
|
|
13
12
|
### Virtual Attributes
|
@@ -16,21 +15,21 @@ module Jira
|
|
16
15
|
# @return [String] JIRA project endpoint
|
17
16
|
#
|
18
17
|
def url
|
19
|
-
@url ||= ENV['JIRA_URL'] || self.read(self.
|
18
|
+
@url ||= ENV['JIRA_URL'] || self.read(self.cli_path)[:global]['url']
|
20
19
|
end
|
21
20
|
|
22
21
|
#
|
23
22
|
# @return [String] JIRA username
|
24
23
|
#
|
25
24
|
def username
|
26
|
-
@username ||= ENV['JIRA_USERNAME'] || self.
|
25
|
+
@username ||= ENV['JIRA_USERNAME'] || self.read(self.cli_path)[:global]['username']
|
27
26
|
end
|
28
27
|
|
29
28
|
#
|
30
29
|
# @return [String] JIRA password
|
31
30
|
#
|
32
31
|
def password
|
33
|
-
@password ||= ENV['JIRA_PASSWORD'] || self.
|
32
|
+
@password ||= ENV['JIRA_PASSWORD'] || self.read(self.cli_path)[:global]['password']
|
34
33
|
end
|
35
34
|
|
36
35
|
#
|
@@ -61,17 +60,10 @@ module Jira
|
|
61
60
|
### Relevant Paths
|
62
61
|
|
63
62
|
#
|
64
|
-
# @return [String] path to .jira-
|
63
|
+
# @return [String] path to .jira-cli file
|
65
64
|
#
|
66
|
-
def
|
67
|
-
@
|
68
|
-
end
|
69
|
-
|
70
|
-
#
|
71
|
-
# @return [String] path to .jira-auth file
|
72
|
-
#
|
73
|
-
def auth_path
|
74
|
-
@auth_path ||= self.root_path + "/.jira-auth"
|
65
|
+
def cli_path
|
66
|
+
@cli_path ||= self.root_path + "/.jira-cli"
|
75
67
|
end
|
76
68
|
|
77
69
|
#
|
@@ -85,16 +77,6 @@ module Jira
|
|
85
77
|
|
86
78
|
protected
|
87
79
|
|
88
|
-
#
|
89
|
-
# Determines and parses the auth file
|
90
|
-
#
|
91
|
-
# @return [String] JIRA username
|
92
|
-
# @return [String] JIRA password
|
93
|
-
#
|
94
|
-
def auth
|
95
|
-
@auth ||= self.read(self.auth_path).split(':')
|
96
|
-
end
|
97
|
-
|
98
80
|
### Core Actions
|
99
81
|
|
100
82
|
#
|
@@ -102,7 +84,6 @@ module Jira
|
|
102
84
|
#
|
103
85
|
def discard_memoized
|
104
86
|
@url = nil
|
105
|
-
@auth = nil
|
106
87
|
@username = nil
|
107
88
|
@password = nil
|
108
89
|
end
|
@@ -112,11 +93,11 @@ module Jira
|
|
112
93
|
#
|
113
94
|
# @param path [String] path of file to read
|
114
95
|
#
|
115
|
-
# @return [
|
96
|
+
# @return [Object] IniFile object of the file at the input path
|
116
97
|
#
|
117
98
|
def read(path)
|
118
99
|
self.validate_path!(path)
|
119
|
-
|
100
|
+
IniFile.load(path, { :comment => '#', :encoding => 'UTF-8' })
|
120
101
|
end
|
121
102
|
|
122
103
|
#
|
data/lib/jira/io.rb
CHANGED
data/lib/jira/mixins.rb
CHANGED
@@ -4,6 +4,7 @@ module Jira
|
|
4
4
|
require 'json'
|
5
5
|
require 'faraday'
|
6
6
|
require 'inquirer'
|
7
|
+
require 'inifile'
|
7
8
|
include Thor::Actions
|
8
9
|
|
9
10
|
protected
|
@@ -30,6 +31,19 @@ module Jira
|
|
30
31
|
return klass
|
31
32
|
end
|
32
33
|
|
34
|
+
#
|
35
|
+
# Prompts the user for a type of index, then returns the
|
36
|
+
# it; failure is < 0
|
37
|
+
#
|
38
|
+
# @return index [Integer] asked type of index
|
39
|
+
#
|
40
|
+
def get_type_of_index(command, description)
|
41
|
+
response = self.io.ask("Index for #{command} to #{description}").strip
|
42
|
+
return -1 if response.empty?
|
43
|
+
index = response.to_i
|
44
|
+
return -1 if index < 0
|
45
|
+
index
|
46
|
+
end
|
33
47
|
end
|
34
48
|
end
|
35
49
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jira-cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Darren Lin Cheng
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-01-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -70,6 +70,26 @@ dependencies:
|
|
70
70
|
- - ">="
|
71
71
|
- !ruby/object:Gem::Version
|
72
72
|
version: 0.2.0
|
73
|
+
- !ruby/object:Gem::Dependency
|
74
|
+
name: inifile
|
75
|
+
requirement: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - "~>"
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: 2.0.2
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 2.0.2
|
83
|
+
type: :runtime
|
84
|
+
prerelease: false
|
85
|
+
version_requirements: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 2.0.2
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: 2.0.2
|
73
93
|
description: CLI used to manage JIRA workflows leveraging git
|
74
94
|
email: darren@thanx.com
|
75
95
|
executables:
|
@@ -81,19 +101,23 @@ files:
|
|
81
101
|
- bin/jira
|
82
102
|
- lib/jira.rb
|
83
103
|
- lib/jira/api.rb
|
104
|
+
- lib/jira/commands/assign.rb
|
84
105
|
- lib/jira/commands/attachments.rb
|
85
106
|
- lib/jira/commands/browse.rb
|
86
107
|
- lib/jira/commands/clipboard.rb
|
87
108
|
- lib/jira/commands/comment.rb
|
88
109
|
- lib/jira/commands/commit.rb
|
110
|
+
- lib/jira/commands/delete.rb
|
89
111
|
- lib/jira/commands/describe.rb
|
90
112
|
- lib/jira/commands/install.rb
|
91
113
|
- lib/jira/commands/log.rb
|
92
114
|
- lib/jira/commands/new.rb
|
93
115
|
- lib/jira/commands/rename.rb
|
94
|
-
- lib/jira/commands/
|
116
|
+
- lib/jira/commands/tickets.rb
|
95
117
|
- lib/jira/commands/transition.rb
|
96
118
|
- lib/jira/commands/version.rb
|
119
|
+
- lib/jira/commands/vote.rb
|
120
|
+
- lib/jira/commands/watch.rb
|
97
121
|
- lib/jira/constants.rb
|
98
122
|
- lib/jira/core.rb
|
99
123
|
- lib/jira/exceptions.rb
|
@@ -120,7 +144,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
120
144
|
version: '0'
|
121
145
|
requirements: []
|
122
146
|
rubyforge_project:
|
123
|
-
rubygems_version: 2.
|
147
|
+
rubygems_version: 2.4.5
|
124
148
|
signing_key:
|
125
149
|
specification_version: 4
|
126
150
|
summary: JIRA CLI
|
data/lib/jira/commands/sprint.rb
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
module Jira
|
2
|
-
class CLI < Thor
|
3
|
-
|
4
|
-
desc "sprint", "Lists all tickets in active sprint"
|
5
|
-
def sprint
|
6
|
-
# TODO
|
7
|
-
self.api(:agile).get(
|
8
|
-
'rapid/charts/sprintreport?rapidViewId=17&sprintId=41'
|
9
|
-
) do |response|
|
10
|
-
groups = {}
|
11
|
-
|
12
|
-
parser = ->(issue){
|
13
|
-
assignee = issue['assignee']
|
14
|
-
status = issue['statusName']
|
15
|
-
groups[assignee] ||= {}
|
16
|
-
groups[assignee][status] ||= []
|
17
|
-
groups[assignee][status] << {
|
18
|
-
ticket: issue['key'],
|
19
|
-
summary: issue['summary']
|
20
|
-
}
|
21
|
-
}
|
22
|
-
|
23
|
-
response['contents']['completedIssues'].each{|issue| parser.call(issue)}
|
24
|
-
response['contents']['incompletedIssues'].each{|issue| parser.call(issue)}
|
25
|
-
|
26
|
-
groups.each do |user, status_groups|
|
27
|
-
puts user
|
28
|
-
status_groups.each do |status, tickets|
|
29
|
-
puts " " + status
|
30
|
-
puts " " + tickets.map{|info| info[:ticket] }.join(' ')
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
end
|
37
|
-
end
|