terjira 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +10 -1
- data/Gemfile.lock +3 -3
- data/README.md +14 -15
- data/bin/console +1 -1
- data/bin/jira +11 -3
- data/bin/terjira +22 -0
- data/lib/terjira/base_cli.rb +5 -3
- data/lib/terjira/board_cli.rb +17 -2
- data/lib/terjira/client/agile.rb +10 -13
- data/lib/terjira/client/auth_option_builder.rb +1 -1
- data/lib/terjira/client/base.rb +9 -12
- data/lib/terjira/client/board.rb +13 -3
- data/lib/terjira/client/field.rb +12 -2
- data/lib/terjira/client/issue.rb +15 -13
- data/lib/terjira/client/issuetype.rb +18 -0
- data/lib/terjira/client/jql_builder.rb +25 -0
- data/lib/terjira/client/priority.rb +1 -1
- data/lib/terjira/client/resolution.rb +1 -1
- data/lib/terjira/client/status.rb +1 -1
- data/lib/terjira/client/user.rb +5 -6
- data/lib/terjira/ext/jira_ruby.rb +12 -4
- data/lib/terjira/ext/tty_prompt.rb +0 -3
- data/lib/terjira/issue_cli.rb +11 -7
- data/lib/terjira/option_support/option_selector.rb +20 -9
- data/lib/terjira/option_support/shared_options.rb +4 -2
- data/lib/terjira/option_supportable.rb +24 -0
- data/lib/terjira/presenters/common_presenter.rb +13 -4
- data/lib/terjira/presenters/issue_presenter.rb +115 -97
- data/lib/terjira/presenters/project_presenter.rb +1 -1
- data/lib/terjira/presenters/sprint_presenter.rb +9 -9
- data/lib/terjira/sprint_cli.rb +1 -0
- data/lib/terjira/utils/file_cache.rb +26 -26
- data/lib/terjira/version.rb +1 -1
- data/lib/terjira.rb +11 -0
- data/terjira.gemspec +4 -4
- metadata +11 -11
- data/lib/terjira/client/jql_query_builer.rb +0 -25
data/lib/terjira/issue_cli.rb
CHANGED
@@ -16,7 +16,12 @@ module Terjira
|
|
16
16
|
def show(issue_key = nil)
|
17
17
|
return invoke(:help) unless issue_key
|
18
18
|
issue = client_class.find(issue_key)
|
19
|
-
|
19
|
+
if issue.issuetype.name.casecmp('epic').zero?
|
20
|
+
epic_issues = client_class.all_epic_issues(issue)
|
21
|
+
render_issue_detail(issue, epic_issues)
|
22
|
+
else
|
23
|
+
render_issue_detail(issue)
|
24
|
+
end
|
20
25
|
end
|
21
26
|
|
22
27
|
desc '( ls | list )', 'List of issues'
|
@@ -56,14 +61,11 @@ module Terjira
|
|
56
61
|
|
57
62
|
desc 'new', 'Create issue'
|
58
63
|
jira_options :summary, :description, :project, :issuetype,
|
59
|
-
:priority, :assignee
|
64
|
+
:priority, :assignee, :parent, :epiclink
|
60
65
|
def new
|
61
66
|
opts = suggest_options(required: [:project, :summary, :issuetype])
|
62
67
|
|
63
|
-
|
64
|
-
epic_name_field = Client::Field.epic_name
|
65
|
-
opts[epic_name_field.key] = write_epic_name
|
66
|
-
end
|
68
|
+
suggest_related_value_options(opts)
|
67
69
|
|
68
70
|
issue = client_class.create(opts)
|
69
71
|
render_issue_detail(issue)
|
@@ -71,11 +73,13 @@ module Terjira
|
|
71
73
|
|
72
74
|
desc 'edit', 'Edit issue'
|
73
75
|
jira_options :summary, :description, :project, :issuetype,
|
74
|
-
:priority, :assignee
|
76
|
+
:priority, :assignee, :epiclink
|
75
77
|
def edit(issue)
|
76
78
|
return if options.blank?
|
77
79
|
issue = client_class.find(issue)
|
78
80
|
opts = suggest_options(resources: { issue: issue })
|
81
|
+
suggest_related_value_options(opts)
|
82
|
+
|
79
83
|
issue = client_class.update(issue, opts)
|
80
84
|
render_issue_detail(issue)
|
81
85
|
end
|
@@ -61,7 +61,8 @@ module Terjira
|
|
61
61
|
|
62
62
|
def select_issuetype
|
63
63
|
fetch(:issuetype) do
|
64
|
-
project =
|
64
|
+
project = get(:issue).try(:project).try(:key)
|
65
|
+
project ||= select_project
|
65
66
|
if project.is_a? String
|
66
67
|
project = Client::Project.find(project)
|
67
68
|
set(:project, project)
|
@@ -79,14 +80,14 @@ module Terjira
|
|
79
80
|
fetch(:status) do
|
80
81
|
statuses = fetch(:statuses) do
|
81
82
|
project = if issue = get(:issue)
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
83
|
+
if issue.respond_to?(:project)
|
84
|
+
issue.project
|
85
|
+
else
|
86
|
+
set(:issue, Client::Issue.find(issue)).project
|
87
|
+
end
|
88
|
+
else
|
89
|
+
select_project
|
90
|
+
end
|
90
91
|
Client::Status.all(project)
|
91
92
|
end
|
92
93
|
|
@@ -120,6 +121,12 @@ module Terjira
|
|
120
121
|
end
|
121
122
|
end
|
122
123
|
|
124
|
+
def write_epiclink_key
|
125
|
+
fetch(:epiclink) do
|
126
|
+
option_prompt.ask('Epic key?')
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
123
130
|
def write_epic_name
|
124
131
|
option_prompt.ask('Epic name?')
|
125
132
|
end
|
@@ -142,6 +149,10 @@ module Terjira
|
|
142
149
|
fetch(:summary) { option_prompt.ask('Summary?') }
|
143
150
|
end
|
144
151
|
|
152
|
+
def write_parent_issue_key
|
153
|
+
fetch(:parent) { option_prompt.ask('Parent issue key?') }
|
154
|
+
end
|
155
|
+
|
145
156
|
private
|
146
157
|
|
147
158
|
def sprint_choice_title(sprint)
|
@@ -4,7 +4,6 @@ module Terjira
|
|
4
4
|
'project' => {
|
5
5
|
type: :string,
|
6
6
|
aliases: '-p',
|
7
|
-
desc: 'project key'
|
8
7
|
},
|
9
8
|
'board' => {
|
10
9
|
type: :numeric,
|
@@ -31,7 +30,6 @@ module Terjira
|
|
31
30
|
'status' => {
|
32
31
|
type: :string,
|
33
32
|
aliases: '-s',
|
34
|
-
desc: 'status'
|
35
33
|
},
|
36
34
|
'resolution' => {
|
37
35
|
type: :string,
|
@@ -56,6 +54,10 @@ module Terjira
|
|
56
54
|
'comment' => {
|
57
55
|
type: :string,
|
58
56
|
aliases: '-m'
|
57
|
+
},
|
58
|
+
'epiclink' => {
|
59
|
+
type: :string,
|
60
|
+
aliases: '-e'
|
59
61
|
}
|
60
62
|
}.freeze
|
61
63
|
|
@@ -23,6 +23,7 @@ module Terjira
|
|
23
23
|
status: :select_issue_status,
|
24
24
|
priority: :select_priority,
|
25
25
|
resolution: :select_resolution,
|
26
|
+
epiclink: :write_epiclink_key,
|
26
27
|
comment: :write_comment
|
27
28
|
}.freeze
|
28
29
|
|
@@ -73,6 +74,29 @@ module Terjira
|
|
73
74
|
origin.merge! default_value_options
|
74
75
|
end
|
75
76
|
|
77
|
+
def suggest_related_value_options(opts = {})
|
78
|
+
if opts[:issuetype]
|
79
|
+
if opts[:issuetype].key_value.casecmp('epic').zero?
|
80
|
+
# Suggest epic name
|
81
|
+
epic_name_field = Client::Field.epic_name
|
82
|
+
opts[epic_name_field.key] ||= write_epic_name
|
83
|
+
else
|
84
|
+
subtask_issuetypes = Client::Issuetype.subtask_issuetypes.map(&:name)
|
85
|
+
if subtask_issuetypes.include? opts[:issuetype].key_value
|
86
|
+
# Suggest parent issue
|
87
|
+
opts[:parent] ||= write_parent_issue_key
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
if opts[:epiclink]
|
93
|
+
epiclink_field = Client::Field.epiclink
|
94
|
+
opts[epiclink_field.key] ||= opts.delete(:epiclink)
|
95
|
+
end
|
96
|
+
|
97
|
+
opts
|
98
|
+
end
|
99
|
+
|
76
100
|
def resource_store
|
77
101
|
ResourceStore.instance
|
78
102
|
end
|
@@ -5,6 +5,10 @@ require 'pastel'
|
|
5
5
|
|
6
6
|
module Terjira
|
7
7
|
module CommonPresenter
|
8
|
+
extend Forwardable
|
9
|
+
|
10
|
+
def_delegators :pastel, :bold, :dim
|
11
|
+
|
8
12
|
def render(text)
|
9
13
|
if text.is_a? Array
|
10
14
|
puts text.join("\n")
|
@@ -17,14 +21,18 @@ module Terjira
|
|
17
21
|
@pastel ||= Pastel.new
|
18
22
|
end
|
19
23
|
|
20
|
-
def
|
24
|
+
def dim_none
|
25
|
+
dim('None')
|
26
|
+
end
|
27
|
+
|
28
|
+
def formatted_date(date_str, date_format = '%c')
|
21
29
|
return nil if date_str.nil? || date_str.empty?
|
22
|
-
Time.parse(date_str).strftime(
|
30
|
+
Time.parse(date_str).strftime(date_format)
|
23
31
|
end
|
24
32
|
|
25
33
|
def username_with_email(user)
|
26
34
|
if user.nil?
|
27
|
-
|
35
|
+
dim_none
|
28
36
|
else
|
29
37
|
title = "#{user.name}, #{user.displayName}"
|
30
38
|
title += " <#{user.emailAddress}>" if user.respond_to?(:emailAddress)
|
@@ -40,12 +48,13 @@ module Terjira
|
|
40
48
|
# when string display length is longger than length argument
|
41
49
|
def insert_new_line(str, length)
|
42
50
|
str.split(/\r\n|\n/).map do |line|
|
51
|
+
line.strip!
|
43
52
|
if line.display_width < 1
|
44
53
|
line
|
45
54
|
else
|
46
55
|
display_length = pastel.strip(line).display_width
|
47
56
|
split_length = (line.length * length / display_length).to_i
|
48
|
-
line.scan(/.{1,#{split_length}}/).join("\n")
|
57
|
+
line.scan(/.{1,#{split_length}}/).join("\n") rescue line
|
49
58
|
end
|
50
59
|
end.join("\n")
|
51
60
|
end
|
@@ -3,9 +3,12 @@
|
|
3
3
|
require 'tty-prompt'
|
4
4
|
require 'tty-table'
|
5
5
|
require 'pastel'
|
6
|
+
require 'erb'
|
6
7
|
|
7
8
|
module Terjira
|
8
9
|
module IssuePresenter
|
10
|
+
COMMENTS_SIZE = 3
|
11
|
+
|
9
12
|
def render_issues(issues, opts = {})
|
10
13
|
return render('Empty') if issues.blank?
|
11
14
|
|
@@ -34,63 +37,21 @@ module Terjira
|
|
34
37
|
end
|
35
38
|
end
|
36
39
|
|
37
|
-
def render_issue_detail(issue)
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
rows << ''
|
44
|
-
rows << issue_sutats_bar(issue)
|
45
|
-
rows << ''
|
46
|
-
|
47
|
-
rows << [pastel.bold('Assignee'), username_with_email(issue.assignee)].join(' ')
|
48
|
-
rows << [pastel.bold('Reporter'), username_with_email(issue.reporter)].join(' ')
|
49
|
-
rows << ''
|
50
|
-
rows << pastel.bold('Description')
|
51
|
-
rows << (issue.description.blank? ? 'None' : issue.description)
|
52
|
-
|
53
|
-
if issue.respond_to?(:environment) && issue.environment.present?
|
54
|
-
rows << pastel.bold('Environment')
|
55
|
-
rows << issue.environment
|
56
|
-
end
|
57
|
-
|
58
|
-
if issue.comments.present?
|
59
|
-
rows << ''
|
60
|
-
rows << pastel.bold('Comments')
|
61
|
-
remain_comments = issue.comments
|
62
|
-
comments = remain_comments.pop(4)
|
63
|
-
|
64
|
-
if comments.size.zero?
|
65
|
-
rows << 'None'
|
66
|
-
elsif remain_comments.empty?
|
67
|
-
rows << pastel.dim("- #{remain_comments.size} previous comments exist -")
|
68
|
-
end
|
69
|
-
|
70
|
-
comments.each do |comment|
|
71
|
-
comment_title = pastel.bold(comment.author['displayName'])
|
72
|
-
comment_title += " #{formatted_date(comment.created)}"
|
73
|
-
rows << comment_title
|
74
|
-
rows << comment.body
|
75
|
-
rows << ''
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
rows = rows.map { |row| insert_new_line(row, screen_width - 10) }
|
80
|
-
|
81
|
-
table = TTY::Table.new header, rows.map { |r| [r] }
|
82
|
-
result = table.render(:unicode, padding: [0, 1, 0, 1], multiline: true)
|
83
|
-
|
84
|
-
render(result)
|
40
|
+
def render_issue_detail(issue, epic_issues = [])
|
41
|
+
result = ERB.new(issue_detail_template, nil, '-').result(binding)
|
42
|
+
result += ERB.new(comments_template, nil, '-').result(binding)
|
43
|
+
rows = insert_new_line(result, screen_width - 10)
|
44
|
+
table = TTY::Table.new nil, rows.split("\n").map { |r| [r] }
|
45
|
+
render table.render(:unicode, padding: [0, 1, 0, 1], multiline: true)
|
85
46
|
end
|
86
47
|
|
87
48
|
def summarise_issue(issue)
|
88
49
|
first_line = [colorize_issue_stastus(issue.status),
|
89
|
-
issue.summary.tr("\t", ' ')].join
|
50
|
+
issue.summary.tr("\t", ' ')].join(' ')
|
90
51
|
|
91
52
|
second_line = [colorize_priority(issue.priority),
|
92
53
|
colorize_issue_type(issue.issuetype),
|
93
|
-
|
54
|
+
issue.assignee.try(:name)].join(' ')
|
94
55
|
|
95
56
|
lines = [first_line, second_line].map do |line|
|
96
57
|
insert_new_line(line, screen_width - 30)
|
@@ -98,67 +59,124 @@ module Terjira
|
|
98
59
|
lines.join("\n")
|
99
60
|
end
|
100
61
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
62
|
+
def issue_detail_template
|
63
|
+
"""<%= bold(issue.key) + ' in ' + issue.project.name %>
|
64
|
+
|
65
|
+
<%= pastel.underline.bold(issue.summary) %>
|
66
|
+
|
67
|
+
<%= bold('Type') %>: <%= colorize_issue_type(issue.issuetype) %>\s\s\s<%= bold('Status') %>: <%= colorize_issue_stastus(issue.status) %>\s\s\s<%= bold('priority') %>: <%= colorize_priority(issue.priority, title: true) %>
|
68
|
+
<% if issue.parent.nil? -%>
|
69
|
+
<%= bold('Epic Link') %>: <%= issue.try(:epic).try(:key) %> <%= issue.try(:epic).try(:name) || dim_none %>
|
70
|
+
<% end -%>
|
71
|
+
<% if issue.try(:parent) && issue.epic.nil? -%>
|
72
|
+
<%= bold('Parent') %>: <%= issue.parent.key %>
|
73
|
+
<% end %>
|
74
|
+
<% if issue.try(:sprint) -%>
|
75
|
+
<%= bold('Sprint') %>: <%= colorize_sprint_state(issue.try(:sprint).try(:state)) %> <%= issue.try(:sprint).try(:id) %>. <%= issue.try(:sprint).try(:name) %>
|
76
|
+
<% end -%>
|
77
|
+
|
78
|
+
<%= bold('Assignee') %>: <%= username_with_email(issue.assignee) %>
|
79
|
+
<%= bold('Reporter') %>: <%= username_with_email(issue.reporter) %>
|
80
|
+
|
81
|
+
<%= bold('Description') %>
|
82
|
+
<%= issue.description || dim_none %>
|
83
|
+
<% if issue.try(:timetracking).is_a? Hash -%>
|
84
|
+
|
85
|
+
<%= bold('Estimate') %>
|
86
|
+
<% if issue.timetracking['originalEstimate'] -%>
|
87
|
+
<%= issue.timetracking['originalEstimate'] %> / <%= issue.timetracking['remainingEstimate'] %>
|
88
|
+
<% else -%>
|
89
|
+
<%= dim_none %>
|
90
|
+
<% end -%>
|
91
|
+
<% end -%>
|
92
|
+
<% if issue.try(:environment) -%>
|
93
|
+
|
94
|
+
<%= bold('Environment') %>
|
95
|
+
<%= issue.environment %>
|
96
|
+
<% end -%>
|
97
|
+
<% if issue.subtasks.size > 0 -%>
|
98
|
+
|
99
|
+
<%= bold('SubTasks') %>
|
100
|
+
<% issue.subtasks.each do |subtask| -%>
|
101
|
+
* <%= bold(subtask.key) %> <%= colorize_issue_stastus(subtask.status) %> <%= subtask.summary %>
|
102
|
+
<% end -%>
|
103
|
+
<% end -%>
|
104
|
+
<% if epic_issues.present? -%>
|
105
|
+
<%= bold('Issues in Epic') %>
|
106
|
+
<% epic_issues.each do |epic_issue| -%>
|
107
|
+
* <%= bold(epic_issue.key) %> <%= colorize_issue_stastus(epic_issue.status) %> <%= epic_issue.summary %> <%= issue.assignee.try(:name) %>
|
108
|
+
<% end -%>
|
109
|
+
<% end -%>
|
110
|
+
"""
|
107
111
|
end
|
108
112
|
|
109
|
-
def
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
113
|
+
def comments_template
|
114
|
+
"""
|
115
|
+
<% remain_comments = issue.comments -%>
|
116
|
+
<% visiable_comments = remain_comments.pop(COMMENTS_SIZE) -%>
|
117
|
+
<%= bold('Comments') %>
|
118
|
+
<% if visiable_comments.empty? -%>
|
119
|
+
<%= dim_none %>
|
120
|
+
<% elsif remain_comments.size != 0 -%>
|
121
|
+
<%= pastel.dim('- ' + remain_comments.size.to_s + ' previous comments exist -') %>
|
122
|
+
<% end -%>
|
123
|
+
<% visiable_comments.each do |comment| -%>
|
124
|
+
<%= pastel.bold(comment.author['displayName']) %> <%= formatted_date(comment.created) %>
|
125
|
+
<%= comment.body %>
|
126
|
+
|
127
|
+
<% end -%>
|
128
|
+
"""
|
114
129
|
end
|
115
130
|
|
131
|
+
private
|
132
|
+
|
116
133
|
def colorize_issue_type(issue_type)
|
117
134
|
title = " #{issue_type.name} "
|
118
|
-
if title =~ /bug/i
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
135
|
+
background = if title =~ /bug/i
|
136
|
+
:on_red
|
137
|
+
elsif title =~ /task/i
|
138
|
+
:on_blue
|
139
|
+
elsif title =~ /story/i
|
140
|
+
:on_green
|
141
|
+
elsif title =~ /epic/i
|
142
|
+
:on_magenta
|
143
|
+
else
|
144
|
+
:on_cyan
|
145
|
+
end
|
146
|
+
pastel.decorate(title, :white, background, :bold)
|
129
147
|
end
|
130
148
|
|
131
149
|
def colorize_issue_stastus(status)
|
132
|
-
|
133
|
-
category
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
150
|
+
category = status.statusCategory['name'] rescue nil
|
151
|
+
category ||= status.name
|
152
|
+
title = "#{status.name}"
|
153
|
+
|
154
|
+
color = if category =~ /to\sdo|open/i
|
155
|
+
:blue
|
156
|
+
elsif category =~ /in\sprogress/i
|
157
|
+
:yellow
|
158
|
+
elsif category =~ /done|close/i
|
159
|
+
:green
|
160
|
+
else
|
161
|
+
:magenta
|
162
|
+
end
|
163
|
+
pastel.decorate(title, color, :bold)
|
146
164
|
end
|
147
165
|
|
148
166
|
def colorize_priority(priority, opts = {})
|
149
167
|
return '' unless priority.respond_to? :name
|
150
168
|
name = priority.name
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
title = opts[:title] ? "#{
|
161
|
-
pastel.
|
169
|
+
infos = if name =~ /high|major|critic/i
|
170
|
+
[:red, '⬆']
|
171
|
+
elsif name =~ /medium|default/i
|
172
|
+
[:yellow, '⬆']
|
173
|
+
elsif name =~ /minor|low|trivial/i
|
174
|
+
[:green, '⬇']
|
175
|
+
else
|
176
|
+
[:green, '•']
|
177
|
+
end
|
178
|
+
title = opts[:title] ? "#{infos[1]} #{name}" : infos[1]
|
179
|
+
pastel.decorate(title, infos[0])
|
162
180
|
end
|
163
181
|
|
164
182
|
def extract_status_names(issues)
|
@@ -3,19 +3,19 @@
|
|
3
3
|
module Terjira
|
4
4
|
module SprintPresenter
|
5
5
|
def render_sprint_detail(sprint)
|
6
|
-
return render(
|
6
|
+
return render('Empty') if sprint.nil?
|
7
7
|
attrs = sprint.attrs
|
8
8
|
summary = [
|
9
9
|
pastel.bold("#{sprint.id}. #{sprint.name} #{colorize_sprint_state(sprint.state)}"),
|
10
|
-
|
11
|
-
"#{formatted_date(attrs[
|
10
|
+
attrs['goal'].to_s,
|
11
|
+
"#{formatted_date(attrs['startDate'])} ~ #{formatted_date(attrs['endDate'])}"
|
12
12
|
]
|
13
13
|
|
14
14
|
render(summary.reject(&:empty?).join("\n"))
|
15
15
|
end
|
16
16
|
|
17
17
|
def render_sprints_summary(sprints)
|
18
|
-
headers =
|
18
|
+
headers = %w(ID Summary).map { |h| pastel.bold(h) }
|
19
19
|
rows = []
|
20
20
|
sort_sprint_by_state(sprints).each do |sprint|
|
21
21
|
rows << [pastel.bold(sprint.id), summarise_sprint(sprint)]
|
@@ -30,10 +30,10 @@ module Terjira
|
|
30
30
|
|
31
31
|
def summarise_sprint(sprint)
|
32
32
|
summary = colorize_sprint_state(sprint.state)
|
33
|
-
summary +=
|
33
|
+
summary += ' ' + pastel.bold(sprint.name)
|
34
34
|
if sprint.respond_to? :startDate
|
35
35
|
summary += "\n"
|
36
|
-
summary += formatted_date(sprint.startDate) +
|
36
|
+
summary += formatted_date(sprint.startDate) + ' ~ '
|
37
37
|
summary += formatted_date(sprint.endDate) if sprint.respond_to? :endDate
|
38
38
|
end
|
39
39
|
summary += "\n#{sprint.goal}" if sprint.respond_to? :goal
|
@@ -41,10 +41,10 @@ module Terjira
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def colorize_sprint_state(state)
|
44
|
-
state = state.capitalize
|
45
|
-
if
|
44
|
+
state = state.to_s.capitalize
|
45
|
+
if state =~ /active/i
|
46
46
|
pastel.on_blue.bold(state)
|
47
|
-
elsif
|
47
|
+
elsif state =~ /close/i
|
48
48
|
pastel.dim(state)
|
49
49
|
else
|
50
50
|
pastel.on_magenta.bold(state)
|
data/lib/terjira/sprint_cli.rb
CHANGED
@@ -3,21 +3,28 @@ require 'fileutils'
|
|
3
3
|
|
4
4
|
module Terjira
|
5
5
|
class FileCache
|
6
|
-
|
7
6
|
MAX_DEPTH = 32
|
8
|
-
ROOT_DIR = ENV[
|
7
|
+
ROOT_DIR = ENV['HOME'] ? "#{ENV['HOME']}/.terjira/" : '~/.terjira/'
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def clear_all
|
11
|
+
return unless File.exist?(ROOT_DIR)
|
12
|
+
FileUtils.rm_r(ROOT_DIR)
|
13
|
+
FileUtils.mkdir_p(ROOT_DIR)
|
14
|
+
end
|
15
|
+
end
|
9
16
|
|
10
17
|
def initialize(domain, expiry = 0, depth = 2)
|
11
18
|
@domain = domain
|
12
19
|
@expiry = expiry
|
13
20
|
@depth = depth > MAX_DEPTH ? MAX_DEPTH : depth
|
14
|
-
FileUtils.mkdir_p(
|
21
|
+
FileUtils.mkdir_p(root_path)
|
15
22
|
end
|
16
23
|
|
17
24
|
# Set a cache value for the given key. If the cache contains an existing value for
|
18
25
|
# the key it will be overwritten.
|
19
26
|
def set(key, value)
|
20
|
-
f = File.open(get_path(key),
|
27
|
+
f = File.open(get_path(key), 'w')
|
21
28
|
Marshal.dump(value, f)
|
22
29
|
f.close
|
23
30
|
end
|
@@ -31,14 +38,12 @@ module Terjira
|
|
31
38
|
FileUtils.rm(path)
|
32
39
|
end
|
33
40
|
|
34
|
-
|
35
|
-
|
41
|
+
return nil unless File.exist?(path)
|
42
|
+
result = nil
|
43
|
+
File.open(path, 'r') do |f|
|
36
44
|
result = Marshal.load(f)
|
37
|
-
f.close
|
38
|
-
return result
|
39
|
-
else
|
40
|
-
return nil
|
41
45
|
end
|
46
|
+
result
|
42
47
|
end
|
43
48
|
|
44
49
|
# Return the value for the specified key from the cache if the key exists in the
|
@@ -54,21 +59,20 @@ module Terjira
|
|
54
59
|
|
55
60
|
# Delete the value for the given key from the cache
|
56
61
|
def delete(key)
|
57
|
-
FileUtils.rm(get_path(key))
|
62
|
+
FileUtils.rm(get_path(key)) if File.exists? get_path(key)
|
58
63
|
end
|
59
64
|
|
60
65
|
# Delete ALL data from the cache, regardless of expiry time
|
61
66
|
def clear
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
end
|
67
|
+
return unless File.exist?(root_path)
|
68
|
+
FileUtils.rm_r(root_path)
|
69
|
+
FileUtils.mkdir_p(root_path)
|
66
70
|
end
|
67
71
|
|
68
72
|
# Delete all expired data from the cache
|
69
73
|
def purge
|
70
74
|
@t_purge = Time.new
|
71
|
-
purge_dir(
|
75
|
+
purge_dir(root_path) if @expiry > 0
|
72
76
|
end
|
73
77
|
|
74
78
|
private
|
@@ -76,16 +80,14 @@ module Terjira
|
|
76
80
|
def get_path(key)
|
77
81
|
md5 = Digest::MD5.hexdigest(key.to_s).to_s
|
78
82
|
|
79
|
-
dir = File.join(
|
83
|
+
dir = File.join(root_path, md5.split(//)[0..@depth - 1])
|
80
84
|
FileUtils.mkdir_p(dir)
|
81
|
-
|
85
|
+
File.join(dir, md5)
|
82
86
|
end
|
83
87
|
|
84
|
-
def
|
85
|
-
if @root
|
86
|
-
|
87
|
-
end
|
88
|
-
return @root
|
88
|
+
def root_path
|
89
|
+
@root = File.join(ROOT_DIR, @domain) if @root.nil?
|
90
|
+
@root
|
89
91
|
end
|
90
92
|
|
91
93
|
def purge_dir(dir)
|
@@ -102,9 +104,7 @@ module Terjira
|
|
102
104
|
end
|
103
105
|
|
104
106
|
# Delete empty directories
|
105
|
-
if Dir.entries(dir).delete_if{|e| e =~ /^\.\.?$/}.empty?
|
106
|
-
Dir.delete(dir)
|
107
|
-
end
|
107
|
+
Dir.delete(dir) if Dir.entries(dir).delete_if { |e| e =~ /^\.\.?$/ }.empty?
|
108
108
|
end
|
109
109
|
end
|
110
110
|
end
|
data/lib/terjira/version.rb
CHANGED
data/lib/terjira.rb
CHANGED
@@ -13,8 +13,19 @@ module Terjira
|
|
13
13
|
class CLI < Thor
|
14
14
|
desc 'login', 'login your Jira'
|
15
15
|
def login
|
16
|
+
pastel = Pastel.new
|
16
17
|
Client::Base.expire_auth_options
|
17
18
|
Client::Base.build_auth_options
|
19
|
+
|
20
|
+
# for touch base resource
|
21
|
+
Client::Field.all
|
22
|
+
puts pastel.blue("Login successful")
|
23
|
+
rescue JIRA::HTTPError => e
|
24
|
+
puts pastel.red(e.message)
|
25
|
+
Client::Base.expire_auth_options
|
26
|
+
rescue => e
|
27
|
+
Client::Base.expire_auth_options
|
28
|
+
raise e
|
18
29
|
end
|
19
30
|
|
20
31
|
desc 'logout', 'logout your Jira'
|