jirify 0.1.1 → 0.1.2
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/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
|