jirify 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/jirify/cli/issue.rb +206 -21
- data/lib/jirify/cli/project.rb +12 -0
- data/lib/jirify/cli/setup.rb +34 -27
- data/lib/jirify/cli/sprint.rb +6 -4
- data/lib/jirify/config.rb +54 -8
- data/lib/jirify/models/base.rb +16 -0
- data/lib/jirify/models/issue.rb +106 -0
- data/lib/jirify/models/project.rb +9 -0
- data/lib/jirify/models/sprint.rb +24 -17
- data/lib/jirify/models/status.rb +49 -0
- data/lib/jirify/models/transition_list.rb +29 -0
- data/lib/jirify/version.rb +1 -1
- data/lib/jirify.rb +21 -4
- metadata +13 -9
- data/lib/jirify/models/issues.rb +0 -34
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5528c8d48b61117388daf001e8035ea61d6e3ed7f91efb1083d1e2d04d94db7b
|
4
|
+
data.tar.gz: fbe37f94e037385cc423df5bf27c9696ae807d0cb6263b070272fecc093e3ede
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5c145893008fe97f50f744d010463ec9fd64cbd48d5f861160674789797de01eb3052beb8ef5f18b410d8df352e587f9031dbe71ee0e8f2d0611b8ee7662556d
|
7
|
+
data.tar.gz: fbfdfb7d636c451a7e25d6f9fe52c19412bfc4e855c788a3dd744437522b2d66722df50cc1bc0563c82822fa4ebdeb5d15c4eb12c97a7fcf28b282f41b4b0f2a
|
data/lib/jirify/cli/issue.rb
CHANGED
@@ -1,25 +1,210 @@
|
|
1
1
|
module Jirify
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
2
|
+
module Subcommands
|
3
|
+
class Issues < Thor
|
4
|
+
default_task :mine
|
5
|
+
|
6
|
+
desc 'mine', 'List all of the issues assigned to you in the current sprint'
|
7
|
+
method_option :in_progress, type: :boolean, aliases: '-i', desc: 'Show only issues in progress'
|
8
|
+
method_option :in_review, type: :boolean, aliases: '-r', desc: 'Show only issues in review'
|
9
|
+
method_option :closed, type: :boolean, aliases: '-c', desc: 'Show only closed issues'
|
10
|
+
method_option :todo, type: :boolean, aliases: '-t', desc: 'Show only issues in todo'
|
11
|
+
method_option :blocked, type: :boolean, aliases: '-b', desc: 'Show only blocked issues'
|
12
|
+
method_option :all,
|
13
|
+
type: :boolean,
|
14
|
+
aliases: '-a',
|
15
|
+
desc: 'Show all assigned issues in Project - not limited to sprint'
|
16
|
+
method_option :status,
|
17
|
+
banner: '<status>',
|
18
|
+
type: :string,
|
19
|
+
aliases: '-s',
|
20
|
+
desc: 'Show only issues with the specified status'
|
21
|
+
method_option :statuses,
|
22
|
+
banner: '<statuses>',
|
23
|
+
type: :array,
|
24
|
+
desc: 'Show only issues with the specified statuses'
|
25
|
+
def mine
|
26
|
+
statuses = build_issue_statuses(options)
|
27
|
+
issues = Jirify::Issue.list_mine(statuses, options[:all])
|
28
|
+
issues.each { |issue| issue.print Config.always_verbose || options[:verbose] }
|
29
|
+
end
|
30
|
+
|
31
|
+
desc 'assignee [ISSUE]', 'Displays issue assignee'
|
32
|
+
def assignee(issue_id)
|
33
|
+
issue = get_issue_or_exit issue_id
|
34
|
+
|
35
|
+
if issue.assignee.nil?
|
36
|
+
puts 'Unassigned'.yellow
|
37
|
+
else
|
38
|
+
puts issue.assignee.name
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
desc 'unassign [ISSUE]', 'Unassign an issue'
|
43
|
+
def unassign(issue_id)
|
44
|
+
issue = get_issue_or_exit issue_id
|
45
|
+
|
46
|
+
if issue.assignee.nil?
|
47
|
+
puts 'Issue already unassigned'.yellow
|
48
|
+
exit(0)
|
49
|
+
end
|
50
|
+
|
51
|
+
puts "Previous assignee: #{issue.assignee.name}. Unassigning..."
|
52
|
+
issue.unassign!
|
53
|
+
end
|
54
|
+
|
55
|
+
desc 'take [ISSUE]', 'Assigns an issue to you'
|
56
|
+
def take(issue_id)
|
57
|
+
issue = get_issue_or_exit issue_id
|
58
|
+
|
59
|
+
puts "Assigning #{issue.key} to #{Config.username}..."
|
60
|
+
issue.assign_to_me!
|
61
|
+
end
|
62
|
+
|
63
|
+
desc 'status [ISSUE]', 'Displays issue status'
|
64
|
+
def status(issue_id)
|
65
|
+
issue = get_issue_or_exit issue_id
|
66
|
+
|
67
|
+
puts issue.status.name
|
68
|
+
end
|
69
|
+
|
70
|
+
desc 'transitions [ISSUE]', 'Displays available transitions'
|
71
|
+
def transitions(issue_id)
|
72
|
+
issue = get_issue_or_exit issue_id
|
73
|
+
|
74
|
+
puts 'Available transitions:'
|
75
|
+
puts issue.transitions.names
|
76
|
+
end
|
77
|
+
|
78
|
+
desc 'transition [ISSUE] [TRANSITION]', 'Manually perform a transition'
|
79
|
+
def transition(issue_id, transition_name)
|
80
|
+
issue = get_issue_or_exit issue_id
|
81
|
+
transition = issue.transitions.list.find { |t| t.name == transition_name }
|
82
|
+
|
83
|
+
if transition.nil?
|
84
|
+
puts "ERROR: Issue can't transition to #{transition_name}".red
|
85
|
+
exit(0)
|
86
|
+
end
|
87
|
+
|
88
|
+
puts "Transitioning #{issue.key} with #{transition_name}...".green
|
89
|
+
issue.transition! transition
|
90
|
+
end
|
91
|
+
|
92
|
+
desc 'block [ISSUE]', "Moves an issue to #{Config.statuses['blocked']}"
|
93
|
+
def block(issue_id)
|
94
|
+
issue = get_issue_or_exit issue_id
|
95
|
+
check_assigned_to_self issue
|
96
|
+
|
97
|
+
issue.reopen! if issue.done?
|
98
|
+
issue.stop_review! if issue.in_review?
|
99
|
+
issue.block!
|
100
|
+
end
|
101
|
+
|
102
|
+
desc 'unblock [ISSUE]', 'Unblocks an issue'
|
103
|
+
def unblock(issue_id)
|
104
|
+
issue = get_issue_or_exit issue_id
|
105
|
+
check_assigned_to_self issue
|
106
|
+
|
107
|
+
if issue.blocked?
|
108
|
+
puts 'Unblocking issue...'
|
109
|
+
issue.unblock!
|
110
|
+
else
|
111
|
+
puts 'Issue wasn\'t blocked anyway :)'.green
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
desc 'todo [ISSUE]', "Moves an issue to #{Config.statuses['todo']}"
|
116
|
+
def todo(issue_id)
|
117
|
+
issue = get_issue_or_exit issue_id
|
118
|
+
check_assigned_to_self issue
|
119
|
+
|
120
|
+
if issue.blocked?
|
121
|
+
issue.unblock!
|
122
|
+
elsif issue.in_progress?
|
123
|
+
issue.stop!
|
124
|
+
elsif issue.in_review?
|
125
|
+
issue.stop_review!
|
126
|
+
issue.stop!
|
127
|
+
elsif issue.done?
|
128
|
+
issue.reopen!
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
desc 'start [ISSUE]', "Moves an issue to #{Config.statuses['in_progress']}"
|
133
|
+
def start(issue_id)
|
134
|
+
issue = get_issue_or_exit issue_id
|
135
|
+
check_assigned_to_self issue
|
136
|
+
|
137
|
+
issue.unblock! if issue.blocked?
|
138
|
+
issue.reopen! if issue.done?
|
139
|
+
|
140
|
+
if issue.in_review?
|
141
|
+
issue.stop_review!
|
142
|
+
else
|
143
|
+
issue.start!
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
desc 'review [ISSUE]', "Moves an issue to #{Config.statuses['in_review']}"
|
148
|
+
def review(issue_id)
|
149
|
+
issue = get_issue_or_exit issue_id
|
150
|
+
check_assigned_to_self issue
|
151
|
+
|
152
|
+
if issue.blocked?
|
153
|
+
issue.unblock!
|
154
|
+
issue.start!
|
155
|
+
elsif issue.todo?
|
156
|
+
issue.start!
|
157
|
+
elsif issue.done?
|
158
|
+
issue.reopen!
|
159
|
+
issue.start!
|
160
|
+
end
|
161
|
+
|
162
|
+
issue.start_review!
|
163
|
+
end
|
164
|
+
|
165
|
+
desc 'close [ISSUE]', "Moves an issue to #{Config.statuses['done']}"
|
166
|
+
def close(issue_id)
|
167
|
+
issue = get_issue_or_exit issue_id
|
168
|
+
check_assigned_to_self issue
|
169
|
+
|
170
|
+
issue.unblock! if issue.blocked?
|
171
|
+
issue.close!
|
172
|
+
end
|
173
|
+
|
174
|
+
protected
|
175
|
+
|
176
|
+
def get_issue_or_exit(issue_id)
|
177
|
+
issue = Jirify::Issue.find_by_id(issue_id)
|
178
|
+
|
179
|
+
if issue.nil?
|
180
|
+
puts 'ERROR: Issue not found'.red
|
181
|
+
exit(0)
|
182
|
+
else
|
183
|
+
issue
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def check_assigned_to_self(issue)
|
188
|
+
unless issue.mine?
|
189
|
+
exit(0) unless yes? 'WARNING! This issue is not assigned to you!'\
|
190
|
+
' Are you sure you want to continue? [Y/n]:'.yellow
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def build_issue_statuses(options)
|
195
|
+
if options[:status]
|
196
|
+
statuses = [options[:status]]
|
197
|
+
else
|
198
|
+
statuses = []
|
199
|
+
statuses << Config.statuses['blocked'] if options[:blocked]
|
200
|
+
statuses << Config.statuses['todo'] if options[:todo]
|
201
|
+
statuses << Config.statuses['in_progress'] if options[:in_progress]
|
202
|
+
statuses << Config.statuses['in_review'] if options[:in_review]
|
203
|
+
statuses << Config.statuses['done'] if options[:closed]
|
204
|
+
end
|
205
|
+
|
206
|
+
statuses
|
207
|
+
end
|
23
208
|
end
|
24
209
|
end
|
25
210
|
end
|
data/lib/jirify/cli/setup.rb
CHANGED
@@ -1,37 +1,44 @@
|
|
1
1
|
module Jirify
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
reconfigure = yes? "You seem to have already configured jirify.\n" \
|
10
|
-
"Do you want to continue and overwrite the current configuration? [Y/N]"
|
11
|
-
exit(0) unless reconfigure
|
12
|
-
end
|
2
|
+
module Subcommands
|
3
|
+
class Setup < Thor
|
4
|
+
default_task :init
|
5
|
+
|
6
|
+
desc 'init', 'A guided setup for jirify'
|
7
|
+
def init
|
8
|
+
say 'Welcome! This will guide you through the configuration of the jirify CLI tool.'
|
13
9
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
filter_labels = ask "Enter a comma-separated list of labels to filter by every time (optional):"
|
10
|
+
if Config.initialized?
|
11
|
+
exit(0) unless yes? 'You seem to have already configured jirify. ' \
|
12
|
+
'Do you want to continue and overwrite the current configuration? [Y/n]:'.yellow
|
13
|
+
end
|
19
14
|
|
20
|
-
|
15
|
+
username = ask 'Enter username:'
|
16
|
+
token = ask 'Enter token (generate from https://id.atlassian.com):'
|
17
|
+
site = ask 'Enter JIRA url:'
|
18
|
+
project = ask 'Enter JIRA Project key:'
|
19
|
+
filter_labels = ask 'Enter a comma-separated list of labels to filter by every time (optional):'
|
21
20
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
'
|
26
|
-
|
27
|
-
|
21
|
+
labels = filter_labels.split ', ' if filter_labels
|
22
|
+
|
23
|
+
options = {
|
24
|
+
'options' => {
|
25
|
+
'username' => username,
|
26
|
+
'token' => token,
|
27
|
+
'site' => site,
|
28
|
+
'project' => project
|
29
|
+
}
|
28
30
|
}
|
29
|
-
}
|
30
31
|
|
31
|
-
|
32
|
+
options['options']['filter_by_labels'] = labels unless labels.empty?
|
32
33
|
|
33
|
-
|
34
|
+
Config.write(options)
|
35
|
+
end
|
36
|
+
|
37
|
+
desc 'verbose', 'Set always verbose to true or false'
|
38
|
+
method_option :enable, type: :boolean, aliases: '-e', default: false, desc: 'Enable or disable'
|
39
|
+
def verbose
|
40
|
+
Config.verbose(options[:enable])
|
41
|
+
end
|
34
42
|
end
|
35
43
|
end
|
36
44
|
end
|
37
|
-
|
data/lib/jirify/cli/sprint.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
module Jirify
|
2
2
|
class CLI < Thor
|
3
|
-
desc
|
4
|
-
method_option :mine, type: :boolean, aliases: '-m', desc:
|
5
|
-
method_option :all_columns, type: :boolean, aliases: '-a', desc:
|
3
|
+
desc 'sprint', 'Show the current sprint table'
|
4
|
+
method_option :mine, type: :boolean, aliases: '-m', desc: 'Show only issues assigned to me'
|
5
|
+
method_option :all_columns, type: :boolean, aliases: '-a', desc: 'Show all columns'
|
6
6
|
def sprint
|
7
|
-
Jirify::Sprint.current(options[:verbose],
|
7
|
+
Jirify::Sprint.current(Config.always_verbose || options[:verbose],
|
8
|
+
options[:all_columns],
|
9
|
+
options[:mine])
|
8
10
|
end
|
9
11
|
end
|
10
12
|
end
|
data/lib/jirify/config.rb
CHANGED
@@ -4,40 +4,86 @@ module Jirify
|
|
4
4
|
class Config
|
5
5
|
class << self
|
6
6
|
def initialized?
|
7
|
-
File.
|
7
|
+
File.exist? config_file
|
8
8
|
end
|
9
9
|
|
10
10
|
def write(config)
|
11
|
-
puts
|
11
|
+
puts 'Writing config:'
|
12
12
|
puts config.to_yaml
|
13
13
|
|
14
14
|
File.write(config_file, config.to_yaml)
|
15
15
|
end
|
16
16
|
|
17
|
+
def verbose(value)
|
18
|
+
unless initialized?
|
19
|
+
puts 'ERROR: You must initialize Jirify first!'.red
|
20
|
+
exit(0)
|
21
|
+
end
|
22
|
+
|
23
|
+
config = YAML.load_file(config_file)
|
24
|
+
config['options']['verbose'] = value
|
25
|
+
write(config)
|
26
|
+
end
|
27
|
+
|
17
28
|
def config_file
|
18
29
|
@config_file ||= "#{Dir.home}/.jirify"
|
19
30
|
end
|
20
31
|
|
21
32
|
def options
|
22
|
-
|
33
|
+
unless initialized?
|
34
|
+
puts 'ERROR: You must initialize Jirify first!'.red
|
35
|
+
exit(0)
|
36
|
+
end
|
37
|
+
|
23
38
|
@options ||= YAML.load_file(config_file)['options']
|
24
39
|
end
|
25
40
|
|
41
|
+
def always_verbose
|
42
|
+
options['verbose']
|
43
|
+
end
|
44
|
+
|
26
45
|
def atlassian_url
|
27
46
|
options['site']
|
28
47
|
end
|
29
48
|
|
49
|
+
def username
|
50
|
+
options['username']
|
51
|
+
end
|
52
|
+
|
30
53
|
def issue_browse_url
|
31
54
|
"#{atlassian_url}/browse/"
|
32
55
|
end
|
33
56
|
|
57
|
+
def statuses
|
58
|
+
options['statuses'] || {
|
59
|
+
'blocked' => 'Blocked',
|
60
|
+
'todo' => 'To Do',
|
61
|
+
'in_progress' => 'In Progress',
|
62
|
+
'in_review' => 'In Review',
|
63
|
+
'done' => 'Closed'
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
def transitions
|
68
|
+
options['transitions'] || {
|
69
|
+
'block' => 'Blocked',
|
70
|
+
'unblock' => 'Unblock',
|
71
|
+
'start' => 'Start Progress',
|
72
|
+
'stop' => 'Stop Progress',
|
73
|
+
'start_review' => 'Code Review',
|
74
|
+
'stop_review' => 'Back to In Progress',
|
75
|
+
'close' => 'Close',
|
76
|
+
'reopen' => 'Reopen'
|
77
|
+
}
|
78
|
+
end
|
79
|
+
|
34
80
|
def client_options
|
35
81
|
{
|
36
|
-
:
|
37
|
-
:
|
38
|
-
:
|
39
|
-
:
|
40
|
-
:
|
82
|
+
username: options['username'],
|
83
|
+
password: options['token'],
|
84
|
+
site: atlassian_url,
|
85
|
+
context_path: '',
|
86
|
+
auth_type: :basic
|
41
87
|
}
|
42
88
|
end
|
43
89
|
end
|
data/lib/jirify/models/base.rb
CHANGED
@@ -1,5 +1,21 @@
|
|
1
1
|
module Jirify
|
2
2
|
class Base
|
3
|
+
def initialize(entity)
|
4
|
+
@entity = entity
|
5
|
+
end
|
6
|
+
|
7
|
+
def method_missing(method, *args, &_block)
|
8
|
+
if @entity.respond_to? method
|
9
|
+
@entity.send method, *args
|
10
|
+
else
|
11
|
+
super
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def respond_to_missing?(method, *)
|
16
|
+
@entity.respond_to? method
|
17
|
+
end
|
18
|
+
|
3
19
|
class << self
|
4
20
|
def client
|
5
21
|
@client ||= JIRA::Client.new(Config.client_options)
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module JIRA
|
2
|
+
module Resource
|
3
|
+
class Issue
|
4
|
+
def transition!(transition)
|
5
|
+
attrs = { transition: transition.id }.to_json
|
6
|
+
client.send(:post, "#{url}/transitions", attrs)
|
7
|
+
end
|
8
|
+
|
9
|
+
def assign_to!(username)
|
10
|
+
client.send(:put, "#{url}/assignee", { name: username }.to_json)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module Jirify
|
17
|
+
class Issue < Base
|
18
|
+
class InvalidTransitionError < StandardError; end
|
19
|
+
|
20
|
+
def mine?
|
21
|
+
!assignee.nil? && assignee.emailAddress == Config.username
|
22
|
+
end
|
23
|
+
|
24
|
+
def assign_to_me!
|
25
|
+
@entity.assign_to!(Config.username.split('@')[0])
|
26
|
+
end
|
27
|
+
|
28
|
+
def unassign!
|
29
|
+
@entity.assign_to!(nil)
|
30
|
+
end
|
31
|
+
|
32
|
+
def status
|
33
|
+
@status ||= Jirify::Status.new @entity.status
|
34
|
+
end
|
35
|
+
|
36
|
+
def status?(status_name)
|
37
|
+
status_name = status_name.to_s if status_name.is_a? Symbol
|
38
|
+
status.name == status_name
|
39
|
+
end
|
40
|
+
|
41
|
+
Config.statuses.keys.each do |status_key|
|
42
|
+
define_method "#{status_key.to_sym}?" do
|
43
|
+
status.name == Config.statuses[status_key]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def transitions(reload = false)
|
48
|
+
if reload
|
49
|
+
@transitions = Jirify::TransitionList.all @entity
|
50
|
+
else
|
51
|
+
@transitions ||= Jirify::TransitionList.all @entity
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
Config.transitions.keys.each do |transition_name|
|
56
|
+
define_method "#{transition_name}!".to_sym do
|
57
|
+
transition = transitions(true).send(transition_name.to_sym)
|
58
|
+
|
59
|
+
if transition.nil?
|
60
|
+
puts "ERROR: Issue can't be transitioned with \"#{transition_name}\"".red
|
61
|
+
exit(0)
|
62
|
+
end
|
63
|
+
|
64
|
+
puts "Transitioning #{key} with \"#{transition_name}\"...".green
|
65
|
+
@entity.transition! transition
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def print(verbose)
|
70
|
+
url = "#{Config.issue_browse_url}#{key}".blue
|
71
|
+
|
72
|
+
if verbose
|
73
|
+
puts "#{status.pretty_name} #{key.ljust(7)}: #{summary} (#{url})"
|
74
|
+
else
|
75
|
+
puts "#{key.ljust(7)}: (#{url})"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class << self
|
80
|
+
def list_mine(statuses = [], all = false)
|
81
|
+
my_issues = find_mine(all).sort_by { |issue| issue.status.name }
|
82
|
+
|
83
|
+
my_issues.select do |issue|
|
84
|
+
statuses.empty? || statuses.any? { |status| issue.status? status }
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def find_mine(all)
|
89
|
+
client.Issue.jql(my_issues_jql(all)).map { |issue| Issue.new issue }
|
90
|
+
end
|
91
|
+
|
92
|
+
def find_by_id(issue_id)
|
93
|
+
Issue.new client.Issue.find(issue_id)
|
94
|
+
rescue StandardError
|
95
|
+
nil
|
96
|
+
end
|
97
|
+
|
98
|
+
protected
|
99
|
+
|
100
|
+
def my_issues_jql(all_issues)
|
101
|
+
all_clause = 'AND sprint in openSprints()' unless all_issues
|
102
|
+
"project='#{project}' #{all_clause} AND assignee='#{Config.username}'"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
data/lib/jirify/models/sprint.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'colorize'
|
2
1
|
require 'terminal-table'
|
3
2
|
require 'json'
|
4
3
|
|
@@ -6,41 +5,49 @@ module Jirify
|
|
6
5
|
class Sprint < Base
|
7
6
|
class << self
|
8
7
|
def current(verbose = false, all_columns = false, only_mine = false)
|
8
|
+
issues = client.Issue.jql current_sprint_jql(only_mine), max_results: 200
|
9
|
+
|
10
|
+
grouped_issues = issues.group_by do |issue|
|
11
|
+
all_columns ? issue.status.name : issue.status.statusCategory['name']
|
12
|
+
end
|
13
|
+
|
14
|
+
print_grouped_issues_as_table(grouped_issues, verbose)
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def current_sprint_jql(only_mine)
|
9
20
|
labels = Config.options['filter_by_labels']
|
10
|
-
labels = labels.join(
|
21
|
+
labels = labels.join(', ') if labels
|
11
22
|
|
12
23
|
labels_clause = "AND labels in (#{labels})" if labels
|
13
|
-
mine_clause
|
24
|
+
mine_clause = "AND assignee='#{Config.username}'" if only_mine
|
25
|
+
sprint_clause = 'AND sprint in openSprints()'
|
14
26
|
|
15
|
-
|
16
|
-
|
17
|
-
if all_columns
|
18
|
-
grouped_issues = issues.group_by { |issue| issue.status.name }
|
19
|
-
else
|
20
|
-
grouped_issues = issues.group_by { |issue| issue.status.statusCategory['name'] }
|
21
|
-
end
|
27
|
+
"project='#{project}' #{sprint_clause} #{labels_clause} #{mine_clause}"
|
28
|
+
end
|
22
29
|
|
30
|
+
def print_grouped_issues_as_table(grouped_issues, verbose)
|
23
31
|
all_groups = grouped_issues.values
|
24
32
|
|
25
33
|
l = all_groups.map(&:length).max
|
26
|
-
transposed = all_groups.map{ |e| e.values_at(0...l) }.transpose
|
34
|
+
transposed = all_groups.map { |e| e.values_at(0...l) }.transpose
|
27
35
|
|
28
36
|
transposed = transposed.map do |row|
|
29
37
|
row.map do |issue|
|
30
38
|
if issue
|
31
|
-
key
|
32
|
-
url
|
39
|
+
key = issue.key.ljust(7)
|
40
|
+
url = "(#{Config.issue_browse_url}/#{issue.key})".blue
|
33
41
|
summary = "#{issue.summary[0, 35]}...\n" if verbose
|
42
|
+
|
34
43
|
"#{key}: #{summary}#{url}"
|
35
44
|
else
|
36
|
-
|
45
|
+
''
|
37
46
|
end
|
38
47
|
end
|
39
48
|
end
|
40
49
|
|
41
|
-
|
42
|
-
|
43
|
-
puts table unless transposed.empty?
|
50
|
+
puts Terminal::Table.new headings: grouped_issues.keys, rows: transposed unless transposed.empty?
|
44
51
|
end
|
45
52
|
end
|
46
53
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Jirify
|
2
|
+
class Status < Base
|
3
|
+
def pretty_name
|
4
|
+
justified = "(#{name})".rjust(longest_status_name + 2)
|
5
|
+
case name
|
6
|
+
when Config.statuses['blocked'] then justified.red
|
7
|
+
when Config.statuses['done'] then justified.green
|
8
|
+
when Config.statuses['in_progress'] then justified.blue
|
9
|
+
when Config.statuses['in_review'] then justified.yellow
|
10
|
+
when Config.statuses['todo'] then justified.black
|
11
|
+
else justified
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
def longest_status_name
|
18
|
+
Config.statuses.values.map(&:length).max
|
19
|
+
end
|
20
|
+
|
21
|
+
class << self
|
22
|
+
def all
|
23
|
+
@all ||= client.Status.all
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_do
|
27
|
+
@to_do ||= find_by_name Config.statuses['todo']
|
28
|
+
end
|
29
|
+
|
30
|
+
def in_progress
|
31
|
+
@in_progress ||= find_by_name Config.statuses['in_progress']
|
32
|
+
end
|
33
|
+
|
34
|
+
def in_review
|
35
|
+
@in_review ||= find_by_name Config.statuses['in_review']
|
36
|
+
end
|
37
|
+
|
38
|
+
def closed
|
39
|
+
@closed ||= find_by_name Config.statuses['done']
|
40
|
+
end
|
41
|
+
|
42
|
+
protected
|
43
|
+
|
44
|
+
def find_by_name(status_name)
|
45
|
+
all.select { |status| status.name == status_name }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Jirify
|
2
|
+
class TransitionList < Base
|
3
|
+
attr_accessor :list
|
4
|
+
|
5
|
+
def initialize(list)
|
6
|
+
@list = list
|
7
|
+
end
|
8
|
+
|
9
|
+
def names
|
10
|
+
@list.map(&:name)
|
11
|
+
end
|
12
|
+
|
13
|
+
Config.transitions.keys.each do |transition_name|
|
14
|
+
define_method(transition_name.to_sym) { find_by_name transition_name }
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def find_by_name(name)
|
20
|
+
@list.find { |transition| transition.name == Config.transitions[name] }
|
21
|
+
end
|
22
|
+
|
23
|
+
class << self
|
24
|
+
def all(issue)
|
25
|
+
TransitionList.new client.Transition.all(issue: issue)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/jirify/version.rb
CHANGED
data/lib/jirify.rb
CHANGED
@@ -1,16 +1,33 @@
|
|
1
1
|
require 'thor'
|
2
2
|
require 'jira-ruby'
|
3
|
+
require 'colorize'
|
3
4
|
|
4
5
|
require 'jirify/config'
|
6
|
+
require 'jirify/models/base'
|
7
|
+
require 'jirify/models/status'
|
8
|
+
require 'jirify/models/transition_list'
|
9
|
+
require 'jirify/models/issue'
|
10
|
+
require 'jirify/models/sprint'
|
11
|
+
require 'jirify/models/project'
|
5
12
|
require 'jirify/cli/setup'
|
6
13
|
require 'jirify/cli/sprint'
|
7
14
|
require 'jirify/cli/issue'
|
8
|
-
require 'jirify/
|
9
|
-
require 'jirify/models/issues'
|
10
|
-
require 'jirify/models/sprint'
|
15
|
+
require 'jirify/cli/project'
|
11
16
|
|
12
17
|
module Jirify
|
13
18
|
class CLI < Thor
|
14
|
-
class_option :verbose, type: :boolean, aliases: '-v', desc:
|
19
|
+
class_option :verbose, type: :boolean, aliases: '-v', desc: 'Show more verbose information'
|
20
|
+
|
21
|
+
desc 'setup SUBCOMMAND', 'Jirify setup tools'
|
22
|
+
subcommand 'setup', Subcommands::Setup
|
23
|
+
|
24
|
+
desc 'issues SUBCOMMAND', 'Work with JIRA Issues'
|
25
|
+
subcommand 'issues', Subcommands::Issues
|
26
|
+
|
27
|
+
desc 'i SUBCOMMAND', 'Work with JIRA Issues'
|
28
|
+
subcommand 'i', Subcommands::Issues
|
29
|
+
|
30
|
+
desc 'projects SUBCOMMAND', 'Work with JIRA Projects'
|
31
|
+
subcommand 'projects', Subcommands::Projects
|
15
32
|
end
|
16
33
|
end
|
metadata
CHANGED
@@ -1,43 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jirify
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Georgi Gardev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-07-
|
11
|
+
date: 2018-07-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: colorize
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '0.8'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '0.8'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: jira-ruby
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '1.5'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '1.5'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: terminal-table
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -77,12 +77,16 @@ files:
|
|
77
77
|
- bin/jira
|
78
78
|
- lib/jirify.rb
|
79
79
|
- lib/jirify/cli/issue.rb
|
80
|
+
- lib/jirify/cli/project.rb
|
80
81
|
- lib/jirify/cli/setup.rb
|
81
82
|
- lib/jirify/cli/sprint.rb
|
82
83
|
- lib/jirify/config.rb
|
83
84
|
- lib/jirify/models/base.rb
|
84
|
-
- lib/jirify/models/
|
85
|
+
- lib/jirify/models/issue.rb
|
86
|
+
- lib/jirify/models/project.rb
|
85
87
|
- lib/jirify/models/sprint.rb
|
88
|
+
- lib/jirify/models/status.rb
|
89
|
+
- lib/jirify/models/transition_list.rb
|
86
90
|
- lib/jirify/version.rb
|
87
91
|
homepage: https://github.com/GeorgeSG/jirify
|
88
92
|
licenses:
|
data/lib/jirify/models/issues.rb
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
require 'colorize'
|
2
|
-
|
3
|
-
module Jirify
|
4
|
-
class Issues < Base
|
5
|
-
class << self
|
6
|
-
def mine(verbose, statuses = [], all = false)
|
7
|
-
all_clause = "AND sprint in openSprints() " unless all
|
8
|
-
my_issues = client.Issue.jql("project='#{project}' #{all_clause}AND assignee='#{Config.options["username"]}'")
|
9
|
-
|
10
|
-
my_issues.sort_by! { |issue| issue.status.name }
|
11
|
-
my_issues.each do |issue|
|
12
|
-
status = issue.status.name
|
13
|
-
|
14
|
-
next if statuses.any? && !statuses.include?(status)
|
15
|
-
|
16
|
-
status_justified = "(#{status})".rjust(14)
|
17
|
-
status_colorized = case status
|
18
|
-
when "Done" then status_justified.green
|
19
|
-
when "In Progress" then status_justified.blue
|
20
|
-
when "In Review" then status_justified.yellow
|
21
|
-
when "Closed" then status_justified.black
|
22
|
-
else status_justified
|
23
|
-
end
|
24
|
-
|
25
|
-
if verbose
|
26
|
-
print "#{status_colorized} #{issue.key.ljust(7)}: #{issue.summary} (#{Config.issue_browse_url}#{issue.key})\n"
|
27
|
-
else
|
28
|
-
print "#{issue.key.ljust(7)}: (#{Config.issue_browse_url}#{issue.key})\n"
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|