TicGit-ng 1.0.2.14 → 1.0.2.15
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/lib/ticgit-ng/attachment.rb +56 -0
- data/lib/ticgit-ng/base.rb +65 -26
- data/lib/ticgit-ng/cli.rb +59 -37
- data/lib/ticgit-ng/command/attach.rb +48 -0
- data/lib/ticgit-ng/command/help.rb +27 -0
- data/lib/ticgit-ng/command/list.rb +1 -1
- data/lib/ticgit-ng/command/tag.rb +5 -8
- data/lib/ticgit-ng/command.rb +2 -1
- data/lib/ticgit-ng/comment.rb +10 -8
- data/lib/ticgit-ng/ticket.rb +136 -15
- data/lib/ticgit-ng/version.rb +1 -1
- data/lib/ticgit-ng.rb +4 -2
- metadata +19 -16
@@ -0,0 +1,56 @@
|
|
1
|
+
module TicGitNG
|
2
|
+
class Attachment
|
3
|
+
attr_reader :user, :added, :filename, :sha, :attachment_name
|
4
|
+
attr_reader :original_filename
|
5
|
+
alias :read :initialize
|
6
|
+
|
7
|
+
def initialize( fname )
|
8
|
+
#FIXME expect fname to be a raw filename that needs to be converted
|
9
|
+
# into a properly formatted ticket name
|
10
|
+
@filename=fname
|
11
|
+
trailing_underscore= File.basename(fname)[/_$/] ? true : false
|
12
|
+
trailing_underscore=false
|
13
|
+
temp=File.basename(fname).split('_').reverse
|
14
|
+
@added=Time.at(temp.pop.to_i)
|
15
|
+
@user= temp.pop
|
16
|
+
@attachment_name= temp.reverse.join('_')
|
17
|
+
if trailing_underscore
|
18
|
+
@attachment_name << '_'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
#Called when attaching a new attachment and when reading/opening attachments
|
23
|
+
#FIXME Make a 'read' and 'create' function to differentiate between
|
24
|
+
# between creation of a ticket and reading an existing ticket.
|
25
|
+
def self.create raw_fname, ticket, time
|
26
|
+
#Attachment naming format:
|
27
|
+
#ticket_name/ATTACHMENTS/123456_jeff.welling@gmail.com_fubar.jpg
|
28
|
+
#raw_fname "/home/guy/Desktop/fubar.jpg"
|
29
|
+
|
30
|
+
#create attachment dir if first run
|
31
|
+
a_name= File.expand_path( File.join(
|
32
|
+
File.join( ticket.ticket_name, 'ATTACHMENTS' ),
|
33
|
+
ticket.create_attachment_name(raw_fname, time)
|
34
|
+
))
|
35
|
+
#create new filename from ticket
|
36
|
+
if File.exist?( File.dirname( a_name ) ) &&
|
37
|
+
!File.directory?( File.dirname(a_name) )
|
38
|
+
|
39
|
+
puts "Could not create ATTACHMENTS directory"
|
40
|
+
exit 1
|
41
|
+
elsif !File.exist?( File.dirname( a_name ) )
|
42
|
+
Dir.mkdir File.dirname( a_name )
|
43
|
+
end
|
44
|
+
#copy/link the raw_filename
|
45
|
+
Dir.chdir( File.dirname(a_name) ) do
|
46
|
+
FileUtils.cp( raw_fname, a_name )
|
47
|
+
end
|
48
|
+
#call init on the new file to properly populate the variables
|
49
|
+
a_name= File.join(
|
50
|
+
File.basename( File.dirname( a_name ) ),
|
51
|
+
File.basename( a_name )
|
52
|
+
)
|
53
|
+
return Attachment.new( a_name )
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/ticgit-ng/base.rb
CHANGED
@@ -10,6 +10,14 @@ module TicGitNG
|
|
10
10
|
|
11
11
|
def initialize(git_dir, opts = {})
|
12
12
|
@git = Git.open(find_repo(git_dir))
|
13
|
+
|
14
|
+
begin
|
15
|
+
git.lib.full_log_commits 1
|
16
|
+
rescue
|
17
|
+
puts "Git error: please check your git repository, you must have at least one commit in git"
|
18
|
+
exit 1
|
19
|
+
end
|
20
|
+
|
13
21
|
@logger = opts[:logger] || Logger.new(STDOUT)
|
14
22
|
|
15
23
|
#This is to accomodate for edge-cases where @logger.puts
|
@@ -65,6 +73,22 @@ module TicGitNG
|
|
65
73
|
|
66
74
|
@state = File.expand_path(File.join(@tic_dir, proj, 'state'))
|
67
75
|
|
76
|
+
branches=git.lib.branches_all.map{|b|b.first}
|
77
|
+
unless branches.include?(which_branch?) && File.directory?(@tic_working)
|
78
|
+
if branches.include?(which_branch?) and !File.exist?(@tic_working)
|
79
|
+
#branch exists but tic_working doesn't
|
80
|
+
#this could be because the dir was deleted or the repo itself
|
81
|
+
#was moved, so recreate the dir.
|
82
|
+
reset_ticgitng
|
83
|
+
elsif @init
|
84
|
+
puts "Initializing TicGit-ng"
|
85
|
+
init_ticgitng_branch( branches.include?(which_branch?) )
|
86
|
+
else
|
87
|
+
puts "Please run `ti init` to initialize TicGit-ng for this repository before running other ti commands."
|
88
|
+
exit
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
68
92
|
if File.file?(@state)
|
69
93
|
load_state
|
70
94
|
else
|
@@ -84,6 +108,9 @@ module TicGitNG
|
|
84
108
|
# save config file
|
85
109
|
def save_state
|
86
110
|
state_list = [@last_tickets, @current_ticket]
|
111
|
+
unless File.exist? @state
|
112
|
+
FileUtils.mkdir_p( File.dirname(@state) )
|
113
|
+
end
|
87
114
|
File.open(@state, 'w+'){|io| Marshal.dump(state_list, io) }
|
88
115
|
File.open(@config_file, 'w+'){|io| io.write(config.to_yaml) }
|
89
116
|
end
|
@@ -103,8 +130,8 @@ module TicGitNG
|
|
103
130
|
end
|
104
131
|
|
105
132
|
# returns new Ticket
|
106
|
-
def ticket_new(title, options = {})
|
107
|
-
t = TicGitNG::Ticket.create(self, title, options)
|
133
|
+
def ticket_new(title, options = {}, time=nil)
|
134
|
+
t = TicGitNG::Ticket.create(self, title, options, time)
|
108
135
|
reset_ticgitng
|
109
136
|
TicGitNG::Ticket.open(self, t.ticket_name, tickets[t.ticket_name])
|
110
137
|
end
|
@@ -122,6 +149,7 @@ module TicGitNG
|
|
122
149
|
ticket = TicGitNG::Ticket.open(self, t, tickets[t])
|
123
150
|
ticket.add_comment(comment)
|
124
151
|
reset_ticgitng
|
152
|
+
ticket
|
125
153
|
end
|
126
154
|
end
|
127
155
|
|
@@ -264,6 +292,7 @@ module TicGitNG
|
|
264
292
|
ticket.add_tag(tag)
|
265
293
|
end
|
266
294
|
reset_ticgitng
|
295
|
+
ticket
|
267
296
|
end
|
268
297
|
end
|
269
298
|
|
@@ -273,6 +302,7 @@ module TicGitNG
|
|
273
302
|
ticket = TicGitNG::Ticket.open(self, t, tickets[t])
|
274
303
|
ticket.change_state(new_state)
|
275
304
|
reset_ticgitng
|
305
|
+
ticket
|
276
306
|
end
|
277
307
|
end
|
278
308
|
end
|
@@ -282,6 +312,7 @@ module TicGitNG
|
|
282
312
|
ticket = TicGitNG::Ticket.open(self, t, tickets[t])
|
283
313
|
ticket.change_assigned(new_assigned)
|
284
314
|
reset_ticgitng
|
315
|
+
ticket
|
285
316
|
end
|
286
317
|
end
|
287
318
|
|
@@ -290,6 +321,7 @@ module TicGitNG
|
|
290
321
|
ticket = TicGitNG::Ticket.open(self, t, tickets[t])
|
291
322
|
ticket.change_points(new_points)
|
292
323
|
reset_ticgitng
|
324
|
+
ticket
|
293
325
|
end
|
294
326
|
end
|
295
327
|
|
@@ -298,15 +330,10 @@ module TicGitNG
|
|
298
330
|
ticket = TicGitNG::Ticket.open(self, t, tickets[t])
|
299
331
|
@current_ticket = ticket.ticket_name
|
300
332
|
save_state
|
333
|
+
ticket
|
301
334
|
end
|
302
335
|
end
|
303
336
|
|
304
|
-
def comment_add(ticket_id, comment, options = {})
|
305
|
-
end
|
306
|
-
|
307
|
-
def comment_list(ticket_id)
|
308
|
-
end
|
309
|
-
|
310
337
|
def tic_states
|
311
338
|
['open', 'resolved', 'invalid', 'hold']
|
312
339
|
end
|
@@ -334,30 +361,22 @@ module TicGitNG
|
|
334
361
|
def read_tickets
|
335
362
|
tickets = {}
|
336
363
|
|
337
|
-
bs = git.lib.branches_all.map{|b| b.first }
|
338
|
-
|
339
|
-
unless (bs.include?(which_branch?) || bs.include?(which_branch?)) &&
|
340
|
-
File.directory?(@tic_working)
|
341
|
-
unless @init
|
342
|
-
puts "Please run `ti init` to initialize TicGit-ng for this repository before running other ti commands."
|
343
|
-
exit
|
344
|
-
else
|
345
|
-
puts "Initializing TicGit-ng"
|
346
|
-
init_ticgitng_branch(
|
347
|
-
git.lib.branches_all.map{|b| b.first }.include?(which_branch?)
|
348
|
-
)
|
349
|
-
end
|
350
|
-
end
|
351
|
-
|
352
364
|
tree = git.lib.full_tree(which_branch?)
|
353
365
|
tree.each do |t|
|
354
366
|
data, file = t.split("\t")
|
355
367
|
mode, type, sha = data.split(" ")
|
356
368
|
tic = file.split('/')
|
357
369
|
if tic.size == 2 # directory depth
|
358
|
-
|
359
|
-
|
360
|
-
|
370
|
+
ticket, info = tic
|
371
|
+
tickets[ticket] ||= { 'files' => [] }
|
372
|
+
tickets[ticket]['files'] << [info, sha]
|
373
|
+
elsif tic.size == 3
|
374
|
+
ticket, attch_dir, filename = tic
|
375
|
+
if filename
|
376
|
+
filename = 'ATTACHMENTS_' + filename
|
377
|
+
tickets[ticket] ||= { 'files' => [] }
|
378
|
+
tickets[ticket]['files'] << [filename, sha]
|
379
|
+
end
|
361
380
|
end
|
362
381
|
end
|
363
382
|
tickets
|
@@ -410,7 +429,9 @@ module TicGitNG
|
|
410
429
|
def new_file(name, contents)
|
411
430
|
File.open(name, 'w+'){|f| f.puts(contents) }
|
412
431
|
end
|
432
|
+
|
413
433
|
def which_branch?
|
434
|
+
#At present use the 'ticgit' branch for backwards compatibility
|
414
435
|
branches=@git.branches.local.map {|b| b.name}
|
415
436
|
if branches.include? 'ticgit-ng'
|
416
437
|
return 'ticgit-ng'
|
@@ -436,6 +457,24 @@ module TicGitNG
|
|
436
457
|
def needs_reset? cache_mtime, gitlog_mtime
|
437
458
|
((cache_mtime.to_i - gitlog_mtime.to_i) > 120) or ((gitlog_mtime.to_i - cache_mtime.to_i) > 120)
|
438
459
|
end
|
460
|
+
|
461
|
+
def ticket_attach filename, ticket_id=nil, time=nil
|
462
|
+
t = ticket_revparse( ticket_id )
|
463
|
+
ticket= TicGitNG::Ticket.open( self, t, tickets[t] )
|
464
|
+
ticket.add_attach( self, filename, time )
|
465
|
+
reset_ticgitng
|
466
|
+
ticket
|
467
|
+
end
|
439
468
|
|
469
|
+
#ticket_get_attachment()
|
470
|
+
#file_id - return the attachment identified by file_id
|
471
|
+
#new_filename - save the attachment as new_filename
|
472
|
+
#ticket_id - get the attachment from ticket_id instead of current
|
473
|
+
def ticket_get_attachment file_id=nil, new_filename=nil, ticket_id=nil
|
474
|
+
if t = ticket_revparse(ticket_id)
|
475
|
+
ticket = Ticket.open( self, t, tickets[t] )
|
476
|
+
ticket.get_attach( file_id, new_filename )
|
477
|
+
end
|
478
|
+
end
|
440
479
|
end
|
441
480
|
end
|
data/lib/ticgit-ng/cli.rb
CHANGED
@@ -110,44 +110,62 @@ module TicGitNG
|
|
110
110
|
end
|
111
111
|
|
112
112
|
def ticket_show(t, more=nil)
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
unless t.comments.empty?
|
132
|
-
puts "Comments (#{t.comments.size}):"
|
133
|
-
t.comments.reverse_each do |c|
|
134
|
-
puts " * Added #{c.added.strftime('%m/%d %H:%M')} by #{c.user}"
|
135
|
-
|
136
|
-
wrapped = c.comment.split("\n").map{|line|
|
137
|
-
line.length > 80 ? line.gsub(/(.{1,80})(\s+|$)/, "\\1\n").strip : line
|
138
|
-
}.join("\n")
|
139
|
-
|
140
|
-
wrapped = wrapped.split("\n").map{|line| "\t#{line}" }
|
113
|
+
days_ago = ((Time.now - t.opened) / (60 * 60 * 24)).round
|
114
|
+
|
115
|
+
data = [
|
116
|
+
['Title', t.title],
|
117
|
+
['TicId', t.ticket_id],
|
118
|
+
'',
|
119
|
+
['Assigned', t.assigned],
|
120
|
+
['Opened', "#{t.opened} (#{days_ago} days)"],
|
121
|
+
['State', t.state.upcase],
|
122
|
+
['Points', t.points || 'no estimate'],
|
123
|
+
['Tags', t.tags.join(', ')],
|
124
|
+
''
|
125
|
+
]
|
126
|
+
|
127
|
+
data.each do |(key, value)|
|
128
|
+
puts(value ? "#{key}: #{value}" : key)
|
129
|
+
end
|
141
130
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
131
|
+
#FIXME display attachments inline chronologically with comments
|
132
|
+
unless t.comments.empty? and t.attachments.empty?
|
133
|
+
comments_and_attachments= Hash.new
|
134
|
+
puts "Comments and attachments (#{t.comments.size + t.attachments.size}):"
|
135
|
+
t.comments.each do |c|
|
136
|
+
comments_and_attachments[c.added]=c
|
137
|
+
end
|
138
|
+
|
139
|
+
t.attachments.each do |a|
|
140
|
+
comments_and_attachments[a.added]=a
|
141
|
+
end
|
142
|
+
comments_and_attachments.sort.each {|item|
|
143
|
+
if item[1].class==TicGitNG::Comment
|
144
|
+
#print comment
|
145
|
+
puts " * Added #{item[1].added.strftime('%m/%d %H:%M')} by #{item[1].user}"
|
146
|
+
|
147
|
+
wrapped = item[1].comment.split("\n").map{|line|
|
148
|
+
line.length > 80 ? line.gsub(/(.{1,80})(\s+|$)/, "\\1\n").strip : line
|
149
|
+
}.join("\n")
|
150
|
+
|
151
|
+
wrapped = wrapped.split("\n").map{|line| "\t#{line}" }
|
152
|
+
|
153
|
+
if wrapped.size > 6 and more.nil?
|
154
|
+
puts wrapped[0, 6].join("\n")
|
155
|
+
puts "\t** more... **"
|
156
|
+
else
|
157
|
+
puts wrapped.join("\n")
|
158
|
+
end
|
159
|
+
puts
|
160
|
+
else
|
161
|
+
#print attachment
|
162
|
+
puts " * Added #{item[1].added.strftime('%m/%d %H:%M')} by #{item[1].user}"
|
163
|
+
puts " Attachment: #{t.attachments.index(item[1]) }"
|
164
|
+
puts " Filename: #{item[1].attachment_name}"
|
165
|
+
puts
|
166
|
+
end
|
167
|
+
}
|
149
168
|
end
|
150
|
-
end
|
151
169
|
end
|
152
170
|
|
153
171
|
class << self
|
@@ -254,4 +272,8 @@ module TicGitNG
|
|
254
272
|
end
|
255
273
|
|
256
274
|
TicGitNG::CLI.reset_window_width
|
257
|
-
|
275
|
+
begin
|
276
|
+
Signal.trap("SIGWINCH") { TicGitNG::CLI.reset_window_width }
|
277
|
+
rescue
|
278
|
+
TicGitNG::CLI.use_fallback
|
279
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module TicGitNG
|
2
|
+
module Command
|
3
|
+
# Attach a file to a ticket
|
4
|
+
#
|
5
|
+
# Usage:
|
6
|
+
# ti attach
|
7
|
+
# (print help for 'ti attach')
|
8
|
+
#
|
9
|
+
# ti attach {filename}
|
10
|
+
# (attach {filename} to current ticket)
|
11
|
+
#
|
12
|
+
# ti attach -i {ID} {filename}
|
13
|
+
# (attach file {filename} to ticket with ID {ID})
|
14
|
+
#
|
15
|
+
# ti attach -g {f_id}
|
16
|
+
# (retrieve attached file {f_id}, place in current dir}
|
17
|
+
#
|
18
|
+
# ti attach -g {f_id} -n {new_filename}
|
19
|
+
# (retrieve attached file {f_id}, place as {new_filename})
|
20
|
+
module Attach
|
21
|
+
def parser(opts)
|
22
|
+
opts.banner = "Usage: ti attach [options] [filename]"
|
23
|
+
|
24
|
+
opts.on_head(
|
25
|
+
"-i TICKET_ID", "--id TICKET_ID", "Attach the file to this ticket"){|v|
|
26
|
+
options.ticket_id = v
|
27
|
+
}
|
28
|
+
opts.on_head(
|
29
|
+
"-g FILE_ID", "--get FILE_ID", "Retrieve the file FILE_ID"){|v|
|
30
|
+
puts "Warning: ticket ID argument is not valid with the retrieve attachment argument" if options.ticket_id
|
31
|
+
options.get_file = v
|
32
|
+
}
|
33
|
+
opts.on_head(
|
34
|
+
"-n N_FILENAME", "--new-filename", "Use this filename for the retrieved attachment"){|v|
|
35
|
+
raise ArgumentError, "Error: New filename argument is only valid with the retrieve arrachment argument" unless options.get_file
|
36
|
+
options.new_filename = v
|
37
|
+
}
|
38
|
+
end
|
39
|
+
def execute
|
40
|
+
if options.get_file
|
41
|
+
tic.ticket_get_attachment( options.get_file, options.new_filename )
|
42
|
+
else
|
43
|
+
tic.ticket_attach( args[0], options.ticket_id )
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module TicGitNG
|
2
|
+
module Command
|
3
|
+
# Shows help for ticgit or a particular command
|
4
|
+
#
|
5
|
+
# Usage:
|
6
|
+
# ti help (show help for ticgit)
|
7
|
+
# ti help {command} (show help for specified command)
|
8
|
+
module Help
|
9
|
+
def parser(opts)
|
10
|
+
opts.banner = "Usage: ti help [command]\n"
|
11
|
+
end
|
12
|
+
def execute
|
13
|
+
cli = CLI.new([])
|
14
|
+
if ARGV.length >= 2 # ti help {command}
|
15
|
+
action = ARGV[1]
|
16
|
+
if command = Command.get(action)
|
17
|
+
cli.extend(command)
|
18
|
+
else
|
19
|
+
puts "Unknown command #{action}\n\n"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
puts cli.usage
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
@@ -6,7 +6,7 @@ module TicGitNG
|
|
6
6
|
o.banner = "Usage: ti list [options]"
|
7
7
|
o.on_head(
|
8
8
|
"-o ORDER", "--order ORDER",
|
9
|
-
"Field to order by - one of : assigned,state,date,title"){|v|
|
9
|
+
"Field to order by - one of : assigned,state,date,title [.desc for reverse order]"){|v|
|
10
10
|
options.order = v
|
11
11
|
}
|
12
12
|
|
@@ -13,17 +13,14 @@ module TicGitNG
|
|
13
13
|
puts 'remove'
|
14
14
|
end
|
15
15
|
|
16
|
-
if ARGV.size >
|
17
|
-
|
18
|
-
|
19
|
-
|
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)
|
16
|
+
if ARGV.size > 2 # `ti tag 1234abc tagname1`
|
17
|
+
tic.ticket_tag(ARGV[2], ARGV[1].chomp, options)
|
18
|
+
elsif ARGV.size == 2 # `ti tag tagname1`
|
19
|
+
tic.ticket_tag(ARGV[1], nil, options)
|
23
20
|
else
|
24
21
|
puts 'You need to at least specify one tag to add'
|
25
22
|
puts
|
26
|
-
puts
|
23
|
+
puts usage
|
27
24
|
end
|
28
25
|
end
|
29
26
|
end
|
data/lib/ticgit-ng/command.rb
CHANGED
@@ -13,8 +13,9 @@ module TicGitNG
|
|
13
13
|
register 'Attach', 'Attach file to ticket', 'attach'
|
14
14
|
register 'Checkout', 'Checkout a ticket', 'checkout', 'co'
|
15
15
|
register 'Comment', 'Comment on a ticket', 'comment'
|
16
|
+
register 'Help', 'Show help for a ticgit command', 'help'
|
16
17
|
register 'List', 'List tickets', 'list'
|
17
|
-
register 'Milestone', 'List and modify milestones', 'milestone'
|
18
|
+
# register 'Milestone', 'List and modify milestones', 'milestone'
|
18
19
|
register 'New', 'Create a new ticket', 'new'
|
19
20
|
register 'Points', 'Assign points to a ticket', 'points'
|
20
21
|
register 'Recent', 'List recent activities', 'recent'
|
data/lib/ticgit-ng/comment.rb
CHANGED
@@ -1,17 +1,19 @@
|
|
1
1
|
module TicGitNG
|
2
2
|
class Comment
|
3
|
+
attr_accessor :base, :user, :added, :comment
|
3
4
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
@
|
8
|
-
@
|
5
|
+
def initialize( c, user, time=nil )
|
6
|
+
raise unless c
|
7
|
+
@comment= c
|
8
|
+
@user=user
|
9
|
+
@added= time.nil? ? Time.now : time
|
10
|
+
self
|
11
|
+
end
|
9
12
|
|
13
|
+
def self.read( base, file_name, sha )
|
10
14
|
type, date, user = file_name.split('_')
|
11
15
|
|
12
|
-
|
13
|
-
@user = user
|
16
|
+
new( (base.git.gblob(sha).contents rescue nil), user, Time.at(date.to_i) )
|
14
17
|
end
|
15
|
-
|
16
18
|
end
|
17
19
|
end
|
data/lib/ticgit-ng/ticket.rb
CHANGED
@@ -25,10 +25,10 @@ module TicGitNG
|
|
25
25
|
@attachments = []
|
26
26
|
end
|
27
27
|
|
28
|
-
def self.create(base, title, options = {})
|
28
|
+
def self.create(base, title, options = {}, time=nil)
|
29
29
|
t = Ticket.new(base, options)
|
30
30
|
t.title = title
|
31
|
-
t.ticket_name = self.create_ticket_name(title)
|
31
|
+
t.ticket_name = self.create_ticket_name(title, time)
|
32
32
|
t.save_new
|
33
33
|
t
|
34
34
|
end
|
@@ -42,39 +42,46 @@ module TicGitNG
|
|
42
42
|
title, date = self.parse_ticket_name(ticket_name)
|
43
43
|
t.opened = date
|
44
44
|
|
45
|
-
ticket_hash['files'].each do |fname,
|
45
|
+
ticket_hash['files'].each do |fname, sha|
|
46
46
|
if fname == 'TICKET_ID'
|
47
|
-
tid =
|
47
|
+
tid = sha
|
48
48
|
elsif fname == 'TICKET_TITLE'
|
49
|
-
t.title = base.git.gblob(
|
49
|
+
t.title = base.git.gblob(sha).contents
|
50
50
|
else
|
51
51
|
# matching
|
52
52
|
data = fname.split('_')
|
53
53
|
|
54
54
|
case data[0]
|
55
55
|
when 'ASSIGNED'
|
56
|
-
t.assigned = data[1]
|
57
|
-
when '
|
58
|
-
|
56
|
+
t.assigned = data[1..-1].join('_')
|
57
|
+
when 'ATTACHMENTS'
|
58
|
+
#Attachments dir naming format:
|
59
|
+
#ticket_name/ATTACHMENTS/123456_jeff.welling@gmail.com_fubar.jpg
|
60
|
+
#data[] format:
|
61
|
+
#"ATTACHMENTS_1342116799_jeff.welling@gmail.com_Rakefile".split('_')
|
62
|
+
filename=File.join( 'ATTACHMENTS', fname.gsub(/^ATTACHMENTS_/,'') )
|
63
|
+
t.attachments << TicGitNG::Attachment.new( filename )
|
59
64
|
when 'COMMENT'
|
60
|
-
t.comments << TicGitNG::Comment.
|
65
|
+
t.comments << TicGitNG::Comment.read(base, fname, sha)
|
61
66
|
when 'POINTS'
|
62
|
-
t.points = base.git.gblob(
|
67
|
+
t.points = base.git.gblob(sha).contents.to_i
|
63
68
|
when 'STATE'
|
64
69
|
t.state = data[1]
|
65
70
|
when 'TAG'
|
66
71
|
t.tags << data[1]
|
67
72
|
when 'TITLE'
|
68
|
-
t.title = base.git.gblob(
|
73
|
+
t.title = base.git.gblob(sha).contents
|
69
74
|
end
|
70
75
|
end
|
71
76
|
end
|
72
77
|
|
78
|
+
if !t.attachments.class==NilClass and t.attachments.size > 1
|
79
|
+
t.attachments= t.attachments.sort {|a1, a2| a1.added <=> a2.added }
|
80
|
+
end
|
73
81
|
t.ticket_id = tid
|
74
82
|
t
|
75
83
|
end
|
76
84
|
|
77
|
-
|
78
85
|
def self.parse_ticket_name(name)
|
79
86
|
epoch, title, rand = name.split('_')
|
80
87
|
title = title.gsub('-', ' ')
|
@@ -144,6 +151,89 @@ module TicGitNG
|
|
144
151
|
base.git.add File.join(ticket_name, t)
|
145
152
|
base.git.commit("added comment to ticket #{ticket_name}")
|
146
153
|
end
|
154
|
+
@comments << TicGitNG::Comment.new( comment, email )
|
155
|
+
self
|
156
|
+
end
|
157
|
+
|
158
|
+
def add_attach( base, filename, time=nil )
|
159
|
+
filename=File.expand_path(filename)
|
160
|
+
#FIXME Refactor -- Attachment.new should be called from Ticket.rb
|
161
|
+
# -- Attachment filename creation should be handled
|
162
|
+
# by the Attachment.rb code
|
163
|
+
base.in_branch do |wd|
|
164
|
+
attachments << (a=TicGitNG::Attachment.create( filename, self, time))
|
165
|
+
base.git.add File.join( ticket_name, a.filename )
|
166
|
+
base.git.commit("added attachment #{File.basename(a.filename)} to ticket #{ticket_name}")
|
167
|
+
end
|
168
|
+
if attachments.class!=NilClass and attachments.size > 1
|
169
|
+
@attachments=attachments.sort {|a1,a2| a1.added <=> a2.added }
|
170
|
+
end
|
171
|
+
self
|
172
|
+
end
|
173
|
+
|
174
|
+
#file_id can be one of:
|
175
|
+
# - An index number of the attachment (1,2,3,...)
|
176
|
+
# - A filename (fubar.jpg)
|
177
|
+
# - nil (nil) means use the last attachment
|
178
|
+
#
|
179
|
+
#if new_filename is nil, use existing filename
|
180
|
+
def get_attach file_id=nil, new_filename=nil
|
181
|
+
attachment=nil
|
182
|
+
pwd=Dir.pwd
|
183
|
+
base.in_branch do |wd|
|
184
|
+
if file_id.to_i==0 and (file_id=="0" or file_id.class==Fixnum)
|
185
|
+
if !attachments[file_id.to_i].nil?
|
186
|
+
attachment= attachments[0]
|
187
|
+
else
|
188
|
+
puts "No attachments match file id #{file_id}"
|
189
|
+
exit 1
|
190
|
+
end
|
191
|
+
elsif file_id.to_i > 0
|
192
|
+
if !attachments[file_id.to_i].nil?
|
193
|
+
attachment= attachments[file_id.to_i]
|
194
|
+
else
|
195
|
+
puts "No attachments match file id #{file_id}"
|
196
|
+
exit 1
|
197
|
+
end
|
198
|
+
else
|
199
|
+
#find attachment by filename
|
200
|
+
attachments.each {|a|
|
201
|
+
attachment=a if a.attachment_name==file_id
|
202
|
+
}
|
203
|
+
if attachment.nil?
|
204
|
+
puts "No attachments match filename #{file_id}"
|
205
|
+
exit 1
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
if !new_filename
|
210
|
+
#if no filename is specified...
|
211
|
+
filename= attachment.attachment_name
|
212
|
+
else
|
213
|
+
#if there is a new_filename given
|
214
|
+
if File.exist?( new_filename ) and File.directory?( new_filename )
|
215
|
+
#if it is a directory, not a filename
|
216
|
+
filename= File.join(
|
217
|
+
new_filename,
|
218
|
+
File.basename(attachment.attachment_name)
|
219
|
+
)
|
220
|
+
else
|
221
|
+
#if it is a filename, not a dir
|
222
|
+
filename= new_filename
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
unless File.exist?( File.dirname(filename) )
|
227
|
+
FileUtils.mkdir_p( File.dirname(filename) )
|
228
|
+
end
|
229
|
+
#save attachment [as new_filename]
|
230
|
+
t=File.join( ticket_name, attachment.filename )
|
231
|
+
unless filename[/^\//]
|
232
|
+
filename=File.join( pwd, filename )
|
233
|
+
end
|
234
|
+
FileUtils.cp( t, filename )
|
235
|
+
end
|
236
|
+
self
|
147
237
|
end
|
148
238
|
|
149
239
|
def change_state(new_state)
|
@@ -159,6 +249,8 @@ module TicGitNG
|
|
159
249
|
base.git.add File.join(ticket_name, t)
|
160
250
|
base.git.commit("added state (#{new_state}) to ticket #{ticket_name}")
|
161
251
|
end
|
252
|
+
@state=new_state
|
253
|
+
self
|
162
254
|
end
|
163
255
|
|
164
256
|
def change_assigned(new_assigned)
|
@@ -175,6 +267,8 @@ module TicGitNG
|
|
175
267
|
base.git.add File.join(ticket_name,t)
|
176
268
|
base.git.commit("assigned #{new_assigned} to ticket #{ticket_name}")
|
177
269
|
end
|
270
|
+
@assigned=new_assigned
|
271
|
+
self
|
178
272
|
end
|
179
273
|
|
180
274
|
def change_points(new_points)
|
@@ -187,6 +281,8 @@ module TicGitNG
|
|
187
281
|
base.git.add File.join(ticket_name, 'POINTS')
|
188
282
|
base.git.commit("set points to #{new_points} for ticket #{ticket_name}")
|
189
283
|
end
|
284
|
+
@points=new_points
|
285
|
+
self
|
190
286
|
end
|
191
287
|
|
192
288
|
def add_tag(tag)
|
@@ -214,6 +310,10 @@ module TicGitNG
|
|
214
310
|
base.git.commit("added tags (#{tag}) to ticket #{ticket_name}")
|
215
311
|
end
|
216
312
|
end
|
313
|
+
tags.each {|tag|
|
314
|
+
@tags << tag
|
315
|
+
}
|
316
|
+
self
|
217
317
|
end
|
218
318
|
|
219
319
|
def remove_tag(tag)
|
@@ -232,6 +332,8 @@ module TicGitNG
|
|
232
332
|
base.git.commit("removed tags (#{tag}) from ticket #{ticket_name}")
|
233
333
|
end
|
234
334
|
end
|
335
|
+
@tags.delete_if {|t| t==tag }
|
336
|
+
self
|
235
337
|
end
|
236
338
|
|
237
339
|
def path
|
@@ -250,10 +352,29 @@ module TicGitNG
|
|
250
352
|
assigned.split('@').first rescue ''
|
251
353
|
end
|
252
354
|
|
253
|
-
def self.create_ticket_name(title)
|
254
|
-
[Time.now.to_i.to_s, Ticket.clean_string(title), rand(999).to_i.to_s].join('_')
|
355
|
+
def self.create_ticket_name(title, time=nil)
|
356
|
+
[time.nil? ? Time.now.to_i.to_s : time, Ticket.clean_string(title), rand(999).to_i.to_s].join('_')
|
255
357
|
end
|
256
358
|
|
257
|
-
|
359
|
+
def create_attachment_name( attachment_name, time=nil )
|
360
|
+
raise ArgumentError, "create_attachment_name( ) only takes a string" unless attachment_name.class==String
|
361
|
+
if time
|
362
|
+
if time.to_i == 0
|
363
|
+
raise ArgumentError, "argument 'time' is not valid" unless time.class==Fixnum
|
364
|
+
else
|
365
|
+
time=time.to_i
|
366
|
+
end
|
367
|
+
end
|
368
|
+
time or time=Time.now.to_i
|
369
|
+
time.to_s+'_'+email+'_'+File.basename( attachment_name )
|
370
|
+
end
|
371
|
+
def ==(ticket2)
|
372
|
+
self.instance_variables.each {|instance_var|
|
373
|
+
unless send( instance_var.gsub('@','').to_sym ) == ticket2.instance_eval {send(instance_var.gsub('@','').to_sym)}
|
374
|
+
return false
|
375
|
+
end
|
376
|
+
}
|
377
|
+
return true
|
378
|
+
end
|
258
379
|
end
|
259
380
|
end
|
data/lib/ticgit-ng/version.rb
CHANGED
data/lib/ticgit-ng.rb
CHANGED
@@ -4,6 +4,7 @@ require 'optparse'
|
|
4
4
|
require 'ostruct'
|
5
5
|
require 'set'
|
6
6
|
require 'yaml'
|
7
|
+
require 'pp'
|
7
8
|
|
8
9
|
# Add the directory containing this file to the start of the load path if it
|
9
10
|
# isn't there already.
|
@@ -15,12 +16,12 @@ require 'rubygems'
|
|
15
16
|
require 'git'
|
16
17
|
|
17
18
|
#Only redefine if we are not using 1.9
|
18
|
-
unless Config::CONFIG["ruby_version"][/^\d\.9/]
|
19
|
+
unless (defined?(RbConfig) ? RbConfig : Config)::CONFIG["ruby_version"][/^\d\.9/]
|
19
20
|
# FIXME: Monkeypatch git until fixed upstream
|
20
21
|
module Git
|
21
22
|
class Lib
|
22
23
|
def config_get(name)
|
23
|
-
do_get = lambda do |
|
24
|
+
do_get = lambda do |x|
|
24
25
|
command('config', ['--get', name])
|
25
26
|
end
|
26
27
|
if @git_dir
|
@@ -35,6 +36,7 @@ end
|
|
35
36
|
|
36
37
|
require 'ticgit-ng/base'
|
37
38
|
require 'ticgit-ng/cli'
|
39
|
+
require 'ticgit-ng/attachment'
|
38
40
|
module TicGitNG
|
39
41
|
autoload :VERSION, 'ticgit-ng/version'
|
40
42
|
autoload :Comment, 'ticgit-ng/comment'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: TicGit-ng
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 73
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 0
|
9
9
|
- 2
|
10
|
-
-
|
11
|
-
version: 1.0.2.
|
10
|
+
- 15
|
11
|
+
version: 1.0.2.15
|
12
12
|
platform: ruby
|
13
13
|
authors:
|
14
14
|
- Scott Chacon
|
@@ -17,7 +17,7 @@ autorequire:
|
|
17
17
|
bindir: bin
|
18
18
|
cert_chain: []
|
19
19
|
|
20
|
-
date: 2012-
|
20
|
+
date: 2012-08-12 00:00:00 Z
|
21
21
|
dependencies:
|
22
22
|
- !ruby/object:Gem::Dependency
|
23
23
|
name: git
|
@@ -58,28 +58,31 @@ extensions: []
|
|
58
58
|
extra_rdoc_files: []
|
59
59
|
|
60
60
|
files:
|
61
|
-
- bin/ti
|
62
61
|
- bin/ticgitweb
|
62
|
+
- bin/ti
|
63
63
|
- lib/ticgit-ng.rb
|
64
|
-
- lib/ticgit-ng/comment.rb
|
65
64
|
- lib/ticgit-ng/cli.rb
|
66
|
-
- lib/ticgit-ng/
|
67
|
-
- lib/ticgit-ng/command/
|
68
|
-
- lib/ticgit-ng/command/
|
69
|
-
- lib/ticgit-ng/command/init.rb
|
65
|
+
- lib/ticgit-ng/attachment.rb
|
66
|
+
- lib/ticgit-ng/command/milestone.rb
|
67
|
+
- lib/ticgit-ng/command/checkout.rb
|
70
68
|
- lib/ticgit-ng/command/state.rb
|
71
69
|
- lib/ticgit-ng/command/list.rb
|
70
|
+
- lib/ticgit-ng/command/assign.rb
|
72
71
|
- lib/ticgit-ng/command/sync.rb
|
73
|
-
- lib/ticgit-ng/command/
|
72
|
+
- lib/ticgit-ng/command/comment.rb
|
74
73
|
- lib/ticgit-ng/command/points.rb
|
75
|
-
- lib/ticgit-ng/command/assign.rb
|
76
|
-
- lib/ticgit-ng/command/milestone.rb
|
77
74
|
- lib/ticgit-ng/command/tag.rb
|
78
|
-
- lib/ticgit-ng/command/
|
75
|
+
- lib/ticgit-ng/command/show.rb
|
76
|
+
- lib/ticgit-ng/command/help.rb
|
77
|
+
- lib/ticgit-ng/command/recent.rb
|
78
|
+
- lib/ticgit-ng/command/attach.rb
|
79
|
+
- lib/ticgit-ng/command/init.rb
|
80
|
+
- lib/ticgit-ng/command/new.rb
|
81
|
+
- lib/ticgit-ng/comment.rb
|
79
82
|
- lib/ticgit-ng/version.rb
|
83
|
+
- lib/ticgit-ng/ticket.rb
|
80
84
|
- lib/ticgit-ng/command.rb
|
81
85
|
- lib/ticgit-ng/base.rb
|
82
|
-
- lib/ticgit-ng/ticket.rb
|
83
86
|
- LICENSE_MIT
|
84
87
|
- LICENSE_GPL
|
85
88
|
- README.mkd
|
@@ -112,7 +115,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
112
115
|
requirements: []
|
113
116
|
|
114
117
|
rubyforge_project: ticgit-ng
|
115
|
-
rubygems_version: 1.
|
118
|
+
rubygems_version: 1.8.15
|
116
119
|
signing_key:
|
117
120
|
specification_version: 3
|
118
121
|
summary: Git based distributed ticketing system
|