jirify 0.1.6 → 0.1.7
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 +27 -3
- data/lib/jirify/cli/issue.rb +23 -23
- data/lib/jirify/cli/project.rb +3 -3
- data/lib/jirify/cli/setup.rb +1 -1
- data/lib/jirify/cli/sprint.rb +6 -3
- data/lib/jirify/config.rb +3 -3
- data/lib/jirify/models/base.rb +21 -19
- data/lib/jirify/models/issue.rb +65 -63
- data/lib/jirify/models/project.rb +6 -4
- data/lib/jirify/models/sprint.rb +14 -43
- data/lib/jirify/models/status.rb +35 -36
- data/lib/jirify/models/transition_list.rb +21 -19
- data/lib/jirify/ui/sprint_cell.rb +63 -0
- data/lib/jirify/ui/sprint_table.rb +80 -0
- data/lib/jirify/version.rb +1 -1
- data/lib/jirify.rb +5 -2
- metadata +181 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eb5f0f6dfffdfe5bf7ba883f13791a579a5defaaa5887cb210bb2c8d4022dbe6
|
4
|
+
data.tar.gz: 44f3bff0804ebe0a796d94417d690a3babb17fab8b3d3403a7882195defe8b67
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6d5069a25d999001f6e63a273d2ea32a3bd89b96b8586f77562cd4c16cd87806a892953a17e8656f460ed3b15c53ff58621a60120d95c689891f1eeecf83b85a
|
7
|
+
data.tar.gz: d4a806b94ab345ba3d89feb15f551ff8f3801a4713004331fc143b36b5abbd80ab24f1225b25561e7962c9eef8b72850413191898dd4a908e3c2b885688184a6
|
data/README.md
CHANGED
@@ -1,10 +1,34 @@
|
|
1
1
|
# jirify
|
2
|
+
[](https://badge.fury.io/rb/jirify)
|
3
|
+
[](https://travis-ci.org/GeorgeSG/jirify)
|
4
|
+
[](https://coveralls.io/github/GeorgeSG/jirify?branch=master)
|
5
|
+
|
2
6
|
A simple ruby gem that helps me work with jira
|
3
7
|
|
4
|
-
|
8
|
+
## Installation
|
5
9
|
1. Run `gem install jirify`.
|
6
|
-
1. Execute `jira setup` and go through the setup process
|
10
|
+
1. Execute `jira setup` and go through the setup process.
|
11
|
+
1. Optionally source `$HOME/.jirify/jirify.bash_completion.sh` to have autocomplete in bash.
|
7
12
|
1. Execute `jira` and `jira <command> help` to learn about available commands.
|
8
13
|
|
9
|
-
|
14
|
+
## Config Explained
|
15
|
+
Currently, the config structure of `jirify` is:
|
16
|
+
- `$HOME/.jirify` folder that contains:
|
17
|
+
- `.jirify` - yaml file generated by `jira setup`
|
18
|
+
- `jirify.bash_completion.sh` - bash completion script you can source. This is placed here by `jira setup`, so if you don't see it or you want to refresh it, run `jira setup` again.
|
19
|
+
- `.cache` - cache for completion script
|
20
|
+
|
21
|
+
### Config file: `$HOME/.jirify/.jirify`
|
22
|
+
```yaml
|
23
|
+
options:
|
24
|
+
username: <atlassian username (email)>
|
25
|
+
token: <token generated from https://id.atlassian.com>
|
26
|
+
site: <JIRA url>
|
27
|
+
project: <JIRA project key>
|
28
|
+
filter_by_labels:
|
29
|
+
- <label to filter by when displaying sprint>
|
30
|
+
verbose: <force jirify to always be verbose>
|
31
|
+
```
|
32
|
+
|
33
|
+
## To Do
|
10
34
|
- Add ability to define mapping between custom statuses and custom transitions in config.
|
data/lib/jirify/cli/issue.rb
CHANGED
@@ -27,12 +27,12 @@ module Jirify
|
|
27
27
|
desc: 'Show only issues with the specified statuses'
|
28
28
|
def mine
|
29
29
|
statuses = build_issue_statuses(options)
|
30
|
-
issues =
|
30
|
+
issues = Models::Issue.list_mine(statuses, options[:all])
|
31
31
|
issues.each do |issue|
|
32
32
|
if options[:key_only]
|
33
|
-
|
33
|
+
say issue.key
|
34
34
|
else
|
35
|
-
issue.
|
35
|
+
say issue.to_s Config.always_verbose || options[:verbose]
|
36
36
|
end
|
37
37
|
end
|
38
38
|
end
|
@@ -53,9 +53,9 @@ module Jirify
|
|
53
53
|
issue = get_issue_or_exit issue_id
|
54
54
|
|
55
55
|
if issue.assignee.nil?
|
56
|
-
|
56
|
+
say 'Unassigned'.yellow
|
57
57
|
else
|
58
|
-
|
58
|
+
say issue.assignee.name
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
@@ -64,11 +64,11 @@ module Jirify
|
|
64
64
|
issue = get_issue_or_exit issue_id
|
65
65
|
|
66
66
|
if issue.assignee.nil?
|
67
|
-
|
67
|
+
say 'Issue already unassigned'.yellow
|
68
68
|
exit(0)
|
69
69
|
end
|
70
70
|
|
71
|
-
|
71
|
+
say "Previous assignee: #{issue.assignee.name}. Unassigning..."
|
72
72
|
issue.unassign!
|
73
73
|
end
|
74
74
|
|
@@ -76,7 +76,7 @@ module Jirify
|
|
76
76
|
def take(issue_id)
|
77
77
|
issue = get_issue_or_exit issue_id
|
78
78
|
|
79
|
-
|
79
|
+
say "Assigning #{issue.key} to #{Config.username}..."
|
80
80
|
issue.assign_to_me!
|
81
81
|
end
|
82
82
|
|
@@ -88,28 +88,28 @@ module Jirify
|
|
88
88
|
def status(issue_id)
|
89
89
|
issue = get_issue_or_exit issue_id
|
90
90
|
|
91
|
-
|
91
|
+
say "Status: #{issue.status.name}"
|
92
92
|
end
|
93
93
|
|
94
94
|
desc 'transitions [ISSUE]', 'Display available transitions'
|
95
95
|
def transitions(issue_id)
|
96
96
|
issue = get_issue_or_exit issue_id
|
97
97
|
|
98
|
-
|
99
|
-
|
98
|
+
say 'Available transitions:'
|
99
|
+
issue.transitions.names.each { |name| say name }
|
100
100
|
end
|
101
101
|
|
102
102
|
desc 'transition [ISSUE] [TRANSITION]', 'Manually perform a transition'
|
103
103
|
def transition(issue_id, transition_name)
|
104
104
|
issue = get_issue_or_exit issue_id
|
105
|
-
transition = issue.transitions.
|
105
|
+
transition = issue.transitions.find_by_name(transition_name)
|
106
106
|
|
107
107
|
if transition.nil?
|
108
|
-
|
108
|
+
say "ERROR: Issue can't transition to #{transition_name}".red
|
109
109
|
exit(0)
|
110
110
|
end
|
111
111
|
|
112
|
-
|
112
|
+
say "Transitioning #{issue.key} with #{transition_name}...".green
|
113
113
|
issue.transition! transition
|
114
114
|
end
|
115
115
|
|
@@ -129,10 +129,10 @@ module Jirify
|
|
129
129
|
check_assigned_to_self issue
|
130
130
|
|
131
131
|
if issue.blocked?
|
132
|
-
|
132
|
+
say 'Unblocking issue...'
|
133
133
|
issue.unblock!
|
134
134
|
else
|
135
|
-
|
135
|
+
say 'Issue wasn\'t blocked anyway :)'.green
|
136
136
|
end
|
137
137
|
end
|
138
138
|
|
@@ -198,10 +198,10 @@ module Jirify
|
|
198
198
|
protected
|
199
199
|
|
200
200
|
def get_issue_or_exit(issue_id)
|
201
|
-
issue =
|
201
|
+
issue = Models::Issue.find_by_id(issue_id)
|
202
202
|
|
203
203
|
if issue.nil?
|
204
|
-
|
204
|
+
say 'ERROR: Issue not found'.red
|
205
205
|
exit(0)
|
206
206
|
else
|
207
207
|
issue
|
@@ -209,13 +209,13 @@ module Jirify
|
|
209
209
|
end
|
210
210
|
|
211
211
|
def check_assigned_to_self(issue)
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
212
|
+
return if issue.mine?
|
213
|
+
|
214
|
+
exit(0) unless yes? 'WARNING! This issue is not assigned to you!'\
|
215
|
+
' Are you sure you want to continue? [Y/n]:'.yellow
|
216
216
|
end
|
217
217
|
|
218
|
-
def build_issue_statuses(options)
|
218
|
+
def build_issue_statuses(options) # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
219
219
|
if options[:status]
|
220
220
|
statuses = [options[:status]]
|
221
221
|
else
|
data/lib/jirify/cli/project.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
module Jirify
|
2
2
|
module Subcommands
|
3
3
|
class Projects < Thor
|
4
|
+
default_task :list
|
5
|
+
|
4
6
|
desc 'list', 'List all projects'
|
5
7
|
def list
|
6
|
-
|
8
|
+
Models::Project.all.map(&:name).each { |name| say name }
|
7
9
|
end
|
8
|
-
|
9
|
-
default_task :list
|
10
10
|
end
|
11
11
|
end
|
12
12
|
end
|
data/lib/jirify/cli/setup.rb
CHANGED
data/lib/jirify/cli/sprint.rb
CHANGED
@@ -4,9 +4,12 @@ module Jirify
|
|
4
4
|
method_option :mine, type: :boolean, aliases: '-m', desc: 'Show only issues assigned to me'
|
5
5
|
method_option :all_columns, type: :boolean, aliases: '-a', desc: 'Show all columns'
|
6
6
|
def sprint
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
verbose = Config.always_verbose || options[:verbose]
|
8
|
+
issues = Models::Sprint.issues_in_current_sprint(options[:mine])
|
9
|
+
|
10
|
+
say UI::SprintTable.new(issues).to_table(options[:all_columns], verbose)
|
11
|
+
rescue UI::WindowTooNarrow
|
12
|
+
say 'ERROR: Your terminal window is too narrow to print the sprint table!'.red
|
10
13
|
end
|
11
14
|
end
|
12
15
|
end
|
data/lib/jirify/config.rb
CHANGED
@@ -22,9 +22,9 @@ module Jirify
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def initialize!
|
25
|
-
FileUtils
|
26
|
-
FileUtils
|
27
|
-
FileUtils
|
25
|
+
FileUtils.mkdir_p CONFIG_FOLDER
|
26
|
+
FileUtils.touch CONFIG_FILE
|
27
|
+
FileUtils.cp "#{File.expand_path('..', File.dirname(__dir__))}/jirify.bash_completion.sh", CONFIG_FOLDER
|
28
28
|
end
|
29
29
|
|
30
30
|
def write(config)
|
data/lib/jirify/models/base.rb
CHANGED
@@ -1,28 +1,30 @@
|
|
1
1
|
module Jirify
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
def method_missing(method, *args, &_block)
|
8
|
-
if @entity.respond_to? method
|
9
|
-
@entity.send method, *args
|
10
|
-
else
|
11
|
-
super
|
2
|
+
module Models
|
3
|
+
class Base
|
4
|
+
def initialize(entity)
|
5
|
+
@entity = entity
|
12
6
|
end
|
13
|
-
end
|
14
7
|
|
15
|
-
|
16
|
-
|
17
|
-
|
8
|
+
def method_missing(method, *args, &_block)
|
9
|
+
if @entity.respond_to? method
|
10
|
+
@entity.send method, *args
|
11
|
+
else
|
12
|
+
super
|
13
|
+
end
|
14
|
+
end
|
18
15
|
|
19
|
-
|
20
|
-
|
21
|
-
@client ||= JIRA::Client.new(Config.client_options)
|
16
|
+
def respond_to_missing?(method, *)
|
17
|
+
@entity.respond_to? method
|
22
18
|
end
|
23
19
|
|
24
|
-
|
25
|
-
|
20
|
+
class << self
|
21
|
+
def client
|
22
|
+
@client ||= JIRA::Client.new(Config.client_options)
|
23
|
+
end
|
24
|
+
|
25
|
+
def project
|
26
|
+
@project ||= Config.options['project']
|
27
|
+
end
|
26
28
|
end
|
27
29
|
end
|
28
30
|
end
|
data/lib/jirify/models/issue.rb
CHANGED
@@ -1,90 +1,92 @@
|
|
1
1
|
module Jirify
|
2
|
-
|
3
|
-
class
|
2
|
+
module Models
|
3
|
+
class Issue < Base
|
4
|
+
class InvalidTransitionError < StandardError; end
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
6
|
+
def mine?
|
7
|
+
!assignee.nil? && assignee.emailAddress == Config.username
|
8
|
+
end
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
10
|
+
def assign_to_me!
|
11
|
+
@entity.assign_to!(Config.username.split('@')[0])
|
12
|
+
end
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
14
|
+
def unassign!
|
15
|
+
@entity.assign_to!(nil)
|
16
|
+
end
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
|
18
|
+
def status
|
19
|
+
@status ||= Status.new @entity.status
|
20
|
+
end
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
22
|
+
def status?(status_name)
|
23
|
+
status_name = status_name.to_s if status_name.is_a? Symbol
|
24
|
+
status.name == status_name
|
25
|
+
end
|
25
26
|
|
26
|
-
|
27
|
-
|
28
|
-
|
27
|
+
Config.statuses.keys.each do |status_key|
|
28
|
+
define_method "#{status_key.to_sym}?" do
|
29
|
+
status.name == Config.statuses[status_key]
|
30
|
+
end
|
29
31
|
end
|
30
|
-
end
|
31
32
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
33
|
+
def transitions(reload = false)
|
34
|
+
if reload
|
35
|
+
@transitions = TransitionList.all @entity
|
36
|
+
else
|
37
|
+
@transitions ||= TransitionList.all @entity
|
38
|
+
end
|
37
39
|
end
|
38
|
-
end
|
39
40
|
|
40
|
-
|
41
|
-
|
42
|
-
|
41
|
+
Config.transitions.keys.each do |transition_name|
|
42
|
+
define_method "#{transition_name}!".to_sym do
|
43
|
+
transition = transitions(true).send(transition_name.to_sym)
|
43
44
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
45
|
+
if transition.nil?
|
46
|
+
puts "ERROR: Issue can't be transitioned with \"#{transition_name}\"".red
|
47
|
+
exit(0)
|
48
|
+
end
|
48
49
|
|
49
|
-
|
50
|
-
|
50
|
+
puts "Transitioning #{key} with \"#{transition_name}\"...".green
|
51
|
+
@entity.transition! transition
|
52
|
+
end
|
51
53
|
end
|
52
|
-
end
|
53
54
|
|
54
|
-
|
55
|
-
|
55
|
+
def to_s(verbose)
|
56
|
+
url = "#{Config.issue_browse_url}#{key}".blue
|
56
57
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
58
|
+
if verbose
|
59
|
+
"#{status.pretty_name} #{key.ljust(7)}: #{summary} (#{url})"
|
60
|
+
else
|
61
|
+
"#{key.ljust(7)}: (#{url})"
|
62
|
+
end
|
61
63
|
end
|
62
|
-
end
|
63
64
|
|
64
|
-
|
65
|
-
|
66
|
-
|
65
|
+
class << self
|
66
|
+
def list_mine(statuses = [], all = false)
|
67
|
+
my_issues = find_mine(all).sort_by { |issue| issue.status.name }
|
67
68
|
|
68
|
-
|
69
|
-
|
69
|
+
my_issues.select do |issue|
|
70
|
+
statuses.empty? || statuses.any? { |status| issue.status? status }
|
71
|
+
end
|
70
72
|
end
|
71
|
-
end
|
72
73
|
|
73
|
-
|
74
|
-
|
75
|
-
|
74
|
+
def find_mine(all)
|
75
|
+
client.Issue.jql(my_issues_jql(all)).map { |issue| Issue.new issue }
|
76
|
+
end
|
76
77
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
78
|
+
def find_by_id(issue_id)
|
79
|
+
Issue.new client.Issue.find(issue_id)
|
80
|
+
rescue StandardError
|
81
|
+
nil
|
82
|
+
end
|
82
83
|
|
83
|
-
|
84
|
+
protected
|
84
85
|
|
85
|
-
|
86
|
-
|
87
|
-
|
86
|
+
def my_issues_jql(all_issues)
|
87
|
+
all_clause = 'AND sprint in openSprints()' unless all_issues
|
88
|
+
"project='#{project}' #{all_clause} AND assignee='#{Config.username}'"
|
89
|
+
end
|
88
90
|
end
|
89
91
|
end
|
90
92
|
end
|
@@ -1,8 +1,10 @@
|
|
1
1
|
module Jirify
|
2
|
-
|
3
|
-
class
|
4
|
-
|
5
|
-
|
2
|
+
module Models
|
3
|
+
class Project < Base
|
4
|
+
class << self
|
5
|
+
def all
|
6
|
+
client.Project.all.map { |project| Project.new project }
|
7
|
+
end
|
6
8
|
end
|
7
9
|
end
|
8
10
|
end
|
data/lib/jirify/models/sprint.rb
CHANGED
@@ -1,53 +1,24 @@
|
|
1
|
-
require 'terminal-table'
|
2
|
-
require 'json'
|
3
|
-
|
4
1
|
module Jirify
|
5
|
-
|
6
|
-
class
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
all_columns ? issue.status.name : issue.status.statusCategory['name']
|
2
|
+
module Models
|
3
|
+
class Sprint < Base
|
4
|
+
class << self
|
5
|
+
def issues_in_current_sprint(only_mine = false, max_results = 200)
|
6
|
+
issues = client.Issue.jql current_sprint_jql(only_mine), max_results: max_results
|
7
|
+
issues.map { |issue| Issue.new issue }
|
12
8
|
end
|
13
9
|
|
14
|
-
|
15
|
-
end
|
16
|
-
|
17
|
-
protected
|
18
|
-
|
19
|
-
def current_sprint_jql(only_mine)
|
20
|
-
labels = Config.options['filter_by_labels']
|
21
|
-
labels = labels.join(', ') if labels
|
10
|
+
protected
|
22
11
|
|
23
|
-
|
24
|
-
|
25
|
-
|
12
|
+
def current_sprint_jql(only_mine)
|
13
|
+
labels = Config.options['filter_by_labels']
|
14
|
+
labels = labels.join(', ') if labels
|
26
15
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
def print_grouped_issues_as_table(grouped_issues, verbose)
|
31
|
-
all_groups = grouped_issues.values
|
32
|
-
|
33
|
-
l = all_groups.map(&:length).max
|
34
|
-
transposed = all_groups.map { |e| e.values_at(0...l) }.transpose
|
16
|
+
labels_clause = "AND labels in (#{labels})" if labels
|
17
|
+
mine_clause = "AND assignee='#{Config.username}'" if only_mine
|
18
|
+
sprint_clause = 'AND sprint in openSprints()'
|
35
19
|
|
36
|
-
|
37
|
-
row.map do |issue|
|
38
|
-
if issue
|
39
|
-
key = issue.key.ljust(7)
|
40
|
-
url = "(#{Config.issue_browse_url}/#{issue.key})".blue
|
41
|
-
summary = "#{issue.summary[0, 35]}...\n" if verbose
|
42
|
-
|
43
|
-
"#{key}: #{summary}#{url}"
|
44
|
-
else
|
45
|
-
''
|
46
|
-
end
|
47
|
-
end
|
20
|
+
"project='#{project}' #{sprint_clause} #{labels_clause} #{mine_clause}"
|
48
21
|
end
|
49
|
-
|
50
|
-
puts Terminal::Table.new headings: grouped_issues.keys, rows: transposed unless transposed.empty?
|
51
22
|
end
|
52
23
|
end
|
53
24
|
end
|
data/lib/jirify/models/status.rb
CHANGED
@@ -1,48 +1,47 @@
|
|
1
1
|
module Jirify
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
2
|
+
module Models
|
3
|
+
class Status < Base
|
4
|
+
def pretty_name
|
5
|
+
justified = "(#{name})".rjust(longest_status_name + 2)
|
6
|
+
case name
|
7
|
+
when Config.statuses['blocked'] then justified.red
|
8
|
+
when Config.statuses['done'] then justified.green
|
9
|
+
when Config.statuses['in_progress'] then justified.blue
|
10
|
+
when Config.statuses['in_review'] then justified.yellow
|
11
|
+
when Config.statuses['todo'] then justified.black
|
12
|
+
else justified
|
13
|
+
end
|
12
14
|
end
|
13
|
-
end
|
14
|
-
|
15
|
-
protected
|
16
|
-
|
17
|
-
def longest_status_name
|
18
|
-
Config.statuses.values.map(&:length).max
|
19
|
-
end
|
20
15
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
16
|
+
def <=>(other)
|
17
|
+
this_index = Status.status_order.index(name)
|
18
|
+
other_index = Status.status_order.index(other.name)
|
19
|
+
return 1 if other_index.nil?
|
20
|
+
return 0 if this_index.nil?
|
25
21
|
|
26
|
-
|
27
|
-
@to_do ||= find_by_name Config.statuses['todo']
|
22
|
+
this_index - other_index
|
28
23
|
end
|
29
24
|
|
30
|
-
|
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
|
25
|
+
protected
|
37
26
|
|
38
|
-
def
|
39
|
-
|
27
|
+
def longest_status_name
|
28
|
+
Config.statuses.values.map(&:length).max
|
40
29
|
end
|
41
30
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
31
|
+
class << self
|
32
|
+
def all
|
33
|
+
client.Status.all.map { |status| Status.new status }
|
34
|
+
end
|
35
|
+
|
36
|
+
def status_order
|
37
|
+
[
|
38
|
+
Config.statuses['blocked'],
|
39
|
+
Config.statuses['todo'],
|
40
|
+
Config.statuses['in_progress'],
|
41
|
+
Config.statuses['in_review'],
|
42
|
+
Config.statuses['done']
|
43
|
+
]
|
44
|
+
end
|
46
45
|
end
|
47
46
|
end
|
48
47
|
end
|
@@ -1,28 +1,30 @@
|
|
1
1
|
module Jirify
|
2
|
-
|
3
|
-
|
2
|
+
module Models
|
3
|
+
class TransitionList < Base
|
4
|
+
attr_accessor :list
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
def names
|
10
|
-
@list.map(&:name)
|
11
|
-
end
|
6
|
+
def initialize(list)
|
7
|
+
@list = list
|
8
|
+
end
|
12
9
|
|
13
|
-
|
14
|
-
|
15
|
-
|
10
|
+
def find_by_name(name)
|
11
|
+
@list.find { |transition| transition.name == name }
|
12
|
+
end
|
16
13
|
|
17
|
-
|
14
|
+
def names
|
15
|
+
@list.map(&:name)
|
16
|
+
end
|
18
17
|
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
Config.transitions.keys.each do |transition_name|
|
19
|
+
define_method(transition_name.to_sym) do
|
20
|
+
find_by_name Config.transitions[transition_name]
|
21
|
+
end
|
22
|
+
end
|
22
23
|
|
23
|
-
|
24
|
-
|
25
|
-
|
24
|
+
class << self
|
25
|
+
def all(issue)
|
26
|
+
TransitionList.new client.Transition.all(issue: issue)
|
27
|
+
end
|
26
28
|
end
|
27
29
|
end
|
28
30
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Jirify
|
2
|
+
module UI
|
3
|
+
class WindowTooNarrow < StandardError; end
|
4
|
+
|
5
|
+
class SprintCell
|
6
|
+
attr_reader :issue, :max_cell_length
|
7
|
+
|
8
|
+
def initialize(issue, max_cell_length)
|
9
|
+
@issue = issue
|
10
|
+
@max_cell_length = max_cell_length
|
11
|
+
end
|
12
|
+
|
13
|
+
def key
|
14
|
+
issue.key
|
15
|
+
end
|
16
|
+
|
17
|
+
def summary
|
18
|
+
issue.summary
|
19
|
+
end
|
20
|
+
|
21
|
+
def url
|
22
|
+
"#{Config.issue_browse_url}#{issue.key}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_s(verbose)
|
26
|
+
raise UI::WindowTooNarrow, 'The terminal window is too narrow.' if max_cell_length <= key.size
|
27
|
+
|
28
|
+
verbose ? to_verbose_cell : to_short_cell
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
def display_url?
|
34
|
+
url.size <= max_cell_length
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_verbose_cell
|
38
|
+
key_and_summary = "#{key}: #{summary}"
|
39
|
+
|
40
|
+
if key_and_summary.size <= max_cell_length
|
41
|
+
row = key_and_summary
|
42
|
+
else
|
43
|
+
row = "#{key}\n#{summary[0...max_cell_length - 3]}..."
|
44
|
+
end
|
45
|
+
|
46
|
+
row << "\n#{url.blue}" if display_url?
|
47
|
+
row
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_short_cell
|
51
|
+
return key unless display_url?
|
52
|
+
|
53
|
+
single_row = "#{key}: #{url}"
|
54
|
+
|
55
|
+
if single_row.size <= max_cell_length
|
56
|
+
"#{key}: #{url.blue}"
|
57
|
+
else
|
58
|
+
"#{key}\n#{url.blue}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'io/console'
|
2
|
+
require 'terminal-table'
|
3
|
+
|
4
|
+
module Jirify
|
5
|
+
module UI
|
6
|
+
class SprintTable
|
7
|
+
attr_reader :issues
|
8
|
+
def initialize(issues)
|
9
|
+
@issues = issues
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_table(all_columns, verbose)
|
13
|
+
grouped_issues = issues.group_by do |issue|
|
14
|
+
all_columns ? issue.status.name : issue.status.statusCategory['name']
|
15
|
+
end
|
16
|
+
|
17
|
+
grouped_issues_as_table(grouped_issues, verbose)
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
|
22
|
+
def terminal_width
|
23
|
+
IO.console.winsize[1]
|
24
|
+
end
|
25
|
+
|
26
|
+
def table_style
|
27
|
+
{ width: terminal_width, border_x: '-', border_i: 'x' }
|
28
|
+
end
|
29
|
+
|
30
|
+
def headings(grouped_issues)
|
31
|
+
grouped_issues.keys.sort_by do |name|
|
32
|
+
status_index = Models::Status.status_order.index(name)
|
33
|
+
if status_index.nil?
|
34
|
+
grouped_issues.keys.length
|
35
|
+
else
|
36
|
+
status_index
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def grouped_issues_as_table(grouped_issues, verbose)
|
42
|
+
transposed = transpose(grouped_issues.values, verbose)
|
43
|
+
return nil if transposed.empty?
|
44
|
+
|
45
|
+
Terminal::Table.new(
|
46
|
+
headings: headings(grouped_issues),
|
47
|
+
rows: transposed,
|
48
|
+
style: table_style
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
def transpose(grouped_issues, verbose)
|
53
|
+
col_padding_per_row = grouped_issues.size * 4
|
54
|
+
max_cell_length = (terminal_width - col_padding_per_row) / grouped_issues.size
|
55
|
+
|
56
|
+
# workaround - not all groups have the same size
|
57
|
+
l = grouped_issues.map(&:length).max
|
58
|
+
grouped_as_array = grouped_issues.map { |e| e.values_at(0...l) }
|
59
|
+
|
60
|
+
grouped_as_array.sort_by! do |row|
|
61
|
+
issue = row.find { |i| !i.nil? }
|
62
|
+
issue.status
|
63
|
+
end
|
64
|
+
|
65
|
+
transposed = grouped_as_array.transpose
|
66
|
+
|
67
|
+
transposed.map! do |row|
|
68
|
+
row.map do |issue|
|
69
|
+
next if issue.nil?
|
70
|
+
SprintCell.new(issue, max_cell_length).to_s(verbose)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
transposed = transposed.zip([:separator] * transposed.size).flatten(1)
|
75
|
+
transposed.pop
|
76
|
+
transposed
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/lib/jirify/version.rb
CHANGED
data/lib/jirify.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
require 'thor'
|
2
1
|
require 'jira-ruby'
|
2
|
+
require 'thor'
|
3
3
|
require 'colorize'
|
4
4
|
|
5
5
|
require 'jirify/version'
|
@@ -13,6 +13,9 @@ require 'jirify/models/issue'
|
|
13
13
|
require 'jirify/models/sprint'
|
14
14
|
require 'jirify/models/project'
|
15
15
|
|
16
|
+
require 'jirify/ui/sprint_cell'
|
17
|
+
require 'jirify/ui/sprint_table'
|
18
|
+
|
16
19
|
require 'jirify/cli/setup'
|
17
20
|
require 'jirify/cli/sprint'
|
18
21
|
require 'jirify/cli/issue'
|
@@ -24,7 +27,7 @@ module Jirify
|
|
24
27
|
|
25
28
|
desc 'version', 'Prints Jirify version'
|
26
29
|
def version
|
27
|
-
|
30
|
+
say "Current Jirify version: #{VERSION}"
|
28
31
|
end
|
29
32
|
|
30
33
|
desc 'setup [SUBCOMMAND]', 'Jirify setup tools'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
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.7
|
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-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colorize
|
@@ -17,6 +17,9 @@ dependencies:
|
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '0.8'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 0.8.1
|
20
23
|
type: :runtime
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -24,6 +27,9 @@ dependencies:
|
|
24
27
|
- - "~>"
|
25
28
|
- !ruby/object:Gem::Version
|
26
29
|
version: '0.8'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 0.8.1
|
27
33
|
- !ruby/object:Gem::Dependency
|
28
34
|
name: jira-ruby
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -45,6 +51,9 @@ dependencies:
|
|
45
51
|
- - "~>"
|
46
52
|
- !ruby/object:Gem::Version
|
47
53
|
version: '2.4'
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: 2.4.3
|
48
57
|
type: :runtime
|
49
58
|
prerelease: false
|
50
59
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -52,6 +61,9 @@ dependencies:
|
|
52
61
|
- - "~>"
|
53
62
|
- !ruby/object:Gem::Version
|
54
63
|
version: '2.4'
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: 2.4.3
|
55
67
|
- !ruby/object:Gem::Dependency
|
56
68
|
name: terminal-table
|
57
69
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,6 +92,168 @@ dependencies:
|
|
80
92
|
- - "~>"
|
81
93
|
- !ruby/object:Gem::Version
|
82
94
|
version: '0.20'
|
95
|
+
- !ruby/object:Gem::Dependency
|
96
|
+
name: coveralls
|
97
|
+
requirement: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - "~>"
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0.7'
|
102
|
+
- - ">="
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: 0.7.1
|
105
|
+
type: :development
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - "~>"
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0.7'
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: 0.7.1
|
115
|
+
- !ruby/object:Gem::Dependency
|
116
|
+
name: guard
|
117
|
+
requirement: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- - "~>"
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '2.14'
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 2.14.2
|
125
|
+
type: :development
|
126
|
+
prerelease: false
|
127
|
+
version_requirements: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '2.14'
|
132
|
+
- - ">="
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
version: 2.14.2
|
135
|
+
- !ruby/object:Gem::Dependency
|
136
|
+
name: guard-rspec
|
137
|
+
requirement: !ruby/object:Gem::Requirement
|
138
|
+
requirements:
|
139
|
+
- - "~>"
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '4.7'
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: 4.7.3
|
145
|
+
type: :development
|
146
|
+
prerelease: false
|
147
|
+
version_requirements: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - "~>"
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '4.7'
|
152
|
+
- - ">="
|
153
|
+
- !ruby/object:Gem::Version
|
154
|
+
version: 4.7.3
|
155
|
+
- !ruby/object:Gem::Dependency
|
156
|
+
name: rake
|
157
|
+
requirement: !ruby/object:Gem::Requirement
|
158
|
+
requirements:
|
159
|
+
- - "~>"
|
160
|
+
- !ruby/object:Gem::Version
|
161
|
+
version: '12.1'
|
162
|
+
type: :development
|
163
|
+
prerelease: false
|
164
|
+
version_requirements: !ruby/object:Gem::Requirement
|
165
|
+
requirements:
|
166
|
+
- - "~>"
|
167
|
+
- !ruby/object:Gem::Version
|
168
|
+
version: '12.1'
|
169
|
+
- !ruby/object:Gem::Dependency
|
170
|
+
name: rake-release
|
171
|
+
requirement: !ruby/object:Gem::Requirement
|
172
|
+
requirements:
|
173
|
+
- - "~>"
|
174
|
+
- !ruby/object:Gem::Version
|
175
|
+
version: '1.0'
|
176
|
+
- - ">="
|
177
|
+
- !ruby/object:Gem::Version
|
178
|
+
version: 1.0.1
|
179
|
+
type: :development
|
180
|
+
prerelease: false
|
181
|
+
version_requirements: !ruby/object:Gem::Requirement
|
182
|
+
requirements:
|
183
|
+
- - "~>"
|
184
|
+
- !ruby/object:Gem::Version
|
185
|
+
version: '1.0'
|
186
|
+
- - ">="
|
187
|
+
- !ruby/object:Gem::Version
|
188
|
+
version: 1.0.1
|
189
|
+
- !ruby/object:Gem::Dependency
|
190
|
+
name: rspec
|
191
|
+
requirement: !ruby/object:Gem::Requirement
|
192
|
+
requirements:
|
193
|
+
- - "~>"
|
194
|
+
- !ruby/object:Gem::Version
|
195
|
+
version: '3.7'
|
196
|
+
type: :development
|
197
|
+
prerelease: false
|
198
|
+
version_requirements: !ruby/object:Gem::Requirement
|
199
|
+
requirements:
|
200
|
+
- - "~>"
|
201
|
+
- !ruby/object:Gem::Version
|
202
|
+
version: '3.7'
|
203
|
+
- !ruby/object:Gem::Dependency
|
204
|
+
name: rubocop
|
205
|
+
requirement: !ruby/object:Gem::Requirement
|
206
|
+
requirements:
|
207
|
+
- - "~>"
|
208
|
+
- !ruby/object:Gem::Version
|
209
|
+
version: '0.57'
|
210
|
+
- - ">="
|
211
|
+
- !ruby/object:Gem::Version
|
212
|
+
version: 0.57.2
|
213
|
+
type: :development
|
214
|
+
prerelease: false
|
215
|
+
version_requirements: !ruby/object:Gem::Requirement
|
216
|
+
requirements:
|
217
|
+
- - "~>"
|
218
|
+
- !ruby/object:Gem::Version
|
219
|
+
version: '0.57'
|
220
|
+
- - ">="
|
221
|
+
- !ruby/object:Gem::Version
|
222
|
+
version: 0.57.2
|
223
|
+
- !ruby/object:Gem::Dependency
|
224
|
+
name: rubocop-rspec
|
225
|
+
requirement: !ruby/object:Gem::Requirement
|
226
|
+
requirements:
|
227
|
+
- - "~>"
|
228
|
+
- !ruby/object:Gem::Version
|
229
|
+
version: '1.27'
|
230
|
+
type: :development
|
231
|
+
prerelease: false
|
232
|
+
version_requirements: !ruby/object:Gem::Requirement
|
233
|
+
requirements:
|
234
|
+
- - "~>"
|
235
|
+
- !ruby/object:Gem::Version
|
236
|
+
version: '1.27'
|
237
|
+
- !ruby/object:Gem::Dependency
|
238
|
+
name: simplecov
|
239
|
+
requirement: !ruby/object:Gem::Requirement
|
240
|
+
requirements:
|
241
|
+
- - "~>"
|
242
|
+
- !ruby/object:Gem::Version
|
243
|
+
version: '0.10'
|
244
|
+
- - ">="
|
245
|
+
- !ruby/object:Gem::Version
|
246
|
+
version: 0.10.2
|
247
|
+
type: :development
|
248
|
+
prerelease: false
|
249
|
+
version_requirements: !ruby/object:Gem::Requirement
|
250
|
+
requirements:
|
251
|
+
- - "~>"
|
252
|
+
- !ruby/object:Gem::Version
|
253
|
+
version: '0.10'
|
254
|
+
- - ">="
|
255
|
+
- !ruby/object:Gem::Version
|
256
|
+
version: 0.10.2
|
83
257
|
description: JIRA for your terminal
|
84
258
|
email: georgi@gardev.com
|
85
259
|
executables:
|
@@ -103,12 +277,15 @@ files:
|
|
103
277
|
- lib/jirify/models/status.rb
|
104
278
|
- lib/jirify/models/transition_list.rb
|
105
279
|
- lib/jirify/monkey_patches/jira_issue.rb
|
280
|
+
- lib/jirify/ui/sprint_cell.rb
|
281
|
+
- lib/jirify/ui/sprint_table.rb
|
106
282
|
- lib/jirify/version.rb
|
107
283
|
homepage: https://github.com/GeorgeSG/jirify
|
108
284
|
licenses:
|
109
285
|
- MIT
|
110
|
-
metadata:
|
111
|
-
|
286
|
+
metadata:
|
287
|
+
source_code_uri: https://github.com/GeorgeSG/jirify
|
288
|
+
post_install_message: Thanks for installing! Run <jira setup> for initial configuration.
|
112
289
|
rdoc_options: []
|
113
290
|
require_paths:
|
114
291
|
- lib
|