terjira 0.1.1 → 0.2.0
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/.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'
|