jirify 0.1.6 → 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/jirify.svg)](https://badge.fury.io/rb/jirify)
|
3
|
+
[![Build Status](https://travis-ci.org/GeorgeSG/jirify.svg?branch=master)](https://travis-ci.org/GeorgeSG/jirify)
|
4
|
+
[![Coverage Status](https://coveralls.io/repos/github/GeorgeSG/jirify/badge.svg?branch=master)](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
|