jira-cli 0.1.6 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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
|