redmine_cli 0.5.1 → 0.6.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/.rvmrc +3 -1
- data/README.md +8 -1
- data/lib/assets/messages/en.yml +10 -2
- data/lib/assets/templates/en/issue/show.erb +5 -23
- data/lib/assets/templates/en/issue/show/detail.erb +45 -0
- data/lib/assets/templates/en/issue/show/detail/some_object.erb +16 -0
- data/lib/assets/templates/en/issue/show/detail/status.erb +18 -0
- data/lib/assets/templates/en/issue/show/detail/user.erb +6 -0
- data/lib/assets/templates/en/issue/show/journals.erb +21 -0
- data/lib/assets/templates/en/issue/show/user.erb +13 -0
- data/lib/redmine_cli/helpers/issue/create.rb +18 -2
- data/lib/redmine_cli/helpers/issue/update.rb +9 -2
- data/lib/redmine_cli/subcommands/conf.rb +9 -0
- data/lib/redmine_cli/subcommands/issue.rb +22 -2
- data/lib/redmine_cli/template_renderer.rb +1 -1
- data/lib/redmine_cli/version.rb +1 -1
- data/redmine_cli.gemspec +1 -1
- metadata +11 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2479d08810ffc54eb4bbdfb57148a88da022f257
|
4
|
+
data.tar.gz: deded6244bc061858921682443d02a95ed089298
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5ef74fd825bd0db651b61b17f4b949d482d4476feebaf42ec62d7c4675de5316a3882b30320896556f1601f2d333e61f763881c28fe118d7e7edb8b41e24cd9c
|
7
|
+
data.tar.gz: 9b83f76f4ee0f9c852bd1b98393a387d250c99fdfdded638c080e9c5abde7c8baddda0ad19ed970ffd1826f9dfc1c69a52838b8e38df330de0d9314f8feea51b
|
data/.rvmrc
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
+
echo 'rvm use 2.2.1@redmine_cli --create'
|
1
2
|
rvm use 2.2.1@redmine_cli --create
|
3
|
+
|
2
4
|
if [ -z "$(gem list | grep '^bundler\s')" ]; then
|
3
5
|
echo
|
4
6
|
echo '--- WARNING ---'
|
5
7
|
echo 'I cannot find "bundler" gem.'
|
6
|
-
echo 'Looks like you use gemset "
|
8
|
+
echo 'Looks like you use gemset "redmine_cli" at first time.'
|
7
9
|
echo 'Please, execute: gem install bundler; bundle install'
|
8
10
|
echo
|
9
11
|
fi
|
data/README.md
CHANGED
@@ -3,7 +3,12 @@
|
|
3
3
|
|
4
4
|
Command-Line Interface for Redmine.
|
5
5
|
|
6
|
-
Why?
|
6
|
+
Why? Because web-browser + mouse sucks
|
7
|
+
|
8
|
+
I hate using Redmine web-interface. All these mouse moves and clicks...
|
9
|
+
This CLI allows me to do some tasks much faster: I read issue descriptions and last comments right in the console.
|
10
|
+
|
11
|
+
It doesnt provide much functional so I need to open my browser sometimes, but for most of my tasks it fits well.
|
7
12
|
|
8
13
|
## Installation
|
9
14
|
|
@@ -29,6 +34,8 @@ Why? 'cause web-browser + mouse sucks
|
|
29
34
|
Don't forget about console aliases!
|
30
35
|
I use something like this:
|
31
36
|
|
37
|
+
# ~/.bashrc
|
38
|
+
#
|
32
39
|
# alias i='redmine issue'
|
33
40
|
# by the way, Thor can guess your commands so:
|
34
41
|
|
data/lib/assets/messages/en.yml
CHANGED
@@ -10,18 +10,23 @@ en:
|
|
10
10
|
show: 'More info about issue'
|
11
11
|
update: 'Change some issue params, add time entry, comment'
|
12
12
|
create: 'Create issue'
|
13
|
+
complete: 'Sets readiness to 100, changes assignee, changes status'
|
13
14
|
options:
|
14
15
|
show:
|
15
16
|
limit: 'Amount of comments (last journals).'
|
17
|
+
comments: 'Show only journals with comments'
|
16
18
|
update:
|
17
19
|
done: 'Readiness percentage'
|
18
|
-
assign: 'ID
|
20
|
+
assign: 'ID or name part of user issue will be assigned to'
|
19
21
|
time: 'Add time entry. Formats: HH:MM; M (minutes); H.h (hours in float)'
|
20
22
|
status: 'Change status of issue. Search by status name substring'
|
21
23
|
comment: 'Leave comment. It will open your text editor.'
|
22
24
|
description: 'Change description with your text editor.'
|
25
|
+
complete:
|
26
|
+
assign: 'ID or name part of user issue will be assigned to. If not defined, issue will be assigned to its author.'
|
23
27
|
conf:
|
24
28
|
init: 'Asks you few question to make your config file useful'
|
29
|
+
status_complete: 'Sets default status for completed issues'
|
25
30
|
user:
|
26
31
|
find: 'Finds users by id|email|name. Without arguments it will show your info'
|
27
32
|
project:
|
@@ -33,6 +38,8 @@ en:
|
|
33
38
|
enter_user: 'Enter your login to Redmine: '
|
34
39
|
enter_password: 'Enter your password: '
|
35
40
|
enter_site: 'Enter URL to your Redmine: '
|
41
|
+
status_complete:
|
42
|
+
select_status: 'Please, select status for completed issues.'
|
36
43
|
issue:
|
37
44
|
update:
|
38
45
|
type_comment_here: 'Type your comment here, save file and close'
|
@@ -52,4 +59,5 @@ en:
|
|
52
59
|
select_item_from_list: 'Select item from list: '
|
53
60
|
trackers: 'trackers'
|
54
61
|
projects: 'projects'
|
55
|
-
|
62
|
+
versions: 'versions'
|
63
|
+
without_version: '<without version>'
|
@@ -1,13 +1,12 @@
|
|
1
|
-
|
2
1
|
% ljust_value = 15
|
3
2
|
% info = { 'ID' => issue.id,
|
4
3
|
% 'Subject' => issue.subject,
|
5
4
|
% 'Status' => issue.status.name,
|
6
5
|
% 'Priority' => issue.priority.name,
|
7
6
|
% 'Readiness' => issue.done_ratio + '%',
|
8
|
-
% 'Author' =>
|
7
|
+
% 'Author' => TemplateRenderer.render('issue/show/user', user: issue.author) }
|
9
8
|
%
|
10
|
-
% info['Assigned to'] =
|
9
|
+
% info['Assigned to'] = TemplateRenderer.render('issue/show/user', user: issue.assigned_to) if issue.assigned_to?
|
11
10
|
% info['Project'] = issue.project.name if issue.project?
|
12
11
|
% info['Version'] = issue.fixed_version.name if issue.fixed_version?
|
13
12
|
% info['Parent'] = "#{issue.parent.id} - #{issue.parent.reload.subject.cut(80)}" if issue.parent?
|
@@ -34,7 +33,7 @@
|
|
34
33
|
|
35
34
|
% end
|
36
35
|
%
|
37
|
-
% if issue.attachments
|
36
|
+
% if issue.attachments.size > 0
|
38
37
|
<%= 'Attachments:'.yellow %>
|
39
38
|
|
40
39
|
% issue.attachments.each do |a|
|
@@ -53,25 +52,8 @@
|
|
53
52
|
% journals = []
|
54
53
|
% issue.journals.reverse.each do |j|
|
55
54
|
% break if journals.size > journals_limit
|
56
|
-
% next
|
55
|
+
% next if comments_only && !j.notes
|
57
56
|
% journals.push j
|
58
57
|
% end
|
59
|
-
|
60
|
-
% journals.reverse.each do |j|
|
61
|
-
|
62
|
-
<%= "----------\n".yellow %>
|
63
|
-
% journal_info = { 'From' => "#{j.user.name} (#{j.user.id})",
|
64
|
-
% 'Created' => j.created_on }
|
65
|
-
%
|
66
|
-
% journal_info.each do |k, v|
|
67
|
-
<%= "#{k}:".ljust(ljust_value).yellow %> <%= v %>
|
68
|
-
|
69
|
-
% end
|
70
|
-
|
71
|
-
|
72
|
-
<%= j.notes %>
|
73
|
-
|
74
|
-
|
75
|
-
% end
|
76
|
-
|
58
|
+
<%= TemplateRenderer.render 'issue/show/journals', journals: journals, ljust_value: ljust_value %>
|
77
59
|
% end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
% name = detail.name
|
2
|
+
% old_value = detail.old_value
|
3
|
+
% new_value = detail.new_value
|
4
|
+
%
|
5
|
+
% if detail.attribute?
|
6
|
+
% case name
|
7
|
+
% when 'subject', 'description'
|
8
|
+
% attribute = name.capitalize
|
9
|
+
% old = old_value.nil? ? '0' : old_value.size.to_s
|
10
|
+
% new = new_value.nil? ? '0' : new_value.size.to_s
|
11
|
+
%
|
12
|
+
% changes = "#{old_value ? old_value.size : 0} chars" + ' -> '.white + "#{new_value ? new_value.size : 0} chars"
|
13
|
+
% when 'assigned_to_id'
|
14
|
+
% attribute = 'Assigned'
|
15
|
+
% changes = TemplateRenderer.render('issue/show/detail/user', new_value: new_value, old_value: old_value)
|
16
|
+
% when 'done_ratio'
|
17
|
+
% attribute = 'Readiness'
|
18
|
+
% changes = "#{old_value}%" + ' -> '.white + "#{new_value}%"
|
19
|
+
% when 'estimated_hours'
|
20
|
+
% attribute = 'Estimated'
|
21
|
+
% changes = "#{old_value || 'nil'}" + ' -> '.white + "#{new_value || 'nil'}"
|
22
|
+
% when 'fixed_version_id'
|
23
|
+
% attribute = 'Version'
|
24
|
+
% changes = TemplateRenderer.render 'issue/show/detail/some_object',
|
25
|
+
% new_value: new_value,
|
26
|
+
% old_value: old_value,
|
27
|
+
% object_class: RedmineRest::Models::Version
|
28
|
+
% when 'status_id'
|
29
|
+
% attribute = 'Status'
|
30
|
+
% changes = TemplateRenderer.render 'issue/show/detail/status',
|
31
|
+
% new_value: new_value,
|
32
|
+
% old_value: old_value
|
33
|
+
% else
|
34
|
+
% attribute = name.capitalize
|
35
|
+
% changes = "#{old_value || 'nil'}" + ' -> '.white + "#{new_value || 'nil'}"
|
36
|
+
% end
|
37
|
+
%
|
38
|
+
% output = "#{attribute}:".ljust(ljust_value).white + changes
|
39
|
+
%
|
40
|
+
% elsif detail.relation?
|
41
|
+
% output = new_value.nil? ? "not #{name} #{old_value}" : "#{name} #{new_value}"
|
42
|
+
% else
|
43
|
+
% output = detail.inspect
|
44
|
+
% end
|
45
|
+
<%= output %>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
% old_obj = object_class.find_by_id(old_value)
|
2
|
+
% old = if old_obj
|
3
|
+
% old_obj.name.to_s + "[#{old_obj.id}]".red
|
4
|
+
% else
|
5
|
+
% 'nil'
|
6
|
+
% end
|
7
|
+
%
|
8
|
+
% new_obj = object_class.find_by_id(new_value)
|
9
|
+
% new = if new_obj
|
10
|
+
% new_obj.name.to_s + "[#{new_obj.id}]".red
|
11
|
+
% else
|
12
|
+
% 'nil'
|
13
|
+
% end
|
14
|
+
%
|
15
|
+
% output = old + ' -> '.white + new
|
16
|
+
<%= output %>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
% list = RedmineRest::Models::IssueStatus.all
|
2
|
+
%
|
3
|
+
% old_status = list.find { |i| i.id == old_value }
|
4
|
+
% old = if old_status
|
5
|
+
% old_status.name.to_s + "[#{old_status.id}]".red
|
6
|
+
% else
|
7
|
+
% 'nil'
|
8
|
+
% end
|
9
|
+
%
|
10
|
+
% new_status = list.find { |i| i.id == new_value }
|
11
|
+
% new = if new_status
|
12
|
+
% new_status.name.to_s + "[#{new_status.id}]".red
|
13
|
+
% else
|
14
|
+
% 'nil'
|
15
|
+
% end
|
16
|
+
%
|
17
|
+
% output = old + ' -> '.white + new
|
18
|
+
<%= output %>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
% journals.reverse.each do |j|
|
2
|
+
|
3
|
+
<%= "----------\n".yellow %>
|
4
|
+
% journal_info = { 'From' => TemplateRenderer.render('issue/show/user', user: j.user),
|
5
|
+
% 'Created' => j.created_on }
|
6
|
+
%
|
7
|
+
% journal_info.each do |k, v|
|
8
|
+
<%= "#{k}:".ljust(ljust_value).yellow %> <%= v %>
|
9
|
+
|
10
|
+
% end
|
11
|
+
|
12
|
+
% if j.details
|
13
|
+
% j.details.each do |d|
|
14
|
+
* <%= TemplateRenderer.render 'issue/show/detail', detail: d, ljust_value: ljust_value %>
|
15
|
+
% end
|
16
|
+
|
17
|
+
% end
|
18
|
+
<%= j.notes %>
|
19
|
+
|
20
|
+
|
21
|
+
% end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
% record = case user
|
2
|
+
% when String, Fixnum then RedmineRest::Models::User.find_by_id(user)
|
3
|
+
% when RedmineRest::Models::User then user
|
4
|
+
% else nil
|
5
|
+
% end
|
6
|
+
%
|
7
|
+
% output = if record
|
8
|
+
% (record.name? ? record.name : "#{record.firstname} #{record.lastname}") + "[#{record.id}]".red
|
9
|
+
% else
|
10
|
+
% 'nil'
|
11
|
+
% end
|
12
|
+
%
|
13
|
+
<%= output %>
|
@@ -14,6 +14,7 @@ module RedmineCLI
|
|
14
14
|
|
15
15
|
def set_attributes
|
16
16
|
set_project
|
17
|
+
set_version
|
17
18
|
set_tracker
|
18
19
|
set_subject
|
19
20
|
set_description
|
@@ -26,10 +27,18 @@ module RedmineCLI
|
|
26
27
|
@issue.project_id = @project.id
|
27
28
|
end
|
28
29
|
|
30
|
+
def set_version
|
31
|
+
list = [dummy_object_with_name(m(:without_version))] + @project.versions.to_a
|
32
|
+
return if list.size == 1
|
33
|
+
|
34
|
+
puts Unicode.upcase(m(:versions)) + ':'
|
35
|
+
@version = ask_for_object(list)
|
36
|
+
@issue.fixed_version_id = @version.is_a?(Models::Version) ? @version.id : nil
|
37
|
+
end
|
38
|
+
|
29
39
|
def set_tracker
|
30
40
|
puts Unicode.upcase(m(:trackers)) + ':'
|
31
|
-
|
32
|
-
@issue.tracker_id = ask_for_object(Models::Tracker.all)
|
41
|
+
@issue.tracker_id = ask_for_object(Models::Tracker.all).id
|
33
42
|
end
|
34
43
|
|
35
44
|
def set_subject
|
@@ -48,6 +57,13 @@ module RedmineCLI
|
|
48
57
|
@assignee = ask_for_object(@project.members)
|
49
58
|
@issue.assigned_to_id = @assignee.id
|
50
59
|
end
|
60
|
+
|
61
|
+
def dummy_object_with_name(name)
|
62
|
+
dummy = Object.new
|
63
|
+
dummy.define_singleton_method(:name) { name }
|
64
|
+
|
65
|
+
dummy
|
66
|
+
end
|
51
67
|
end
|
52
68
|
end
|
53
69
|
end
|
@@ -39,9 +39,16 @@ module RedmineCLI
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def update_status(issue)
|
42
|
-
|
42
|
+
opt = options[:status]
|
43
|
+
return unless opt
|
43
44
|
|
44
|
-
|
45
|
+
list = Models::IssueStatus.all
|
46
|
+
if opt.numeric?
|
47
|
+
status = list.find { |s| s.id == opt }
|
48
|
+
return issue.status_id = status.id if status
|
49
|
+
end
|
50
|
+
|
51
|
+
found_statuses = list.filter_by_name_substring(opt)
|
45
52
|
case found_statuses.size
|
46
53
|
when 0 then @errors.push "Status: #{m(:not_found)}"
|
47
54
|
when 1 then issue.status_id = found_statuses.first.id
|
@@ -22,6 +22,15 @@ module RedmineCLI
|
|
22
22
|
|
23
23
|
puts m(:thank_you)
|
24
24
|
end
|
25
|
+
|
26
|
+
desc 'status-complete', m('desc.conf.status_complete')
|
27
|
+
def status_complete
|
28
|
+
puts m('commands.conf.status_complete.select_status')
|
29
|
+
Config['statuses'] ||= {}
|
30
|
+
Config['statuses']['complete'] = ask_for_object(RedmineRest::Models::IssueStatus.all).id
|
31
|
+
Config.save
|
32
|
+
puts m(:thank_you)
|
33
|
+
end
|
25
34
|
end
|
26
35
|
end
|
27
36
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'thor'
|
2
2
|
require 'redmine_rest'
|
3
|
+
require_relative 'conf'
|
3
4
|
|
4
5
|
module RedmineCLI
|
5
6
|
module Subcommands
|
@@ -24,13 +25,33 @@ module RedmineCLI
|
|
24
25
|
|
25
26
|
desc 'show <id>', m('desc.issue.show')
|
26
27
|
option :limit, aliases: ['-l'], type: :numeric, default: 5, desc: m('desc.issue.options.show.limit')
|
28
|
+
option :comments_only, aliases: ['-c'], type: :boolean, desc: m('desc.issue.options.show.comments')
|
27
29
|
def show(id)
|
28
|
-
puts erb
|
30
|
+
puts erb 'issue/show',
|
31
|
+
issue: Models::Issue.find(id),
|
32
|
+
journals_limit: options[:limit],
|
33
|
+
comments_only: options[:comments_only]
|
29
34
|
|
30
35
|
rescue ActiveResource::ResourceNotFound # WARNING: it can be raised by associations in template
|
31
36
|
puts m(:not_found)
|
32
37
|
end
|
33
38
|
|
39
|
+
desc 'complete <id>', m('desc.issue.complete')
|
40
|
+
option :assign, aliases: ['-a'], type: :string, desc: m('desc.issue.options.complete.assign')
|
41
|
+
def complete(id)
|
42
|
+
issue = Models::Issue.find(id)
|
43
|
+
invoke(Conf, 'status_complete', []) unless Config['statuses'] && Config['statuses']['complete']
|
44
|
+
assign_to = if options[:assign]
|
45
|
+
InputParser.parse_user(options[:assign], project: issue.project).id
|
46
|
+
else
|
47
|
+
issue.author.id
|
48
|
+
end
|
49
|
+
|
50
|
+
invoke(:update, [id], ['-d', '100', '-s', Config['statuses']['complete'], '-c', '-a', assign_to])
|
51
|
+
rescue ActiveResource::ResourceNotFound
|
52
|
+
puts m(:not_found)
|
53
|
+
end
|
54
|
+
|
34
55
|
#
|
35
56
|
# TODO:
|
36
57
|
# * estimated time
|
@@ -69,7 +90,6 @@ module RedmineCLI
|
|
69
90
|
|
70
91
|
@issue = Models::Issue.new
|
71
92
|
set_attributes
|
72
|
-
|
73
93
|
@issue.save
|
74
94
|
puts 'Done.'
|
75
95
|
end
|
data/lib/redmine_cli/version.rb
CHANGED
data/redmine_cli.gemspec
CHANGED
@@ -27,7 +27,7 @@ Gem::Specification.new do |spec|
|
|
27
27
|
|
28
28
|
spec.add_dependency 'thor', '~> 0.19'
|
29
29
|
spec.add_dependency 'i18n', '~> 0.7'
|
30
|
-
spec.add_dependency 'redmine_rest', '0.
|
30
|
+
spec.add_dependency 'redmine_rest', '0.8.0'
|
31
31
|
spec.add_dependency 'non_config', '0.1.2'
|
32
32
|
spec.add_dependency 'colorize', '~> 0.7'
|
33
33
|
spec.add_dependency 'unicode', '~> 0.4'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redmine_cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dmitriy Non
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-05-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -100,14 +100,14 @@ dependencies:
|
|
100
100
|
requirements:
|
101
101
|
- - '='
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: 0.
|
103
|
+
version: 0.8.0
|
104
104
|
type: :runtime
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
108
|
- - '='
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: 0.
|
110
|
+
version: 0.8.0
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
112
|
name: non_config
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -174,6 +174,12 @@ files:
|
|
174
174
|
- lib/assets/templates/en/id_and_name_list.erb
|
175
175
|
- lib/assets/templates/en/issue/list.erb
|
176
176
|
- lib/assets/templates/en/issue/show.erb
|
177
|
+
- lib/assets/templates/en/issue/show/detail.erb
|
178
|
+
- lib/assets/templates/en/issue/show/detail/some_object.erb
|
179
|
+
- lib/assets/templates/en/issue/show/detail/status.erb
|
180
|
+
- lib/assets/templates/en/issue/show/detail/user.erb
|
181
|
+
- lib/assets/templates/en/issue/show/journals.erb
|
182
|
+
- lib/assets/templates/en/issue/show/user.erb
|
177
183
|
- lib/assets/templates/en/user/find.erb
|
178
184
|
- lib/redmine_cli.rb
|
179
185
|
- lib/redmine_cli/config.rb
|
@@ -211,7 +217,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
211
217
|
version: '0'
|
212
218
|
requirements: []
|
213
219
|
rubyforge_project:
|
214
|
-
rubygems_version: 2.
|
220
|
+
rubygems_version: 2.5.1
|
215
221
|
signing_key:
|
216
222
|
specification_version: 4
|
217
223
|
summary: CLI for Redmine
|