TicGit-ng 1.0.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.
- data/LICENSE_GPL +681 -0
- data/LICENSE_MIT +29 -0
- data/README.mkd +425 -0
- data/bin/ti +13 -0
- data/bin/ticgitweb +313 -0
- data/lib/ticgit-ng/base.rb +365 -0
- data/lib/ticgit-ng/cli.rb +240 -0
- data/lib/ticgit-ng/command/assign.rb +43 -0
- data/lib/ticgit-ng/command/checkout.rb +14 -0
- data/lib/ticgit-ng/command/comment.rb +36 -0
- data/lib/ticgit-ng/command/list.rb +80 -0
- data/lib/ticgit-ng/command/milestone.rb +26 -0
- data/lib/ticgit-ng/command/new.rb +46 -0
- data/lib/ticgit-ng/command/points.rb +28 -0
- data/lib/ticgit-ng/command/recent.rb +29 -0
- data/lib/ticgit-ng/command/show.rb +18 -0
- data/lib/ticgit-ng/command/state.rb +45 -0
- data/lib/ticgit-ng/command/sync.rb +29 -0
- data/lib/ticgit-ng/command/tag.rb +31 -0
- data/lib/ticgit-ng/command.rb +77 -0
- data/lib/ticgit-ng/comment.rb +17 -0
- data/lib/ticgit-ng/ticket.rb +233 -0
- data/lib/ticgit-ng/version.rb +3 -0
- data/lib/ticgit-ng.rb +35 -0
- metadata +120 -0
@@ -0,0 +1,240 @@
|
|
1
|
+
require 'ticgit-ng'
|
2
|
+
require 'ticgit-ng/command'
|
3
|
+
|
4
|
+
# used Cap as a model for this - thanks Jamis
|
5
|
+
|
6
|
+
module TicGitNG
|
7
|
+
class CLI
|
8
|
+
def self.execute
|
9
|
+
parse(ARGV).execute!
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.parse(args)
|
13
|
+
#new() calls initialize(...) below
|
14
|
+
cli = new(args)
|
15
|
+
cli.parse_options!
|
16
|
+
cli
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :action, :options, :args, :tic
|
20
|
+
attr_accessor :out
|
21
|
+
|
22
|
+
def initialize(args, path = '.', out = $stdout)
|
23
|
+
@args = args.dup
|
24
|
+
@tic = TicGitNG.open(path, :keep_state => true)
|
25
|
+
@options = OpenStruct.new
|
26
|
+
@out = out
|
27
|
+
|
28
|
+
@out.sync = true # so that Net::SSH prompts show up
|
29
|
+
rescue NoRepoFound
|
30
|
+
puts "No repo found"
|
31
|
+
exit
|
32
|
+
end
|
33
|
+
|
34
|
+
def execute!
|
35
|
+
if mod = Command.get(action)
|
36
|
+
extend(mod)
|
37
|
+
|
38
|
+
if respond_to?(:parser)
|
39
|
+
option_parser = Command.parser(action, &method(:parser))
|
40
|
+
else
|
41
|
+
option_parser = Command.parser(action)
|
42
|
+
end
|
43
|
+
|
44
|
+
option_parser.parse!(args)
|
45
|
+
|
46
|
+
execute if respond_to?(:execute)
|
47
|
+
else
|
48
|
+
puts usage
|
49
|
+
|
50
|
+
if args.empty? and !action
|
51
|
+
exit
|
52
|
+
else
|
53
|
+
puts('%p is not a command' % action)
|
54
|
+
exit 1
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def parse_options! #:nodoc:
|
60
|
+
if args.empty?
|
61
|
+
puts "Please specify at least one action to execute."
|
62
|
+
puts
|
63
|
+
puts usage(args)
|
64
|
+
exit 1
|
65
|
+
end
|
66
|
+
|
67
|
+
@action = args.shift
|
68
|
+
end
|
69
|
+
|
70
|
+
def usage(args = nil)
|
71
|
+
old_args = args || [action, *self.args].compact
|
72
|
+
|
73
|
+
if respond_to?(:parser)
|
74
|
+
Command.parser('COMMAND', &method(:parser))
|
75
|
+
# option_parser.parse!(args)
|
76
|
+
else
|
77
|
+
Command.usage(old_args.first, old_args)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def get_editor_message(message_file = nil)
|
82
|
+
message_file = Tempfile.new('ticgitng_message').path if !message_file
|
83
|
+
|
84
|
+
editor = ENV["EDITOR"] || 'vim'
|
85
|
+
system("#{editor} #{message_file}");
|
86
|
+
message = File.readlines(message_file)
|
87
|
+
message = message.select { |line| line[0, 1] != '#' } # removing comments
|
88
|
+
if message.empty?
|
89
|
+
return false
|
90
|
+
else
|
91
|
+
return message
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def ticket_show(t, more=nil)
|
96
|
+
days_ago = ((Time.now - t.opened) / (60 * 60 * 24)).round
|
97
|
+
|
98
|
+
data = [
|
99
|
+
['Title', t.title],
|
100
|
+
['TicId', t.ticket_id],
|
101
|
+
'',
|
102
|
+
['Assigned', t.assigned],
|
103
|
+
['Opened', "#{t.opened} (#{days_ago} days)"],
|
104
|
+
['State', t.state.upcase],
|
105
|
+
['Points', t.points || 'no estimate'],
|
106
|
+
['Tags', t.tags.join(', ')],
|
107
|
+
''
|
108
|
+
]
|
109
|
+
|
110
|
+
data.each do |(key, value)|
|
111
|
+
puts(value ? "#{key}: #{value}" : key)
|
112
|
+
end
|
113
|
+
|
114
|
+
unless t.comments.empty?
|
115
|
+
puts "Comments (#{t.comments.size}):"
|
116
|
+
t.comments.reverse_each do |c|
|
117
|
+
puts " * Added #{c.added.strftime('%m/%d %H:%M')} by #{c.user}"
|
118
|
+
|
119
|
+
wrapped = c.comment.split("\n").map{|line|
|
120
|
+
line.length > 80 ? line.gsub(/(.{1,80})(\s+|$)/, "\\1\n").strip : line
|
121
|
+
}.join("\n")
|
122
|
+
|
123
|
+
wrapped = wrapped.split("\n").map{|line| "\t#{line}" }
|
124
|
+
|
125
|
+
if wrapped.size > 6 and more.nil?
|
126
|
+
puts wrapped[0, 6].join("\n")
|
127
|
+
puts "\t** more... **"
|
128
|
+
else
|
129
|
+
puts wrapped.join("\n")
|
130
|
+
end
|
131
|
+
puts
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
class << self
|
137
|
+
attr_accessor :window_lines, :window_cols
|
138
|
+
|
139
|
+
TIOCGWINSZ_INTEL = 0x5413 # For an Intel processor
|
140
|
+
TIOCGWINSZ_PPC = 0x40087468 # For a PowerPC processor
|
141
|
+
STDOUT_HANDLE = 0xFFFFFFF5 # For windows
|
142
|
+
|
143
|
+
def reset_window_width
|
144
|
+
try_using(TIOCGWINSZ_PPC) ||
|
145
|
+
try_using(TIOCGWINSZ_INTEL) ||
|
146
|
+
try_windows ||
|
147
|
+
use_fallback
|
148
|
+
end
|
149
|
+
|
150
|
+
# Set terminal dimensions using ioctl syscall on *nix platform
|
151
|
+
# TODO: find out what is raised here on windows.
|
152
|
+
def try_using(mask)
|
153
|
+
buf = [0,0,0,0].pack("S*")
|
154
|
+
|
155
|
+
if $stdout.ioctl(mask, buf) >= 0
|
156
|
+
self.window_lines, self.window_cols = buf.unpack("S2")
|
157
|
+
true
|
158
|
+
end
|
159
|
+
rescue Errno::EINVAL
|
160
|
+
end
|
161
|
+
|
162
|
+
def try_windows
|
163
|
+
lines, cols = windows_terminal_size
|
164
|
+
self.window_lines, self.window_cols = lines, cols if lines and cols
|
165
|
+
end
|
166
|
+
|
167
|
+
# Determine terminal dimensions on windows platform
|
168
|
+
def windows_terminal_size
|
169
|
+
m_GetStdHandle = Win32API.new(
|
170
|
+
'kernel32', 'GetStdHandle', ['L'], 'L')
|
171
|
+
m_GetConsoleScreenBufferInfo = Win32API.new(
|
172
|
+
'kernel32', 'GetConsoleScreenBufferInfo', ['L', 'P'], 'L' )
|
173
|
+
format = 'SSSSSssssSS'
|
174
|
+
buf = ([0] * format.size).pack(format)
|
175
|
+
stdout_handle = m_GetStdHandle.call(STDOUT_HANDLE)
|
176
|
+
|
177
|
+
m_GetConsoleScreenBufferInfo.call(stdout_handle, buf)
|
178
|
+
(bufx, bufy, curx, cury, wattr,
|
179
|
+
left, top, right, bottom, maxx, maxy) = buf.unpack(format)
|
180
|
+
return bottom - top + 1, right - left + 1
|
181
|
+
rescue NameError
|
182
|
+
end
|
183
|
+
|
184
|
+
def use_fallback
|
185
|
+
self.window_lines, self.window_cols = 25, 80
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def window_lines
|
190
|
+
TicGitNG::CLI.window_lines
|
191
|
+
end
|
192
|
+
|
193
|
+
def window_cols
|
194
|
+
TicGitNG::CLI.window_cols
|
195
|
+
end
|
196
|
+
|
197
|
+
if ''.respond_to?(:chars)
|
198
|
+
# assume 1.9
|
199
|
+
def just(value, size = 10, side = :left)
|
200
|
+
value = value.to_s
|
201
|
+
|
202
|
+
if value.bytesize > size
|
203
|
+
sub_value = "#{value[0, size - 1]}\xe2\x80\xa6"
|
204
|
+
else
|
205
|
+
sub_value = value[0, size]
|
206
|
+
end
|
207
|
+
|
208
|
+
just_common(sub_value, size, side)
|
209
|
+
end
|
210
|
+
else
|
211
|
+
def just(value, size = 10, side = :left)
|
212
|
+
chars = value.to_s.scan(/./um)
|
213
|
+
|
214
|
+
if chars.size > size
|
215
|
+
sub_value = "#{chars[0, size-1]}\xe2\x80\xa6"
|
216
|
+
else
|
217
|
+
sub_value = chars.join
|
218
|
+
end
|
219
|
+
|
220
|
+
just_common(sub_value, size, side)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def just_common(value, size, side)
|
225
|
+
case side
|
226
|
+
when :r, :right
|
227
|
+
value.rjust(size)
|
228
|
+
when :l, :left
|
229
|
+
value.ljust(size)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def puts(*strings)
|
234
|
+
@out.puts(*strings)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
TicGitNG::CLI.reset_window_width
|
240
|
+
Signal.trap("SIGWINCH") { TicGitNG::CLI.reset_window_width }
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module TicGitNG
|
2
|
+
module Command
|
3
|
+
# Assigns a ticket to someone
|
4
|
+
#
|
5
|
+
# Usage:
|
6
|
+
# ti assign (assign checked out ticket to current user)
|
7
|
+
# ti assign {1} (assign ticket to current user)
|
8
|
+
# ti assign -c {1} (assign ticket to current user and checkout the ticket)
|
9
|
+
# ti assign -u {name} (assign ticket to specified user)
|
10
|
+
# ti assign -u {name} {1} (assign specified ticket to specified user)
|
11
|
+
module Assign
|
12
|
+
def parser(opts)
|
13
|
+
opts.banner = "Usage: ti assign [options] [ticket_id]"
|
14
|
+
opts.on_head(
|
15
|
+
"-u USER", "--user USER", "Assign the ticket to this user"){|v|
|
16
|
+
options.user = v
|
17
|
+
}
|
18
|
+
opts.on_head(
|
19
|
+
"-c TICKET", "--checkout TICKET", "Checkout this ticket"){|v|
|
20
|
+
options.checkout = v
|
21
|
+
}
|
22
|
+
end
|
23
|
+
def execute
|
24
|
+
handle_ticket_assign
|
25
|
+
end
|
26
|
+
def handle_ticket_assign
|
27
|
+
tic.ticket_checkout(options.checkout) if options.checkout
|
28
|
+
if ARGV.length == 1 #ti assign
|
29
|
+
tic_id=nil
|
30
|
+
elsif ARGV.length == 2 #ti assign {ticid}
|
31
|
+
tic_id=ARGV[2]
|
32
|
+
elsif ARGV.length == 3 #ti assign -u/-c {user/ticid}
|
33
|
+
if options.user
|
34
|
+
tic_id=nil
|
35
|
+
else
|
36
|
+
tic_id=options.checkout
|
37
|
+
end
|
38
|
+
end
|
39
|
+
tic.ticket_assign((options.user rescue nil), tic_id)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module TicGitNG
|
2
|
+
module Command
|
3
|
+
module Comment
|
4
|
+
def parser(opts)
|
5
|
+
opts.banner = "Usage: ti comment [tic_id] [options]"
|
6
|
+
opts.on_head(
|
7
|
+
"-m MESSAGE", "--message MESSAGE",
|
8
|
+
"Message you would like to add as a comment"){|v|
|
9
|
+
options.message = v
|
10
|
+
}
|
11
|
+
opts.on_head(
|
12
|
+
"-f FILE", "--file FILE",
|
13
|
+
"A file that contains the comment you would like to add"){|v|
|
14
|
+
raise ArgumentError, "Only 1 of -f/--file and -m/--message can be specified" if options.message
|
15
|
+
raise ArgumentError, "File #{v} doesn't exist" unless File.file?(v)
|
16
|
+
raise ArgumentError, "File #{v} must be <= 2048 bytes" unless File.size(v) <= 2048
|
17
|
+
options.file = v
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def execute
|
22
|
+
tid = args[0].strip if args[0]
|
23
|
+
message, file = options.message, options.file
|
24
|
+
|
25
|
+
if message
|
26
|
+
tic.ticket_comment(message, tid)
|
27
|
+
elsif file
|
28
|
+
tic.ticket_comment(File.read(file), tid)
|
29
|
+
else
|
30
|
+
return unless message = get_editor_message
|
31
|
+
tic.ticket_comment(message.join(''), tid)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module TicGitNG
|
2
|
+
module Command
|
3
|
+
# List tickets
|
4
|
+
module List
|
5
|
+
def parser(o)
|
6
|
+
o.banner = "Usage: ti list [options]"
|
7
|
+
o.on_head(
|
8
|
+
"-o ORDER", "--order ORDER",
|
9
|
+
"Field to order by - one of : assigned,state,date,title"){|v|
|
10
|
+
options.order = v
|
11
|
+
}
|
12
|
+
|
13
|
+
o.on_head(
|
14
|
+
"-t TAG[,TAG]", "--tags TAG[,TAG]", Array,
|
15
|
+
"List only tickets with specific tag(s)",
|
16
|
+
"Prefix the tag with '-' to negate"){|v|
|
17
|
+
options.tags ||= Set.new
|
18
|
+
options.tags.merge v
|
19
|
+
}
|
20
|
+
|
21
|
+
o.on_head(
|
22
|
+
"-s STATE[,STATE]", "--states STATE[,STATE]", Array,
|
23
|
+
"List only tickets in a specific state(s)",
|
24
|
+
"Prefix the state with '-' to negate"){|v|
|
25
|
+
options.states ||= Set.new
|
26
|
+
options.states.merge v
|
27
|
+
}
|
28
|
+
|
29
|
+
o.on_head(
|
30
|
+
"-a ASSIGNED", "--assigned ASSIGNED",
|
31
|
+
"List only tickets assigned to someone"){|v|
|
32
|
+
options.assigned = v
|
33
|
+
}
|
34
|
+
|
35
|
+
o.on_head("-S SAVENAME", "--saveas SAVENAME",
|
36
|
+
"Save this list as a saved name"){|v|
|
37
|
+
options.save = v
|
38
|
+
}
|
39
|
+
|
40
|
+
o.on_head("-l", "--list", "Show the saved queries"){|v|
|
41
|
+
options.list = true
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
def execute
|
46
|
+
options.saved = args[0] if args[0]
|
47
|
+
|
48
|
+
if tickets = tic.ticket_list(options.to_hash)
|
49
|
+
counter = 0
|
50
|
+
cols = [80, window_cols].max
|
51
|
+
|
52
|
+
puts
|
53
|
+
puts [' ', just('#', 4, 'r'),
|
54
|
+
just('TicId', 6),
|
55
|
+
just('Title', cols - 56),
|
56
|
+
just('State', 5),
|
57
|
+
just('Date', 5),
|
58
|
+
just('Assgn', 8),
|
59
|
+
just('Tags', 20) ].join(" ")
|
60
|
+
|
61
|
+
puts "-" * cols
|
62
|
+
|
63
|
+
tickets.each do |t|
|
64
|
+
counter += 1
|
65
|
+
tic.current_ticket == t.ticket_name ? add = '*' : add = ' '
|
66
|
+
puts [add, just(counter, 4, 'r'),
|
67
|
+
t.ticket_id[0,6],
|
68
|
+
just(t.title, cols - 56),
|
69
|
+
just(t.state, 5),
|
70
|
+
t.opened.strftime("%m/%d"),
|
71
|
+
just(t.assigned_name, 8),
|
72
|
+
just(t.tags.join(','), 20) ].join(" ")
|
73
|
+
end
|
74
|
+
puts
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module TicGitNG
|
2
|
+
module Command
|
3
|
+
# tic milestone
|
4
|
+
# tic milestone migration1 (list tickets)
|
5
|
+
# tic milestone -n migration1 3/4/08 (new milestone)
|
6
|
+
# tic milestone -a {1} (add ticket to milestone)
|
7
|
+
# tic milestone -d migration1 (delete)
|
8
|
+
module Milestone
|
9
|
+
def parser(opts)
|
10
|
+
opts.banner = "Usage: ti milestone [milestone_name] [options] [date]"
|
11
|
+
|
12
|
+
opts.on_head(
|
13
|
+
"-n MILESTONE", "--new MILESTONE",
|
14
|
+
"Add a new milestone to this project"){|v| options.new = v }
|
15
|
+
|
16
|
+
opts.on_head(
|
17
|
+
"-a TICKET", "--new TICKET",
|
18
|
+
"Add a ticket to this milestone"){|v| options.add = v }
|
19
|
+
|
20
|
+
opts.on_head(
|
21
|
+
"-d MILESTONE", "--delete MILESTONE",
|
22
|
+
"Remove a milestone"){|v| options.remove = v }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module TicGitNG
|
2
|
+
module Command
|
3
|
+
module New
|
4
|
+
def parser(opts)
|
5
|
+
opts.banner = "Usage: ti new [options]"
|
6
|
+
opts.on_head(
|
7
|
+
"-t TITLE", "--title TITLE",
|
8
|
+
"Title to use for the name of the new ticket"){|v| options.title = v }
|
9
|
+
end
|
10
|
+
|
11
|
+
def execute
|
12
|
+
if title = options.title
|
13
|
+
ticket_show(tic.ticket_new(title, options.to_hash))
|
14
|
+
else
|
15
|
+
# interactive
|
16
|
+
message_file = Tempfile.new('ticgit_message').path
|
17
|
+
File.open(message_file, 'w') do |f|
|
18
|
+
f.puts "\n# ---"
|
19
|
+
f.puts "tags:"
|
20
|
+
f.puts "# first line will be the title of the tic, the rest will be the first comment"
|
21
|
+
f.puts "# if you would like to add initial tags, put them on the 'tags:' line, comma delim"
|
22
|
+
end
|
23
|
+
if message = get_editor_message(message_file)
|
24
|
+
title = message.shift
|
25
|
+
if title && title.chomp.length > 0
|
26
|
+
title = title.chomp
|
27
|
+
if message.last[0, 5] == 'tags:'
|
28
|
+
tags = message.pop
|
29
|
+
tags = tags.gsub('tags:', '')
|
30
|
+
tags = tags.split(',').map { |t| t.strip }
|
31
|
+
end
|
32
|
+
if message.size > 0
|
33
|
+
comment = message.join("")
|
34
|
+
end
|
35
|
+
ticket_show(tic.ticket_new(title, :comment => comment, :tags => tags))
|
36
|
+
else
|
37
|
+
puts "You need to at least enter a title"
|
38
|
+
end
|
39
|
+
else
|
40
|
+
puts "It seems you wrote nothing"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module TicGitNG
|
2
|
+
module Command
|
3
|
+
# Assigns points to a ticket
|
4
|
+
#
|
5
|
+
# Usage:
|
6
|
+
# ti points {1} {points} (assigns points to a specified ticket)
|
7
|
+
module Points
|
8
|
+
def parser(opts)
|
9
|
+
opts.banner = "ti points [ticket_id] points"
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute
|
13
|
+
case args.size
|
14
|
+
when 1
|
15
|
+
new_points = args[0].to_i
|
16
|
+
when 2
|
17
|
+
tid = args[0]
|
18
|
+
new_points = args[1].to_i
|
19
|
+
else
|
20
|
+
puts usage
|
21
|
+
exit 1
|
22
|
+
end
|
23
|
+
|
24
|
+
tic.ticket_points(new_points, tid)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module TicGitNG
|
2
|
+
module Command
|
3
|
+
module Recent
|
4
|
+
def parser(opts)
|
5
|
+
opts.banner = 'Usage: ti recent'
|
6
|
+
end
|
7
|
+
|
8
|
+
def execute
|
9
|
+
# "args[0]" seems to be superfluous. It's usage
|
10
|
+
# is undocumented, and supplying an argument
|
11
|
+
# doesn't seem to do anything.
|
12
|
+
#
|
13
|
+
# Im guessing the purpose of args[0] was to provide a
|
14
|
+
# specific ticket_id whos history would be looked up
|
15
|
+
# intead of looking up the history for all tickets.
|
16
|
+
#
|
17
|
+
# #FIXME Reimplement that functionality and updte
|
18
|
+
# docs to match
|
19
|
+
tic.ticket_recent(args[0]).each do |commit|
|
20
|
+
sha = commit.sha[0, 7]
|
21
|
+
date = commit.date.strftime("%m/%d %H:%M")
|
22
|
+
message = commit.message
|
23
|
+
|
24
|
+
puts "#{sha} #{date}\t#{message}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module TicGitNG
|
2
|
+
module Command
|
3
|
+
module Show
|
4
|
+
def parser(opts)
|
5
|
+
opts.banner = "Usage: ti show [--full] [ticid]"
|
6
|
+
opts.on_head(
|
7
|
+
"-f", "--full", "Show long comments in full, don't truncate after the 5th line"){|v|
|
8
|
+
options.full= v
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute
|
13
|
+
t = tic.ticket_show(args[0])
|
14
|
+
ticket_show(t, options.full ) if t
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module TicGitNG
|
2
|
+
module Command
|
3
|
+
module State
|
4
|
+
def parser(opts)
|
5
|
+
opts.banner = "Usage: ti state [ticid] state"
|
6
|
+
end
|
7
|
+
|
8
|
+
def execute
|
9
|
+
if args.size > 1
|
10
|
+
tid, new_state = args[0].strip, args[1].strip
|
11
|
+
|
12
|
+
if valid_state?(new_state)
|
13
|
+
tic.ticket_change(new_state, tid)
|
14
|
+
else
|
15
|
+
puts "Invalid State - please choose from: #{joined_states}"
|
16
|
+
end
|
17
|
+
elsif args.size > 0
|
18
|
+
# new state
|
19
|
+
new_state = args[0].chomp
|
20
|
+
|
21
|
+
if valid_state?(new_state)
|
22
|
+
tic.ticket_change(new_state)
|
23
|
+
else
|
24
|
+
puts "Invalid State - please choose from: #{joined_states}"
|
25
|
+
end
|
26
|
+
else
|
27
|
+
puts 'You need to at least specify a new state for the current ticket'
|
28
|
+
puts "please choose from: #{joined_states}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def valid_state?(state)
|
33
|
+
available_states.include?(state)
|
34
|
+
end
|
35
|
+
|
36
|
+
def available_states
|
37
|
+
tic.tic_states.sort
|
38
|
+
end
|
39
|
+
|
40
|
+
def joined_states
|
41
|
+
available_states.join(', ')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module TicGitNG
|
2
|
+
module Command
|
3
|
+
module Sync
|
4
|
+
def parser(opts)
|
5
|
+
opts.banner = "Usage: ti sync [options]"
|
6
|
+
opts.on_head(
|
7
|
+
"-r REPO", "--repo REPO", "Sync ticgit-ng branch with REPO"){|v|
|
8
|
+
options.repo = v
|
9
|
+
}
|
10
|
+
opts.on_head(
|
11
|
+
"-n", "--no-push", "Do not push to the remote repo"){|v|
|
12
|
+
options.no_push = true
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
def execute
|
17
|
+
if options.repo and options.no_push
|
18
|
+
tic.sync_tickets(options.repo, false)
|
19
|
+
elsif options.repo
|
20
|
+
tic.sync_tickets(options.repo)
|
21
|
+
elsif options.no_push
|
22
|
+
tic.sync_tickets('origin', false)
|
23
|
+
else
|
24
|
+
tic.sync_tickets()
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module TicGitNG
|
2
|
+
module Command
|
3
|
+
module Tag
|
4
|
+
def parser(opts)
|
5
|
+
opts.banner = "Usage: ti tag [tic_id] [options] [tag_name] "
|
6
|
+
opts.on_head(
|
7
|
+
"-d", "--delete",
|
8
|
+
"Remove this tag from the ticket"){|v| options.remove = v }
|
9
|
+
end
|
10
|
+
|
11
|
+
def execute
|
12
|
+
if options.remove
|
13
|
+
puts 'remove'
|
14
|
+
end
|
15
|
+
|
16
|
+
if ARGV.size > 3
|
17
|
+
tid = ARGV[1].chomp
|
18
|
+
tic.ticket_tag(ARGV[3].chomp, tid, options)
|
19
|
+
elsif ARGV.size > 2 #tag
|
20
|
+
tic.ticket_tag(ARGV[2], nil, options)
|
21
|
+
elsif ARGV.size == 2 #tag add 'tag_foobar'
|
22
|
+
tic.ticket_tag(ARGV[1], nil, options)
|
23
|
+
else
|
24
|
+
puts 'You need to at least specify one tag to add'
|
25
|
+
puts
|
26
|
+
puts parser
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|