configurability 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/rake/svn.rb ADDED
@@ -0,0 +1,668 @@
1
+ #
2
+ # Subversion Rake Tasks
3
+
4
+ #
5
+ # Authors:
6
+ # * Michael Granger <ged@FaerieMUD.org>
7
+ #
8
+
9
+ require 'pp'
10
+ require 'yaml'
11
+ require 'date'
12
+ require 'time'
13
+ require 'abbrev'
14
+
15
+ unless defined?( SVN_DOTDIR )
16
+
17
+ # Subversion constants -- directory names for releases and tags
18
+ SVN_TRUNK_DIR = 'trunk'
19
+ SVN_RELEASES_DIR = 'releases'
20
+ SVN_BRANCHES_DIR = 'branches'
21
+ SVN_TAGS_DIR = 'tags'
22
+
23
+ SVN_DOTDIR = BASEDIR + '.svn'
24
+ SVN_ENTRIES = SVN_DOTDIR + 'entries'
25
+
26
+ # Ignore .svn directories in the various FileLists used by the main Rakefile
27
+ [
28
+ BIN_FILES,
29
+ LIB_FILES,
30
+ EXT_FILES,
31
+ DATA_FILES,
32
+ SPEC_FILES,
33
+ TEST_FILES,
34
+ EXTRA_PKGFILES,
35
+ ].each {|filelist| filelist.exclude(/\.svn/) }
36
+
37
+ # Strftime format for tags/releases
38
+ TAG_TIMESTAMP_FORMAT = '%Y%m%d-%H%M%S'
39
+ TAG_TIMESTAMP_PATTERN = /\d{4}\d{2}\d{2}-\d{6}/
40
+
41
+ RELEASE_VERSION_PATTERN = /\d+\.\d+\.\d+/
42
+
43
+ DEFAULT_KEYWORDS = %w[Date Rev Author URL Id]
44
+ KEYWORDED_FILEDIRS = %w[applets spec bin etc ext experiments examples lib misc docs]
45
+ KEYWORDED_FILEPATTERN = /
46
+ ^(?:
47
+ (?:meta)?rakefile.* # Rakefiles
48
+ |
49
+ .*\.(?:rb|c|h|js|html|css|template|erb|page) # Source file extensions
50
+ |
51
+ readme|install|todo
52
+ )$/ix
53
+
54
+
55
+
56
+ ###
57
+ ### Subversion-specific Helpers
58
+ ###
59
+ module SubversionHelpers
60
+
61
+ ###############
62
+ module_function
63
+ ###############
64
+
65
+ ### Return a new tag for the given time
66
+ def make_new_tag( time=Time.now )
67
+ return time.strftime( TAG_TIMESTAMP_FORMAT )
68
+ end
69
+
70
+
71
+ ### Get the subversion information for the current working directory as
72
+ ### a hash.
73
+ def get_svn_info( dir='.' )
74
+ return {} unless File.directory?( File.join(dir, '.svn') )
75
+ info = IO.read( '|-' ) or exec 'svn', 'info', dir.to_s
76
+ return YAML.load( info ) # 'svn info' outputs valid YAML! Yay!
77
+ rescue NotImplementedError
78
+ trace "No fork(), proceeding without svn info..."
79
+ return {}
80
+ end
81
+
82
+
83
+ ### Get a list of the objects registered with subversion under the specified directory and
84
+ ### return them as an Array of Pathame objects.
85
+ def get_svn_filelist( dir='.' )
86
+ list = IO.read( '|-' ) or exec 'svn', 'st', '-v', '--ignore-externals', dir
87
+
88
+ # Split into lines, filter out the unknowns, and grab the filenames as Pathnames
89
+ # :FIXME: This will break if we ever put in a file with spaces in its name. This
90
+ # will likely be the least of our worries if we do so, however, so it's not worth
91
+ # the additional complexity to make it handle that case. If we do need that, there's
92
+ # always the --xml output for 'svn st'...
93
+ return list.split( $/ ).
94
+ reject {|line| line =~ /^(\?|(\s*|--- .*)$)/ }.
95
+ collect {|fn| Pathname(fn[/\S+$/]) }
96
+ end
97
+
98
+ ### Return the URL to the repository root for the specified +dir+.
99
+ def get_svn_repo_root( dir='.' )
100
+ info = get_svn_info( dir )
101
+ return info['Repository Root']
102
+ end
103
+
104
+
105
+ ### Return the Subversion URL to the given +dir+.
106
+ def get_svn_url( dir='.' )
107
+ info = get_svn_info( dir )
108
+ return info['URL']
109
+ end
110
+
111
+
112
+ ### Return the path of the specified +dir+ under the svn root of the
113
+ ### checkout.
114
+ def get_svn_path( dir='.' )
115
+ root = get_svn_repo_root( dir )
116
+ url = get_svn_url( dir )
117
+
118
+ return url.sub( root + '/', '' )
119
+ end
120
+
121
+
122
+ ### Return the keywords for the specified array of +files+ as a Hash keyed by filename.
123
+ def get_svn_keyword_map( *files )
124
+ files.flatten!
125
+ files.push( '.' ) if files.empty?
126
+
127
+ cmd = ['svn', 'pg', 'svn:keywords', *files]
128
+
129
+ # trace "Executing: svn pg svn:keywords " + files.join(' ')
130
+ output = IO.read( '|-' ) or exec( 'svn', 'pg', 'svn:keywords', *files )
131
+
132
+ kwmap = {}
133
+ output.split( "\n" ).each do |line|
134
+ next if line !~ /\s+-\s+/
135
+ path, keywords = line.split( /\s+-\s+/, 2 )
136
+ kwmap[ path ] = keywords.split
137
+ end
138
+
139
+ return kwmap
140
+ end
141
+
142
+
143
+ ### Return the latest revision number of the specified +dir+ as an Integer.
144
+ def get_svn_rev( dir='.' )
145
+ info = get_svn_info( dir )
146
+ return info['Revision']
147
+ end
148
+ alias get_vcs_rev get_svn_rev
149
+
150
+
151
+ ### Return the latest revision number of the specified +dir+ as an Integer.
152
+ def get_last_changed_rev( dir='.' )
153
+ info = get_svn_info( dir )
154
+ return info['Last Changed Rev']
155
+ end
156
+
157
+
158
+ ### Return a list of the entries at the specified Subversion url. If
159
+ ### no +url+ is specified, it will default to the list in the URL
160
+ ### corresponding to the current working directory.
161
+ def svn_ls( url=nil )
162
+ url ||= get_svn_url()
163
+ list = IO.read( '|-' ) or exec 'svn', 'ls', url
164
+
165
+ trace 'svn ls of %s: %p' % [url, list] if $trace
166
+
167
+ return [] if list.nil? || list.empty?
168
+ return list.split( $INPUT_RECORD_SEPARATOR )
169
+ end
170
+
171
+
172
+ ### Return the URL of the latest timestamp in the tags directory.
173
+ def get_latest_svn_timestamp_tag
174
+ rooturl = get_svn_repo_root()
175
+ tagsurl = rooturl + "/#{SVN_TAGS_DIR}"
176
+
177
+ tags = svn_ls( tagsurl ).grep( TAG_TIMESTAMP_PATTERN ).sort
178
+ return nil if tags.nil? || tags.empty?
179
+ return tagsurl + '/' + tags.last
180
+ end
181
+
182
+
183
+ ### Get a subversion diff of the specified targets and return it. If no targets are
184
+ ### specified, the current directory will be diffed instead.
185
+ def get_svn_diff( *targets )
186
+ targets << BASEDIR if targets.empty?
187
+ trace "Getting svn diff for targets: %p" % [targets]
188
+ log = IO.read( '|-' ) or exec 'svn', 'diff', *(targets.flatten)
189
+
190
+ return log
191
+ end
192
+
193
+
194
+ ### Generate a commit log and invoke the user's editor on it.
195
+ def edit_commit_log
196
+ diff = make_commit_log()
197
+
198
+ File.open( COMMIT_MSG_FILE, File::WRONLY|File::TRUNC|File::CREAT ) do |fh|
199
+ fh.print( diff )
200
+ end
201
+
202
+ edit( COMMIT_MSG_FILE )
203
+ end
204
+
205
+
206
+ ### Get a subversion status as an Array of tuples of the form:
207
+ ### [ <status>, <path> ]
208
+ def get_svn_status( *targets )
209
+ targets << BASEDIR if targets.empty?
210
+ trace "Getting svn status for targets: %p" % [targets]
211
+ status = IO.read( '|-' ) or exec 'svn', 'st', '--ignore-externals', *(targets.flatten)
212
+ entries = status.split( /\n/ ).
213
+ select {|line| line !~ /^(\s*|--- .*)$/ }.
214
+ collect do |line|
215
+ flag, path = line.strip.split( /\s+/, 2 )
216
+ [ flag, Pathname.new(path) ]
217
+ end
218
+
219
+ return entries
220
+ end
221
+
222
+
223
+ ### Return the URL of the latest timestamp in the tags directory.
224
+ def get_latest_release_tag
225
+ rooturl = get_svn_repo_root()
226
+ releaseurl = rooturl + "/#{SVN_RELEASES_DIR}"
227
+
228
+ tags = svn_ls( releaseurl ).grep( RELEASE_VERSION_PATTERN ).sort_by do |tag|
229
+ tag[RELEASE_VERSION_PATTERN].split('.').collect {|i| Integer(i) }
230
+ end
231
+ return nil if tags.empty?
232
+
233
+ return releaseurl + '/' + tags.last
234
+ end
235
+
236
+
237
+ ### Return the names of all existing branches.
238
+ def get_branch_names
239
+ rooturl = get_svn_repo_root()
240
+ branchesurl = rooturl + "/#{SVN_BRANCHES_DIR}"
241
+
242
+ return svn_ls( branchesurl )
243
+ end
244
+
245
+
246
+ ### Extract a diff from the specified subversion working +dir+ and return it.
247
+ def make_svn_commit_log( dir='.' )
248
+ diff = IO.read( '|-' ) or exec 'svn', 'diff'
249
+ fail "No differences." if diff.empty?
250
+
251
+ return diff
252
+ end
253
+
254
+
255
+ ### Extract the svn log from the specified subversion working +dir+,
256
+ ### starting from rev +start+ and ending with rev +finish+, and return it.
257
+ def make_svn_log( dir='.', start='PREV', finish='HEAD' )
258
+ trace "svn log -r#{start}:#{finish} #{dir}"
259
+ log = IO.read( '|-' ) or exec 'svn', 'log', "-r#{start}:#{finish}", dir
260
+ fail "No log between #{start} and #{finish}." if log.empty?
261
+
262
+ return log
263
+ end
264
+
265
+
266
+ ### Extract the verbose XML svn log from the specified subversion working +dir+,
267
+ ### starting from rev +start+ and ending with rev +finish+, and return it.
268
+ def make_xml_svn_log( dir='.', start='PREV', finish='HEAD' )
269
+ trace "svn log --xml --verbose -r#{start}:#{finish} #{dir}"
270
+ log = IO.read( '|-' ) or exec 'svn', 'log', '--verbose', '--xml', "-r#{start}:#{finish}", dir
271
+ fail "No log between #{start} and #{finish}." if log.empty?
272
+
273
+ return log
274
+ end
275
+
276
+
277
+ ### Create a changelog from the subversion log of the specified +dir+ and return it.
278
+ def make_svn_changelog( dir='.' )
279
+ require 'xml/libxml'
280
+
281
+ changelog = ''
282
+ path_prefix = '/' + get_svn_path( dir ) + '/'
283
+
284
+ xmllog = make_xml_svn_log( dir, 0 )
285
+
286
+ parser = XML::Parser.string( xmllog )
287
+ root = parser.parse.root
288
+ root.find( '//log/logentry' ).to_a.reverse.each do |entry|
289
+ trace "Making a changelog entry for r%s" % [ entry['revision'] ]
290
+
291
+ added = []
292
+ deleted = []
293
+ changed = []
294
+
295
+ entry.find( 'paths/path').each do |path|
296
+ pathname = path.content
297
+ pathname.sub!( path_prefix , '' ) if pathname.count('/') > 1
298
+
299
+ case path['action']
300
+ when 'A', 'R'
301
+ if path['copyfrom-path']
302
+ verb = path['action'] == 'A' ? 'renamed' : 'copied'
303
+ added << "%s\n#{FILE_INDENT}-> #{verb} from %s@r%s" % [
304
+ pathname,
305
+ path['copyfrom-path'],
306
+ path['copyfrom-rev'],
307
+ ]
308
+ else
309
+ added << "%s (new)" % [ pathname ]
310
+ end
311
+
312
+ when 'M'
313
+ changed << pathname
314
+
315
+ when 'D'
316
+ deleted << pathname
317
+
318
+ else
319
+ log "Unknown action %p in rev %d" % [ path['action'], entry['revision'] ]
320
+ end
321
+
322
+ end
323
+
324
+ date = Time.parse( entry.find_first('date').content )
325
+
326
+ # cvs2svn doesn't set 'author'
327
+ author = 'unknown'
328
+ if entry.find_first( 'author' )
329
+ author = entry.find_first( 'author' ).content
330
+ end
331
+
332
+ msg = entry.find_first( 'msg' ).content
333
+ rev = entry['revision']
334
+
335
+ changelog << "-- #{date.rfc2822} by #{author} (r#{rev}) -----\n"
336
+ changelog << " Added: " << humanize_file_list(added) << "\n" unless added.empty?
337
+ changelog << " Changed: " << humanize_file_list(changed) << "\n" unless changed.empty?
338
+ changelog << " Deleted: " << humanize_file_list(deleted) << "\n" unless deleted.empty?
339
+ changelog << "\n"
340
+
341
+ indent = msg[/^(\s*)/] + LOG_INDENT
342
+
343
+ changelog << indent << msg.strip.gsub(/\n\s*/m, "\n#{indent}")
344
+ changelog << "\n\n\n"
345
+ end
346
+
347
+ return changelog
348
+ end
349
+
350
+
351
+ ### Returns a human-scannable file list by joining and truncating the list if it's too long.
352
+ def humanize_file_list( list, indent=FILE_INDENT )
353
+ listtext = list[0..5].join( "\n#{indent}" )
354
+ if list.length > 5
355
+ listtext << " (and %d other/s)" % [ list.length - 5 ]
356
+ end
357
+
358
+ return listtext
359
+ end
360
+
361
+
362
+ ### Add the list of +pathnames+ to the svn:ignore list.
363
+ def svn_ignore_files( *pathnames )
364
+ pathnames.flatten!
365
+
366
+ map = pathnames.inject({}) do |map,path|
367
+ map[ path.dirname ] ||= []
368
+ map[ path.dirname ] << path.basename
369
+ map
370
+ end
371
+
372
+ trace "Ignoring %d files in %d directories." % [ pathnames.length, map.length ]
373
+
374
+ map.each do |dir, files|
375
+ trace " %s: %p" % [ dir, files ]
376
+ io = open( '|-' ) or exec 'svn', 'pg', 'svn:ignore', dir
377
+ ignorelist = io.read.strip
378
+ ignorelist << "\n" << files.join("\n")
379
+ system 'svn', 'ps', 'svn:ignore', ignorelist, dir
380
+ end
381
+ end
382
+
383
+
384
+ ### Delete the files in the given +filelist+ after confirming with the user.
385
+ def delete_extra_files( filelist )
386
+ description = humanize_file_list( filelist, ' ' )
387
+ log "Files to delete:\n ", description
388
+ ask_for_confirmation( "Really delete them?", false ) do
389
+ filelist.each do |f|
390
+ rm_rf( f, :verbose => true )
391
+ end
392
+ end
393
+ end
394
+
395
+ end # module SubversionHelpers
396
+
397
+
398
+ ###
399
+ ### Tasks
400
+ ###
401
+
402
+ desc "Subversion tasks"
403
+ namespace :svn do
404
+ include SubversionHelpers
405
+
406
+ desc "Copy the HEAD revision of the current #{SVN_TRUNK_DIR}/ to #{SVN_TAGS_DIR}/ with a " +
407
+ "current timestamp."
408
+ task :tag do
409
+ svninfo = get_svn_info()
410
+ tag = make_new_tag()
411
+ svntrunk = svninfo['Repository Root'] + "/#{SVN_TRUNK_DIR}"
412
+ svntagdir = svninfo['Repository Root'] + "/#{SVN_TAGS_DIR}"
413
+ svntag = svntagdir + '/' + tag
414
+
415
+ desc = "Tagging trunk as #{svntag}"
416
+ ask_for_confirmation( desc ) do
417
+ msg = prompt_with_default( "Commit log: ", "Tagging for code push" )
418
+ run 'svn', 'cp', '-m', msg, svntrunk, svntag
419
+ end
420
+ end
421
+
422
+
423
+ desc "Copy the HEAD revision of the current #{SVN_TRUNK_DIR}/ to #{SVN_BRANCHES_DIR} with a " +
424
+ "user-specified name."
425
+ task :branch, [:name] do |task, args|
426
+ branchname = args.name
427
+ unless branchname
428
+ branchname = prompt( "Branch name" ) or abort
429
+ end
430
+
431
+ svninfo = get_svn_info()
432
+ svntrunk = Pathname.new( svninfo['Repository Root'] ) + SVN_TRUNK_DIR
433
+ svnbranchdir = Pathname.new( svninfo['Repository Root'] ) + SVN_BRANCHES_DIR
434
+ svnbranch = svnbranchdir + branchname
435
+
436
+ desc = "Making a new branch: #{svnbranch}"
437
+ ask_for_confirmation( desc ) do
438
+ msg = prompt_with_default( "Commit log: ", "Making a '#{args.name}' branch" )
439
+ run 'svn', 'cp', '-m', msg, svntrunk, svnbranch
440
+ ask_for_confirmation( "Switch to the new branch?", false ) do
441
+ run 'svn', 'sw', svnbranch
442
+ end
443
+ end
444
+ end
445
+
446
+
447
+ desc "Switch the working copy to the named branch"
448
+ task :switch, [:name] do |task, args|
449
+ branches = get_branch_names().collect {|name| name.chomp('/') }
450
+
451
+ unless args.name
452
+ log "Branches are:\n" + branches.collect {|br| " #{br}" }.join( "\n" )
453
+
454
+ begin
455
+ oldproc = Readline.completion_proc
456
+ abbrev = branches.abbrev
457
+ Readline.completion_proc = lambda{|string| abbrev[string] }
458
+
459
+ name = prompt( "Branch to switch to" ) or abort
460
+ args.with_defaults( :name => name )
461
+ ensure
462
+ Readline.completion_proc = oldproc unless oldproc.nil?
463
+ end
464
+ end
465
+
466
+ svninfo = get_svn_info()
467
+ abort "Branch '#{args.name}' does not exist" unless branches.include?( args.name )
468
+ branchuri = Pathname.new( svninfo['Repository Root'] ) + SVN_BRANCHES_DIR + args.name
469
+ run 'svn', 'sw', branchuri
470
+ end
471
+ task :sw => :switch
472
+
473
+
474
+ desc "Switch to the trunk if the working copy isn't there already."
475
+ task :trunk do
476
+ svninfo = get_svn_info()
477
+ svntrunk = Pathname.new( svninfo['Repository Root'] ) + SVN_TRUNK_DIR
478
+
479
+ if svninfo['URL'] != svntrunk.to_s
480
+ log "Switching to #{svntrunk}"
481
+ run 'svn', 'sw', svntrunk
482
+ else
483
+ log "You are already on trunk (#{svntrunk})"
484
+ end
485
+ end
486
+
487
+
488
+ desc "Copy the most recent tag to #{SVN_RELEASES_DIR}/#{PKG_VERSION}"
489
+ task :prep_release do
490
+ last_tag = get_latest_svn_timestamp_tag()
491
+ svninfo = get_svn_info()
492
+ svnroot = Pathname.new( svninfo['Repository Root'] )
493
+ svntrunk = svnroot + SVN_TRUNK_DIR
494
+ svnrel = svnroot + SVN_RELEASES_DIR
495
+ release = PKG_VERSION
496
+ svnrelease = svnrel + release
497
+
498
+ unless svn_ls( svnrel.dirname ).include?( svnrel.basename.to_s + '/' )
499
+ log "Releases path #{svnrel} does not exist."
500
+ ask_for_confirmation( "To continue I'll need to create it." ) do
501
+ run 'svn', 'mkdir', svnrel, '-m', 'Creating releases/ directory'
502
+ end
503
+ else
504
+ trace "Found release dir #{svnrel}"
505
+ end
506
+
507
+ releases = svn_ls( svnrel ).collect {|name| name.sub(%r{/$}, '') }
508
+ trace "Releases: %p" % [releases]
509
+ if releases.include?( release )
510
+ error "Version #{release} already has a branch (#{svnrelease}). Did you mean " +
511
+ "to increment the version in #{VERSION_FILE}?"
512
+ fail
513
+ else
514
+ trace "No #{release} version currently exists"
515
+ end
516
+
517
+ desc = "Tagging trunk as #{svnrelease}..."
518
+ ask_for_confirmation( desc ) do
519
+ msg = prompt_with_default( "Commit log: ", "Branching for release" )
520
+ run 'svn', 'cp', '-m', msg, svntrunk, svnrelease
521
+ end
522
+ end
523
+
524
+ ### Task for debugging the #get_target_args helper
525
+ task :show_targets do
526
+ $stdout.puts "Targets from ARGV (%p): %p" % [ARGV, get_target_args()]
527
+ end
528
+
529
+
530
+ desc "Update from Subversion"
531
+ task :update do
532
+ run 'svn', 'up', '--ignore-externals'
533
+ end
534
+
535
+
536
+ desc "Add/ignore any files that are unknown in the working copy"
537
+ task :newfiles do
538
+ log "Checking for new files..."
539
+ entries = get_svn_status()
540
+
541
+ unless entries.empty?
542
+ files_to_add = []
543
+ files_to_ignore = []
544
+ files_to_delete = []
545
+
546
+ entries.find_all {|entry| entry[0] == '?'}.each do |entry|
547
+ action = prompt_with_default( " #{entry[1]}: (a)dd, (i)gnore, (s)kip (d)elete", 's' )
548
+ case action
549
+ when 'a'
550
+ files_to_add << entry[1]
551
+ when 'i'
552
+ files_to_ignore << entry[1]
553
+ when 'd'
554
+ files_to_delete << entry[1]
555
+ end
556
+ end
557
+
558
+ unless files_to_add.empty?
559
+ run 'svn', 'add', *files_to_add
560
+ end
561
+
562
+ unless files_to_ignore.empty?
563
+ svn_ignore_files( *files_to_ignore )
564
+ end
565
+
566
+ unless files_to_delete.empty?
567
+ delete_extra_files( files_to_delete )
568
+ end
569
+ end
570
+ end
571
+ task :add => :newfiles
572
+
573
+
574
+ desc "Check in all the changes in your current working copy"
575
+ task :checkin => ['svn:update', 'svn:newfiles', 'test', 'svn:fix_keywords', COMMIT_MSG_FILE] do
576
+ targets = get_target_args()
577
+ $stderr.puts '---', File.read( COMMIT_MSG_FILE ), '---'
578
+ ask_for_confirmation( "Continue with checkin?" ) do
579
+ run 'svn', 'ci', '-F', COMMIT_MSG_FILE, targets
580
+ rm_f COMMIT_MSG_FILE
581
+ end
582
+ end
583
+ task :commit => :checkin
584
+ task :ci => :checkin
585
+
586
+
587
+ task :clean do
588
+ rm_f COMMIT_MSG_FILE
589
+ end
590
+
591
+
592
+ desc "Check and fix any missing keywords for any files in the project which need them"
593
+ task :fix_keywords do
594
+ log "Checking subversion keywords..."
595
+ paths = get_svn_filelist( BASEDIR ).
596
+ select {|path| path.file? && path.to_s =~ KEYWORDED_FILEPATTERN }
597
+
598
+ trace "Looking at %d paths for keywords:\n %p" % [paths.length, paths]
599
+ kwmap = get_svn_keyword_map( paths )
600
+
601
+ buf = ''
602
+ PP.pp( kwmap, buf, 132 )
603
+ trace "keyword map is: %s" % [buf]
604
+
605
+ files_needing_fixups = paths.find_all do |path|
606
+ (kwmap[path.to_s] & DEFAULT_KEYWORDS) != DEFAULT_KEYWORDS
607
+ end
608
+
609
+ unless files_needing_fixups.empty?
610
+ $stderr.puts "Files needing keyword fixes: ",
611
+ files_needing_fixups.collect {|f|
612
+ " %s: %s" % [f, kwmap[f] ? kwmap[f].join(' ') : "(no keywords)"]
613
+ }
614
+ ask_for_confirmation( "Will add default keywords to these files." ) do
615
+ run 'svn', 'ps', 'svn:keywords', DEFAULT_KEYWORDS.join(' '), *files_needing_fixups
616
+ end
617
+ else
618
+ log "Keywords are all up to date."
619
+ end
620
+ end
621
+
622
+
623
+ task :debug_helpers do
624
+ methods = [
625
+ :get_last_changed_rev,
626
+ :get_latest_release_tag,
627
+ :get_latest_svn_timestamp_tag,
628
+ :get_svn_diff,
629
+ :get_svn_filelist,
630
+ :get_svn_info,
631
+ :get_svn_keyword_map,
632
+ :get_svn_path,
633
+ :get_svn_repo_root,
634
+ :get_svn_rev,
635
+ :get_svn_status,
636
+ :get_svn_url,
637
+ :svn_ls,
638
+ ]
639
+ maxlen = methods.collect {|sym| sym.to_s.length }.max
640
+
641
+ methods.each do |meth|
642
+ res = send( meth )
643
+ puts "%*s => %p" % [ maxlen, colorize(meth.to_s, :cyan), res ]
644
+ end
645
+ end
646
+
647
+ end
648
+
649
+ if SVN_DOTDIR.exist?
650
+ trace "Defining subversion VCS tasks"
651
+
652
+ desc "Check in all the changes in your current working copy"
653
+ task :ci => 'svn:ci'
654
+ desc "Check in all the changes in your current working copy"
655
+ task :checkin => 'svn:ci'
656
+
657
+ desc "Tag a release"
658
+ task :prep_release => 'svn:prep_release'
659
+
660
+ file COMMIT_MSG_FILE do
661
+ edit_commit_log()
662
+ end
663
+ else
664
+ trace "Not defining subversion tasks: no #{SVN_DOTDIR}"
665
+ end
666
+
667
+ end # unless defined?( SVN_DOTDIR )
668
+