ursm-ditz 0.4 → 0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/Changelog +15 -0
- data/INSTALL +20 -0
- data/LICENSE +674 -0
- data/Manifest.txt +40 -0
- data/PLUGINS.txt +140 -0
- data/README.txt +43 -27
- data/Rakefile +36 -3
- data/ReleaseNotes +6 -0
- data/bin/ditz +49 -63
- data/contrib/completion/ditz.bash +25 -9
- data/lib/ditz.rb +52 -7
- data/lib/ditz/file-storage.rb +54 -0
- data/lib/{hook.rb → ditz/hook.rb} +1 -1
- data/lib/{html.rb → ditz/html.rb} +42 -4
- data/lib/{lowline.rb → ditz/lowline.rb} +31 -11
- data/lib/{model-objects.rb → ditz/model-objects.rb} +53 -21
- data/lib/ditz/model.rb +321 -0
- data/lib/{operator.rb → ditz/operator.rb} +122 -67
- data/lib/ditz/plugins/git-sync.rb +83 -0
- data/lib/{plugins → ditz/plugins}/git.rb +57 -18
- data/lib/ditz/plugins/issue-claiming.rb +174 -0
- data/lib/ditz/plugins/issue-labeling.rb +161 -0
- data/lib/{util.rb → ditz/util.rb} +4 -0
- data/lib/{view.rb → ditz/view.rb} +0 -0
- data/lib/{views.rb → ditz/views.rb} +7 -4
- data/man/{ditz.1 → man1/ditz.1} +1 -1
- data/setup.rb +1585 -0
- data/share/ditz/blue-check.png +0 -0
- data/{lib → share/ditz}/component.rhtml +7 -5
- data/share/ditz/green-bar.png +0 -0
- data/share/ditz/green-check.png +0 -0
- data/share/ditz/index.rhtml +130 -0
- data/share/ditz/issue.rhtml +119 -0
- data/share/ditz/issue_table.rhtml +28 -0
- data/share/ditz/red-check.png +0 -0
- data/share/ditz/release.rhtml +98 -0
- data/share/ditz/style.css +226 -0
- data/share/ditz/unassigned.rhtml +23 -0
- data/share/ditz/yellow-bar.png +0 -0
- metadata +50 -28
- data/lib/index.rhtml +0 -113
- data/lib/issue.rhtml +0 -111
- data/lib/issue_table.rhtml +0 -33
- data/lib/model.rb +0 -208
- data/lib/plugins/issue-claiming.rb +0 -92
- data/lib/release.rhtml +0 -69
- data/lib/style.css +0 -127
- data/lib/trollop.rb +0 -518
- data/lib/unassigned.rhtml +0 -31
- data/lib/vendor/yaml_waml.rb +0 -28
@@ -0,0 +1,83 @@
|
|
1
|
+
## git-sync ditz plugin
|
2
|
+
##
|
3
|
+
## This plugin is useful for when you want synchronized, non-distributed issue
|
4
|
+
## coordination with other developers, and you're using git. It allows you to
|
5
|
+
## synchronize issue updates with other developers by using the 'ditz sync'
|
6
|
+
## command, which does all the git work of sending and receiving issue change
|
7
|
+
## for you. However, you have to set things up in a very specific way for this
|
8
|
+
## to work:
|
9
|
+
##
|
10
|
+
## 1. Your ditz state must be on a separate branch. I recommend calling it
|
11
|
+
## 'bugs'. Create this branch, do a ditz init, and push it to the remote
|
12
|
+
## repo. (This means you won't be able to mingle issue change and code
|
13
|
+
## change in the same commits. If you care.)
|
14
|
+
## 2. Make a checkout of the bugs branch in a separate directory, but NOT in
|
15
|
+
## your code checkout. If you're developing in a directory called "project",
|
16
|
+
## I recommend making a ../project-bugs/ directory, cloning the repo there
|
17
|
+
## as well, and keeping that directory checked out to the 'bugs' branch.
|
18
|
+
## (There are various complicated things you can do to make that directory
|
19
|
+
## share git objects with your code directory, but I wouldn't bother unless
|
20
|
+
## you really care about disk space. Just make it an independent clone.)
|
21
|
+
## 3. Set that directory as your issue-dir in your .ditz-config file in your
|
22
|
+
## code checkout directory. (This file should be in .gitignore, btw.)
|
23
|
+
## 4. Run 'ditz reconfigure' and fill in the local branch name, remote
|
24
|
+
## branch name, and remote repo for the issue tracking branch.
|
25
|
+
##
|
26
|
+
## Once that's set up, 'ditz sync' will change to the bugs checkout dir, bundle
|
27
|
+
## up any changes you've made to issue status, push them to the remote repo,
|
28
|
+
## and pull any new changes in too. All ditz commands will read from your bugs
|
29
|
+
## directory, so you should be able to use ditz without caring about where
|
30
|
+
## things are anymore.
|
31
|
+
##
|
32
|
+
## This complicated setup is necessary to avoid accidentally mingling code
|
33
|
+
## change and issue change. With this setup, issue change is synchronized,
|
34
|
+
## but how you synchronize code is still up to you.
|
35
|
+
##
|
36
|
+
## Usage:
|
37
|
+
## 0. read all the above text very carefully
|
38
|
+
## 1. add a line "- git-sync" to the .ditz-plugins file in the project
|
39
|
+
## root
|
40
|
+
## 2. run 'ditz reconfigure' and answer its questions
|
41
|
+
## 3. run 'ditz sync' with abandon
|
42
|
+
|
43
|
+
module Ditz
|
44
|
+
|
45
|
+
class Config
|
46
|
+
field :git_sync_local_branch, :prompt => "Local bugs branch name for ditz sync", :default => "bugs"
|
47
|
+
field :git_sync_remote_branch, :prompt => "Remote bugs branch name for ditz sync", :default => "bugs"
|
48
|
+
field :git_sync_remote_repo, :prompt => "Remote bugs repo name for ditz sync", :default => "origin"
|
49
|
+
end
|
50
|
+
|
51
|
+
class Operator
|
52
|
+
operation :sync, "Sync the repo containing ditz via git" do
|
53
|
+
opt :dry_run, "Dry run: print the commants, but don't execute them", :short => "n"
|
54
|
+
end
|
55
|
+
def sync project, config, opts
|
56
|
+
unless config.git_sync_local_branch
|
57
|
+
$stderr.puts "Please run ditz reconfigure and set the local and remote branch names"
|
58
|
+
return
|
59
|
+
end
|
60
|
+
|
61
|
+
Dir.chdir $project_root
|
62
|
+
puts "[in #{$project_root}]"
|
63
|
+
sh "git add *.yaml", :force => true, :fake => opts[:dry_run]
|
64
|
+
sh "git commit -m 'issue updates'", :force => true, :fake => opts[:dry_run]
|
65
|
+
sh "git pull", :fake => opts[:dry_run]
|
66
|
+
sh "git push #{config.git_sync_remote_repo} #{config.git_sync_local_branch}:#{config.git_sync_remote_branch}", :fake => opts[:dry_run]
|
67
|
+
puts
|
68
|
+
puts "Ditz issue state synchronized."
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def sh s, opts={}
|
74
|
+
puts "+ #{s}"
|
75
|
+
return if opts[:fake]
|
76
|
+
unless system(s) || opts[:force]
|
77
|
+
$stderr.puts "non-zero (#{$?.exitstatus}) exit code: #{s}"
|
78
|
+
exit(-1)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
@@ -1,3 +1,31 @@
|
|
1
|
+
## git ditz plugin
|
2
|
+
##
|
3
|
+
## This plugin allows issues to be associated with git commits and git
|
4
|
+
## branches. Git commits can be easily tagged with a ditz issue with the 'ditz
|
5
|
+
## commit' command, and both 'ditz show' and the ditz HTML output will then
|
6
|
+
## contain a list of associated commits for each issue.
|
7
|
+
##
|
8
|
+
## Issues can also be assigned a single git feature branch. In this case, all
|
9
|
+
## commits on that branch will listed as commits for that issue. This
|
10
|
+
## particular feature is fairly rudimentary, however---it assumes the reference
|
11
|
+
## point is the 'master' branch, and once the feature branch is merged back
|
12
|
+
## into master, the list of commits disappears.
|
13
|
+
##
|
14
|
+
## Two configuration variables are added, which, when specified, are used to
|
15
|
+
## construct HTML links for the git commit id and branch names in the generated
|
16
|
+
## HTML output.
|
17
|
+
##
|
18
|
+
## Commands added:
|
19
|
+
## ditz set-branch: set the git branch of an issue
|
20
|
+
## ditz commit: run git-commit, and insert the issue id into the commit
|
21
|
+
## message.
|
22
|
+
##
|
23
|
+
## Usage:
|
24
|
+
## 1. add a line "- git" to the .ditz-plugins file in the project root
|
25
|
+
## 2. run ditz reconfigure, and enter the URL prefixes, if any, from
|
26
|
+
## which to create commit and branch links.
|
27
|
+
## 3. use 'ditz commit' with abandon.
|
28
|
+
|
1
29
|
require 'time'
|
2
30
|
|
3
31
|
module Ditz
|
@@ -11,7 +39,7 @@ class Issue
|
|
11
39
|
filters << "master..#{git_branch}" if git_branch
|
12
40
|
|
13
41
|
output = filters.map do |f|
|
14
|
-
`git log --pretty=format:\"%aD\t%an <%ae>\t%h\t%s\" #{f}`
|
42
|
+
`git log --pretty=format:\"%aD\t%an <%ae>\t%h\t%s\" #{f} 2> /dev/null`
|
15
43
|
end.join
|
16
44
|
|
17
45
|
@git_commits = output.split(/\n/).map { |l| l.split("\t") }.
|
@@ -55,17 +83,14 @@ EOS
|
|
55
83
|
|
56
84
|
[<<EOS, { :commits => commits, :url_prefix => config.git_commit_url_prefix }]
|
57
85
|
<h2>Commits for this issue</h2>
|
58
|
-
<table>
|
86
|
+
<table class="log">
|
59
87
|
<% commits.each_with_index do |(time, who, hash, msg), i| %>
|
60
|
-
|
61
|
-
<
|
62
|
-
|
63
|
-
<
|
64
|
-
<% end %>
|
65
|
-
<td class="logtime"><%=t time %></td>
|
66
|
-
<td class="logwho"><%=obscured_email who %></td>
|
67
|
-
<td class="logwhat"><%=h msg %> [<%= url_prefix && !url_prefix.blank? ? link_to([url_prefix, hash].join, hash) : hash %>]</td>
|
88
|
+
<tr class="<%= i % 2 == 0 ? "even-row" : "odd-row" %>">
|
89
|
+
<td class="time"><%=t time %></td>
|
90
|
+
<td class="person"><%=obscured_email who %></td>
|
91
|
+
<td class="message"><%=h msg %> [<%= url_prefix && !url_prefix.blank? ? link_to([url_prefix, hash].join, hash) : hash %>]</td>
|
68
92
|
</tr>
|
93
|
+
<tr><td></td></tr>
|
69
94
|
<% end %>
|
70
95
|
</table>
|
71
96
|
EOS
|
@@ -98,16 +123,30 @@ class Operator
|
|
98
123
|
:short => "-v", :default => false
|
99
124
|
opt :message, "Use the given <s> as the commit message.", \
|
100
125
|
:short => "-m", :type => :string
|
126
|
+
opt :edit, "Further edit the message, even if --message is given.", :short => "-e", :default => false
|
101
127
|
end
|
128
|
+
|
102
129
|
def commit project, config, opts, issue
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
130
|
+
opts[:edit] = true if opts[:message].nil?
|
131
|
+
|
132
|
+
args = {
|
133
|
+
:verbose => "--verbose",
|
134
|
+
:all => "--all",
|
135
|
+
:edit => "--edit",
|
136
|
+
}.map { |k, v| opts[k] ? v : "" }.join(" ")
|
137
|
+
|
138
|
+
comment = "# #{issue.name}: #{issue.title}"
|
139
|
+
tag = "Ditz-issue: #{issue.id}"
|
140
|
+
message = if opts[:message] && !opts[:edit]
|
141
|
+
"#{opts[:message]}\n\n#{tag}"
|
142
|
+
elsif opts[:message] && opts[:edit]
|
143
|
+
"#{opts[:message]}\n\n#{comment}\n#{tag}"
|
144
|
+
else
|
145
|
+
"#{comment}\n#{tag}"
|
146
|
+
end
|
147
|
+
|
148
|
+
message = message.gsub("\"", "\\\"")
|
149
|
+
exec "git commit #{args} --message=\"#{message}\""
|
111
150
|
end
|
112
151
|
end
|
113
152
|
|
@@ -0,0 +1,174 @@
|
|
1
|
+
## issue-claiming ditz plugin
|
2
|
+
##
|
3
|
+
## This plugin allows people to claim issues. This is useful for avoiding
|
4
|
+
## duplication of work---you can check to see if someone's claimed an
|
5
|
+
## issue before starting to work on it, and you can let people know what
|
6
|
+
## you're working on.
|
7
|
+
##
|
8
|
+
## Commands added:
|
9
|
+
## ditz claim: claim an issue for yourself
|
10
|
+
## ditz unclaim: unclaim a claimed issue
|
11
|
+
## ditz mine: show all issues claimed by you
|
12
|
+
## ditz claimed: show all claimed issues, by developer
|
13
|
+
## ditz unclaimed: show all unclaimed issues
|
14
|
+
##
|
15
|
+
## Usage:
|
16
|
+
## 1. add a line "- issue-claiming" to the .ditz-plugins file in the project
|
17
|
+
## root
|
18
|
+
## 2. use the above commands to abandon
|
19
|
+
|
20
|
+
module Ditz
|
21
|
+
class Issue
|
22
|
+
field :claimer, :ask => false
|
23
|
+
|
24
|
+
def claim who, comment, force=false
|
25
|
+
raise Error, "already claimed by #{claimer}" if claimer && !force
|
26
|
+
log "issue claimed", who, comment
|
27
|
+
self.claimer = who
|
28
|
+
end
|
29
|
+
|
30
|
+
def unclaim who, comment, force=false
|
31
|
+
raise Error, "not claimed" unless claimer
|
32
|
+
raise Error, "can only be unclaimed by #{claimer}" unless claimer == who || force
|
33
|
+
if claimer == who
|
34
|
+
log "issue unclaimed", who, comment
|
35
|
+
else
|
36
|
+
log "unassigned from #{claimer}", who, comment
|
37
|
+
end
|
38
|
+
self.claimer = nil
|
39
|
+
end
|
40
|
+
|
41
|
+
def claimed?; claimer end
|
42
|
+
def unclaimed?; !claimed? end
|
43
|
+
end
|
44
|
+
|
45
|
+
class ScreenView
|
46
|
+
add_to_view :issue_summary do |issue, config|
|
47
|
+
" Claimed by: #{issue.claimer || 'none'}\n"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class HtmlView
|
52
|
+
add_to_view :issue_summary do |issue, config|
|
53
|
+
next unless issue.claimer
|
54
|
+
[<<EOS, { :issue => issue }]
|
55
|
+
<tr>
|
56
|
+
<td class='attrname'>Claimed by:</td>
|
57
|
+
<td class='attrval'><%= h(issue.claimer) %></td>
|
58
|
+
</tr>
|
59
|
+
EOS
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class Operator
|
64
|
+
alias :__issue_claiming_start :start
|
65
|
+
def start project, config, opts, issue
|
66
|
+
if issue.claimed? && issue.claimer != config.user
|
67
|
+
raise Error, "issue #{issue.name} claimed by #{issue.claimer}"
|
68
|
+
else
|
69
|
+
__issue_claiming_start project, config, opts, issue
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
alias :__issue_claiming_stop :stop
|
74
|
+
def stop project, config, opts, issue
|
75
|
+
if issue.claimed? && issue.claimer != config.user
|
76
|
+
raise Error, "issue #{issue.name} claimed by #{issue.claimer}"
|
77
|
+
else
|
78
|
+
__issue_claiming_stop project, config, opts, issue
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
alias :__issue_claiming_close :close
|
83
|
+
def close project, config, opts, issue
|
84
|
+
if issue.claimed? && issue.claimer != config.user
|
85
|
+
raise Error, "issue #{issue.name} claimed by #{issue.claimer}"
|
86
|
+
else
|
87
|
+
__issue_claiming_close project, config, opts, issue
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
operation :claim, "Claim an issue for yourself", :issue do
|
92
|
+
opt :force, "Claim this issue even if someone else has claimed it", :default => false
|
93
|
+
end
|
94
|
+
def claim project, config, opts, issue
|
95
|
+
puts "Claiming issue #{issue.name}: #{issue.title}."
|
96
|
+
comment = ask_multiline "Comments" unless $opts[:no_comment]
|
97
|
+
issue.claim config.user, comment, opts[:force]
|
98
|
+
puts "Issue #{issue.name} marked as claimed by #{config.user}"
|
99
|
+
end
|
100
|
+
|
101
|
+
operation :unclaim, "Unclaim a claimed issue", :issue do
|
102
|
+
opt :force, "Unclaim this issue even if it's claimed by someone else", :default => false
|
103
|
+
end
|
104
|
+
def unclaim project, config, opts, issue
|
105
|
+
puts "Unclaiming issue #{issue.name}: #{issue.title}."
|
106
|
+
comment = ask_multiline "Comments" unless $opts[:no_comment]
|
107
|
+
issue.unclaim config.user, comment, opts[:force]
|
108
|
+
puts "Issue #{issue.name} marked as unclaimed."
|
109
|
+
end
|
110
|
+
|
111
|
+
operation :mine, "Show all issues claimed by you", :maybe_release do
|
112
|
+
opt :all, "Show all issues, not just open ones"
|
113
|
+
end
|
114
|
+
def mine project, config, opts, releases
|
115
|
+
releases ||= project.unreleased_releases + [:unassigned]
|
116
|
+
releases = [*releases]
|
117
|
+
|
118
|
+
issues = project.issues.select do |i|
|
119
|
+
r = project.release_for(i.release) || :unassigned
|
120
|
+
releases.member?(r) && i.claimer == config.user && (opts[:all] || i.open?)
|
121
|
+
end
|
122
|
+
if issues.empty?
|
123
|
+
puts "No issues."
|
124
|
+
else
|
125
|
+
print_todo_list_by_release_for project, issues
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
operation :claimed, "Show claimed issues by claimer", :maybe_release do
|
130
|
+
opt :all, "Show all issues, not just open ones"
|
131
|
+
end
|
132
|
+
def claimed project, config, opts, releases
|
133
|
+
releases ||= project.unreleased_releases + [:unassigned]
|
134
|
+
releases = [*releases]
|
135
|
+
|
136
|
+
issues = project.issues.inject({}) do |h, i|
|
137
|
+
r = project.release_for(i.release) || :unassigned
|
138
|
+
if i.claimed? && (opts[:all] || i.open?) && releases.member?(r)
|
139
|
+
(h[i.claimer] ||= []) << i
|
140
|
+
end
|
141
|
+
h
|
142
|
+
end
|
143
|
+
|
144
|
+
if issues.empty?
|
145
|
+
puts "No issues."
|
146
|
+
else
|
147
|
+
issues.keys.sort.each do |c|
|
148
|
+
puts "#{c}:"
|
149
|
+
puts todo_list_for(issues[c], :show_release => true)
|
150
|
+
puts
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
operation :unclaimed, "Show all unclaimed issues", :maybe_release do
|
156
|
+
opt :all, "Show all issues, not just open ones"
|
157
|
+
end
|
158
|
+
def unclaimed project, config, opts, releases
|
159
|
+
releases ||= project.unreleased_releases + [:unassigned]
|
160
|
+
releases = [*releases]
|
161
|
+
|
162
|
+
issues = project.issues.select do |i|
|
163
|
+
r = project.release_for(i.release) || :unassigned
|
164
|
+
releases.member?(r) && i.claimer.nil? && (opts[:all] || i.open?)
|
165
|
+
end
|
166
|
+
if issues.empty?
|
167
|
+
puts "No issues."
|
168
|
+
else
|
169
|
+
print_todo_list_by_release_for project, issues
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
## issue-labeling ditz plugin
|
2
|
+
##
|
3
|
+
## This plugin allows label issues. This can replace the issue component
|
4
|
+
## and/or issue types (bug,feature,task), by providing a more flexible
|
5
|
+
## to organize your issues.
|
6
|
+
##
|
7
|
+
## Commands added:
|
8
|
+
## ditz new_label [label]: create a new label for the project
|
9
|
+
## ditz label <issue> <labels>: label an issue with some labels
|
10
|
+
## ditz unlabel <issue> [labels]: remove some label(s) of an issue
|
11
|
+
## ditz labeled <labels> [release]: show all issues with these labels
|
12
|
+
##
|
13
|
+
## Usage:
|
14
|
+
## 1. add a line "- issue-labeling" to the .ditz-plugins file in the project
|
15
|
+
## root
|
16
|
+
## 2. use the above commands to abandon
|
17
|
+
|
18
|
+
# TODO:
|
19
|
+
# * extend the HTML view to have per-labels listings
|
20
|
+
# * allow for more compact way to type them (completion, prefixes...)
|
21
|
+
|
22
|
+
module Ditz
|
23
|
+
|
24
|
+
class Label < ModelObject
|
25
|
+
include Comparable
|
26
|
+
field :name
|
27
|
+
|
28
|
+
def name_prefix; @name.gsub(/\s+/, "-").downcase end
|
29
|
+
|
30
|
+
def <=> x ; name <=> x.name end
|
31
|
+
def == x ; name == x.name end
|
32
|
+
|
33
|
+
end # class Label
|
34
|
+
|
35
|
+
class Project
|
36
|
+
field :labels, :multi => true, :interactive_generator => :get_labels
|
37
|
+
|
38
|
+
def get_labels
|
39
|
+
lab_names = ask_for_many("labels")
|
40
|
+
([name] + lab_names).uniq.map { |n| Label.create_interactively :with => { :name => n } }
|
41
|
+
end
|
42
|
+
|
43
|
+
def label_for label_name
|
44
|
+
labels.find { |i| i.name == label_name }
|
45
|
+
end
|
46
|
+
|
47
|
+
def labels_for label_names
|
48
|
+
label_names.split(/\s*,\s*/).map do |val|
|
49
|
+
label_for(val) or raise Error, "no label with name #{val}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end # class Project
|
54
|
+
|
55
|
+
class Issue
|
56
|
+
field :labels, :multi => true, :interactive_generator => :get_labels
|
57
|
+
|
58
|
+
def get_labels config, project
|
59
|
+
ask_for_selection(project.labels, "label", :name, true).map {|x|x.name}
|
60
|
+
end
|
61
|
+
|
62
|
+
def apply_labels new_labels, who, comment
|
63
|
+
log "issue labeled", who, comment
|
64
|
+
new_labels.each { |l| add_label l }
|
65
|
+
end
|
66
|
+
|
67
|
+
def remove_labels labels_to_remove, who, comment
|
68
|
+
log "issue unlabeled", who, comment
|
69
|
+
if labels_to_remove.nil?
|
70
|
+
self.labels = []
|
71
|
+
else
|
72
|
+
labels_to_remove.each { |l| drop_label l }
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def labeled? label=nil; (label.nil?)? !labels.empty? : labels.include?(label) end
|
77
|
+
def unlabeled? label=nil; !labeled?(label) end
|
78
|
+
end
|
79
|
+
|
80
|
+
class ScreenView
|
81
|
+
add_to_view :issue_summary do |issue, config|
|
82
|
+
" Labels: #{(issue.labeled?)? issue.labels.map{|l|l.name}.join(', ') : 'none'}\n"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class HtmlView
|
87
|
+
add_to_view :issue_summary do |issue, config|
|
88
|
+
next if issue.unlabeled?
|
89
|
+
[<<EOS, { :issue => issue }]
|
90
|
+
<tr>
|
91
|
+
<td class='attrname'>Labels:</td>
|
92
|
+
<td class='attrval'><%= h(issue.labels.map{|l|l.name}.join(', ')) %></td>
|
93
|
+
</tr>
|
94
|
+
EOS
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class Operator
|
99
|
+
|
100
|
+
operation :new_label, "Create a new label for the project", :maybe_label
|
101
|
+
def new_label project, config, maybe_label
|
102
|
+
puts "Adding label #{maybe_label}." if maybe_label
|
103
|
+
label = Label.create_interactively(:args => [project, config], :with => { :name => maybe_label }) or return
|
104
|
+
project.add_label label
|
105
|
+
puts "Added label #{label.name}."
|
106
|
+
end
|
107
|
+
|
108
|
+
operation :label, "Apply labels to an issue", :issue, :labels
|
109
|
+
def label project, config, issue, label_names
|
110
|
+
labels = project.labels_for label_names
|
111
|
+
puts "Adding labels #{label_names} to issue #{issue.name}: #{issue.title}."
|
112
|
+
comment = ask_multiline "Comments" unless $opts[:no_comment]
|
113
|
+
issue.apply_labels labels, config.user, comment
|
114
|
+
show_labels issue
|
115
|
+
end
|
116
|
+
|
117
|
+
operation :unlabel, "Remove some labels of an issue", :issue, :maybe_labels
|
118
|
+
def unlabel project, config, issue, label_names
|
119
|
+
labels = if label_names
|
120
|
+
puts "Removing #{label_names} labels from issue #{issue.name}: #{issue.title}."
|
121
|
+
project.labels_for label_names
|
122
|
+
else
|
123
|
+
puts "Removing labels from issue #{issue.name}: #{issue.title}."
|
124
|
+
nil
|
125
|
+
end
|
126
|
+
comment = ask_multiline "Comments" unless $opts[:no_comment]
|
127
|
+
issue.remove_labels labels, config.user, comment
|
128
|
+
show_labels issue
|
129
|
+
end
|
130
|
+
|
131
|
+
def show_labels issue
|
132
|
+
if issue.labeled?
|
133
|
+
puts "Issue #{issue.name} is now labeled with #{issue.labels.map{|l|l.name}.join(', ')}"
|
134
|
+
else
|
135
|
+
puts "Issue #{issue.name} is now unlabeled"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
private :show_labels
|
139
|
+
|
140
|
+
operation :labeled, "Show labeled issues", :labels, :maybe_release do
|
141
|
+
opt :all, "Show all issues, not just open ones"
|
142
|
+
end
|
143
|
+
def labeled project, config, opts, labels, releases
|
144
|
+
releases ||= project.unreleased_releases + [:unassigned]
|
145
|
+
releases = [*releases]
|
146
|
+
labels = project.labels_for labels
|
147
|
+
|
148
|
+
issues = project.issues.select do |i|
|
149
|
+
r = project.release_for(i.release) || :unassigned
|
150
|
+
labels.all? { |l| i.labeled? l } && (opts[:all] || i.open?) && releases.member?(r)
|
151
|
+
end
|
152
|
+
|
153
|
+
if issues.empty?
|
154
|
+
puts "No issues."
|
155
|
+
else
|
156
|
+
print_todo_list_by_release_for project, issues
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|