pluginfactory 1.0.4 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/rake/packaging.rb CHANGED
@@ -1,12 +1,15 @@
1
1
  #
2
2
  # Packaging Rake Tasks
3
- # $Id: packaging.rb 21 2008-08-07 23:45:52Z deveiant $
3
+
4
4
  #
5
5
 
6
6
  require 'rbconfig'
7
+ require 'pathname'
7
8
  require 'rake/packagetask'
8
9
  require 'rake/gempackagetask'
9
10
 
11
+ require Pathname( __FILE__ ).dirname.expand_path + 'hg.rb'
12
+
10
13
  include Config
11
14
 
12
15
  ### Task: gem
@@ -16,8 +19,7 @@ Rake::PackageTask.new( PKG_NAME, PKG_VERSION ) do |task|
16
19
  task.need_tar_bz2 = true
17
20
  task.need_zip = true
18
21
  task.package_dir = PKGDIR.to_s
19
- task.package_files = RELEASE_FILES.
20
- collect {|f| f.relative_path_from(BASEDIR).to_s }
22
+ task.package_files = RELEASE_FILES.collect {|f| f.to_s }
21
23
  end
22
24
  task :package => [:gem]
23
25
 
@@ -36,14 +38,31 @@ file gempath.to_s => [PKGDIR.to_s] + GEMSPEC.files do
36
38
  end
37
39
  end
38
40
 
41
+
42
+ prerelease_gempath = PKGDIR + SNAPSHOT_GEM_NAME
43
+
44
+ desc "Build a pre-release RubyGem package"
45
+ task :prerelease_gem => prerelease_gempath.to_s
46
+ file prerelease_gempath.to_s => [PKGDIR.to_s] + GEMSPEC.files do
47
+ when_writing( "Creating prerelease GEM" ) do
48
+ gemspec = GEMSPEC.clone
49
+ gemspec.version = Gem::Version.create( "%s.%s" % [GEMSPEC.version, PKG_BUILD] )
50
+ Gem::Builder.new( gemspec ).build
51
+ verbose( true ) do
52
+ mv SNAPSHOT_GEM_NAME, prerelease_gempath
53
+ end
54
+ end
55
+ end
56
+
57
+
39
58
  ### Task: install
40
59
  desc "Install #{PKG_NAME} as a conventional library"
41
- task :install do
60
+ task :install => "spec:quiet" do
42
61
  log "Installing #{PKG_NAME} as a conventional library"
43
62
  sitelib = Pathname.new( CONFIG['sitelibdir'] )
44
63
  sitearch = Pathname.new( CONFIG['sitearchdir'] )
45
64
  Dir.chdir( LIBDIR ) do
46
- LIB_FILES.each do |libfile|
65
+ LIB_FILES.collect {|path| Pathname(path) }.each do |libfile|
47
66
  relpath = libfile.relative_path_from( LIBDIR )
48
67
  target = sitelib + relpath
49
68
  FileUtils.mkpath target.dirname,
@@ -53,8 +72,10 @@ task :install do
53
72
  end
54
73
  end
55
74
  if EXTDIR.exist?
75
+ trace " looking for a binary extension (%s)" % [ EXTDIR + "*.#{Config::CONFIG['DLEXT']}" ]
56
76
  Dir.chdir( EXTDIR ) do
57
- Pathname.glob( EXTDIR + Config::CONFIG['DLEXT'] ) do |dl|
77
+ Pathname.glob( "*.#{Config::CONFIG['DLEXT']}" ) do |dl|
78
+ trace " found: #{dl}"
58
79
  target = sitearch + dl.basename
59
80
  FileUtils.install dl, target,
60
81
  :mode => 0755, :verbose => true, :noop => $dryrun
@@ -82,7 +103,7 @@ task :uninstall do
82
103
  sitearch = Pathname.new( CONFIG['sitearchdir'] )
83
104
 
84
105
  Dir.chdir( LIBDIR ) do
85
- LIB_FILES.each do |libfile|
106
+ LIB_FILES.collect {|path| Pathname(path) }.each do |libfile|
86
107
  relpath = libfile.relative_path_from( LIBDIR )
87
108
  target = sitelib + relpath
88
109
  FileUtils.rm_f target, :verbose => true, :noop => $dryrun
@@ -91,8 +112,10 @@ task :uninstall do
91
112
  end
92
113
  end
93
114
  if EXTDIR.exist?
115
+ trace " looking for a binary extension (%s)" % [ EXTDIR + "*.#{Config::CONFIG['DLEXT']}" ]
94
116
  Dir.chdir( EXTDIR ) do
95
- Pathname.glob( EXTDIR + Config::CONFIG['DLEXT'] ) do |dl|
117
+ Pathname.glob( "*.#{Config::CONFIG['DLEXT']}" ) do |dl|
118
+ trace " found: #{dl}"
96
119
  target = sitearch + dl.basename
97
120
  FileUtils.rm target, :verbose => true, :noop => $dryrun
98
121
  end
@@ -110,3 +133,12 @@ end
110
133
 
111
134
 
112
135
 
136
+ desc "Add development depdendencies to the gemspec -- this is meant to be chained " +
137
+ "together with :gem"
138
+ task :include_dev_dependencies do
139
+ DEVELOPMENT_DEPENDENCIES.each do |name, version|
140
+ version = '>= 0' if version.length.zero?
141
+ GEMSPEC.add_development_dependency( name, version )
142
+ end
143
+ end
144
+
data/rake/publishing.rb CHANGED
@@ -26,8 +26,8 @@ class Net::SMTP
26
26
  return self
27
27
  end
28
28
  end
29
-
30
-
29
+
30
+
31
31
  #######
32
32
  private
33
33
  #######
@@ -100,7 +100,7 @@ begin
100
100
 
101
101
 
102
102
  namespace :release do
103
- task :default => [ 'svn:release', :upload, :publish, :announce ]
103
+ task :default => [ :prep_release, :upload, :publish, :announce ]
104
104
 
105
105
  desc "Re-publish the release with the current version number"
106
106
  task :rerelease => [ :upload, :publish, :announce ]
@@ -111,31 +111,28 @@ begin
111
111
  $publish_privately = true
112
112
  Rake::Task['release:rerelease'].invoke
113
113
  end
114
-
114
+
115
115
 
116
116
  desc "Generate the release notes"
117
117
  task :notes => [RELEASE_NOTES_FILE]
118
118
  file RELEASE_NOTES_FILE do |task|
119
- last_rel_tag = get_latest_release_tag() or
120
- fail ">>> No releases tagged! Try running 'rake svn:release' first"
121
- trace "Last release tag is: %p" % [ last_rel_tag ]
122
- start = get_last_changed_rev( last_rel_tag ) || 1
123
- trace "Starting rev is: %p" % [ start ]
124
- log_output = make_svn_log( '.', start, 'HEAD' )
119
+ last_tag = MercurialHelpers.get_tags.grep( /\d+\.\d+\.\d+/ ).
120
+ collect {|ver| vvec(ver) }.sort.last.unpack( 'N*' ).join('.')
125
121
 
126
122
  File.open( task.name, File::WRONLY|File::TRUNC|File::CREAT ) do |fh|
127
- fh.print( log_output )
123
+ fh.puts "Release Notes for #{PKG_VERSION}",
124
+ "--------------------------------", '', ''
128
125
  end
129
126
 
130
127
  edit task.name
131
128
  end
132
129
  CLOBBER.include( RELEASE_NOTES_FILE )
133
-
134
-
130
+
131
+
135
132
  desc "Upload project documentation and packages to #{PROJECT_HOST}"
136
133
  task :upload => [ :upload_docs, :upload_packages ]
137
134
  task :project => :upload # the old name
138
-
135
+
139
136
  desc "Publish the project docs to #{PROJECT_HOST}"
140
137
  task :upload_docs => [ :rdoc ] do
141
138
  when_writing( "Publishing docs to #{PROJECT_SCPDOCURL}" ) do
@@ -172,7 +169,7 @@ begin
172
169
  == Installation
173
170
 
174
171
  Via gems:
175
-
172
+
176
173
  $ sudo gem install #{GEMSPEC.name}
177
174
 
178
175
  or from source:
@@ -185,7 +182,7 @@ begin
185
182
  == Changes
186
183
  #{relnotes}
187
184
  }.gsub( /^\t+/, '' )
188
-
185
+
189
186
  File.open( task.name, File::WRONLY|File::TRUNC|File::CREAT ) do |fh|
190
187
  fh.print( announce_body )
191
188
  end
@@ -193,8 +190,8 @@ begin
193
190
  edit task.name
194
191
  end
195
192
  CLOBBER.include( RELEASE_ANNOUNCE_FILE )
196
-
197
-
193
+
194
+
198
195
  desc 'Send out a release announcement'
199
196
  task :announce => [RELEASE_ANNOUNCE_FILE] do
200
197
  email = TMail::Mail.new
@@ -217,13 +214,13 @@ begin
217
214
  puts '---',
218
215
  email.to_s,
219
216
  '---'
220
-
217
+
221
218
  ask_for_confirmation( "Will send via #{SMTP_HOST}." ) do
222
219
  pwent = Etc.getpwuid( Process.euid )
223
220
  curuser = pwent ? pwent.name : 'unknown'
224
221
  username = prompt_with_default( "SMTP user", curuser )
225
222
  password = prompt_for_password()
226
-
223
+
227
224
  trace "Creating SMTP connection to #{SMTP_HOST}:#{SMTP_PORT}"
228
225
  smtp = Net::SMTP.new( SMTP_HOST, SMTP_PORT )
229
226
  smtp.set_debug_output( $stdout )
@@ -237,73 +234,77 @@ begin
237
234
  trace "done."
238
235
  end
239
236
  end
240
-
241
-
237
+
238
+
242
239
  desc 'Publish the new release to RubyForge'
243
240
  task :publish => [:clean, :package, :notes] do |task|
244
241
  project = GEMSPEC.rubyforge_project
245
242
 
246
- rf = RubyForge.new
247
- log "Loading RubyForge config"
248
- rf.configure
249
-
250
- group_id = rf.autoconfig['group_ids'][RUBYFORGE_GROUP] or
251
- fail "Your configuration doesn't have a group id for '#{RUBYFORGE_GROUP}'"
252
-
253
- # If this project doesn't yet exist, create it
254
- unless rf.autoconfig['package_ids'].key?( project )
255
- ask_for_confirmation( "Package '#{project}' doesn't exist on RubyForge. Create it?" ) do
256
- log "Creating new package '#{project}'"
257
- rf.create_package( group_id, project )
258
- end
259
- end
260
-
261
- package_id = rf.autoconfig['package_ids'][ project ]
262
-
263
- # Make sure this release doesn't already exist
264
- releases = rf.autoconfig['release_ids']
265
- if releases.key?( GEMSPEC.name ) && releases[ GEMSPEC.name ].key?( PKG_VERSION )
266
- log "Rubyforge seems to already have #{ PKG_FILE_NAME }"
243
+ if $publish_privately
244
+ log "Skipping push of release files to RubyForge"
267
245
  else
268
- config = rf.userconfig or
269
- fail "You apparently haven't set up your RubyForge credentials on this machine."
270
- config['release_notes'] = GEMSPEC.description
271
- config['release_changes'] = File.read( RELEASE_NOTES_FILE )
272
-
273
- files = FileList[ PKGDIR + GEM_FILE_NAME ]
274
- files.include PKGDIR + "#{PKG_FILE_NAME}.tar.gz"
275
- files.include PKGDIR + "#{PKG_FILE_NAME}.tar.bz2"
276
- files.include PKGDIR + "#{PKG_FILE_NAME}.zip"
277
-
278
- log "Releasing #{PKG_FILE_NAME}"
279
- when_writing do
280
- log "Publishing to RubyForge: \n",
281
- "\tproject: #{RUBYFORGE_GROUP}\n",
282
- "\tpackage: #{PKG_NAME.downcase}\n",
283
- "\tpackage version: #{PKG_VERSION}\n",
284
- "\tfiles: " + files.collect {|f| f.to_s }.join(', ') + "\n"
285
-
286
- ask_for_confirmation( "Publish to RubyForge?" ) do
287
- log 'Logging in...'
288
- rf.login
289
- log "Adding the new release to the '#{project}' project"
290
- rf.add_release( group_id, package_id, PKG_VERSION, *files )
246
+ rf = RubyForge.new
247
+ log "Loading RubyForge config"
248
+ rf.configure
249
+
250
+ group_id = rf.autoconfig['group_ids'][RUBYFORGE_GROUP] or
251
+ fail "Your configuration doesn't have a group id for '#{RUBYFORGE_GROUP}'"
252
+
253
+ # If this project doesn't yet exist, create it
254
+ unless rf.autoconfig['package_ids'].key?( project )
255
+ ask_for_confirmation( "Package '#{project}' doesn't exist on RubyForge. Create it?" ) do
256
+ log "Creating new package '#{project}'"
257
+ rf.create_package( group_id, project )
258
+ end
259
+ end
260
+
261
+ package_id = rf.autoconfig['package_ids'][ project ]
262
+
263
+ # Make sure this release doesn't already exist
264
+ releases = rf.autoconfig['release_ids']
265
+ if releases.key?( GEMSPEC.name ) && releases[ GEMSPEC.name ].key?( PKG_VERSION )
266
+ log "Rubyforge seems to already have #{ PKG_FILE_NAME }"
267
+ else
268
+ config = rf.userconfig or
269
+ fail "You apparently haven't set up your RubyForge credentials on this machine."
270
+ config['release_notes'] = GEMSPEC.description
271
+ config['release_changes'] = File.read( RELEASE_NOTES_FILE )
272
+
273
+ files = FileList[ PKGDIR + GEM_FILE_NAME ]
274
+ files.include PKGDIR + "#{PKG_FILE_NAME}.tar.gz"
275
+ files.include PKGDIR + "#{PKG_FILE_NAME}.tar.bz2"
276
+ files.include PKGDIR + "#{PKG_FILE_NAME}.zip"
277
+
278
+ log "Releasing #{PKG_FILE_NAME}"
279
+ when_writing do
280
+ log "Publishing to RubyForge: \n",
281
+ "\tproject: #{RUBYFORGE_GROUP}\n",
282
+ "\tpackage: #{PKG_NAME.downcase}\n",
283
+ "\tpackage version: #{PKG_VERSION}\n",
284
+ "\tfiles: " + files.collect {|f| f.to_s }.join(', ') + "\n"
285
+
286
+ ask_for_confirmation( "Publish to RubyForge?" ) do
287
+ log 'Logging in...'
288
+ rf.login
289
+ log "Adding the new release to the '#{project}' project"
290
+ rf.add_release( group_id, package_id, PKG_VERSION, *files )
291
+ end
291
292
  end
292
293
  end
293
294
  end
294
295
  end
295
296
  end
296
-
297
+
297
298
  rescue LoadError => err
298
299
  if !Object.const_defined?( :Gem )
299
300
  require 'rubygems'
300
301
  retry
301
302
  end
302
-
303
+
303
304
  task :no_release_tasks do
304
305
  fail "Release tasks not defined: #{err.message}"
305
306
  end
306
-
307
+
307
308
  task :release => :no_release_tasks
308
309
  task "release:announce" => :no_release_tasks
309
310
  task "release:publish" => :no_release_tasks
data/rake/rdoc.rb CHANGED
@@ -1,45 +1,30 @@
1
1
  #
2
2
  # RDoc Rake tasks
3
- # $Id: rdoc.rb 86 2009-02-23 06:38:40Z deveiant $
3
+
4
4
  #
5
5
 
6
+ gem 'rdoc', '>= 2.4.3'
7
+
8
+ require 'rubygems'
6
9
  require 'rdoc/rdoc'
7
10
  require 'rake/clean'
8
-
9
-
10
- if RDoc::RDoc::GENERATORS.key?( 'darkfish' )
11
- $have_darkfish = true
12
- else
13
- trace "No darkfish generator."
14
- $have_darkfish = false
15
- end
16
-
11
+ require 'rdoc/task'
17
12
 
18
13
  # Append docs/lib to the load path if it exists for a locally-installed Darkfish
19
14
  DOCSLIB = DOCSDIR + 'lib'
20
15
  $LOAD_PATH.unshift( DOCSLIB.to_s ) if DOCSLIB.exist?
21
16
 
22
17
  # Make relative string paths of all the stuff we need to generate docs for
23
- DOCFILES = LIB_FILES + EXT_FILES + GEMSPEC.extra_rdoc_files
18
+ DOCFILES = Rake::FileList[ LIB_FILES + EXT_FILES + GEMSPEC.extra_rdoc_files ]
24
19
 
25
20
 
26
21
  directory RDOCDIR.to_s
27
22
  CLOBBER.include( RDOCDIR )
28
23
 
29
24
  desc "Build API documentation in #{RDOCDIR}"
30
- task :rdoc => [ Rake.application.rakefile, *DOCFILES ] do
31
- args = RDOC_OPTIONS
32
- args += [ '-o', RDOCDIR.to_s ]
33
- args += [ '-f', 'darkfish' ] if $have_darkfish
34
- args += DOCFILES.collect {|pn| pn.to_s }
35
-
36
- trace "Building docs with arguments: %s" % [ args.join(' ') ]
37
- RDoc::RDoc.new.document( args ) rescue nil
38
- end
39
-
40
- desc "Rebuild API documentation in #{RDOCDIR}"
41
- task :rerdoc do
42
- rm_r( RDOCDIR ) if RDOCDIR.exist?
43
- Rake::Task[ :rdoc ].invoke
25
+ RDoc::Task.new do |task|
26
+ task.main = "README"
27
+ task.rdoc_files.include( DOCFILES )
28
+ task.rdoc_dir = RDOCDIR.to_s
29
+ task.options = RDOC_OPTIONS
44
30
  end
45
-
data/rake/style.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # Style Fixup Rake Tasks
3
- # $Id: style.rb 10 2008-07-18 15:52:48Z deveiant $
3
+
4
4
  #
5
5
  # Authors:
6
6
  # * Michael Granger <ged@FaerieMUD.org>
data/rake/svn.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # Subversion Rake Tasks
3
- # $Id: svn.rb 86 2009-02-23 06:38:40Z deveiant $
3
+
4
4
  #
5
5
  # Authors:
6
6
  # * Michael Granger <ged@FaerieMUD.org>
@@ -10,593 +10,659 @@ require 'pp'
10
10
  require 'yaml'
11
11
  require 'date'
12
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
13
69
 
14
- # Strftime format for tags/releases
15
- TAG_TIMESTAMP_FORMAT = '%Y%m%d-%H%M%S'
16
- TAG_TIMESTAMP_PATTERN = /\d{4}\d{2}\d{2}-\d{6}/
17
-
18
- RELEASE_VERSION_PATTERN = /\d+\.\d+\.\d+/
19
-
20
- DEFAULT_EDITOR = 'vi'
21
- DEFAULT_KEYWORDS = %w[Date Rev Author URL Id]
22
- KEYWORDED_FILEDIRS = %w[applets spec bin etc ext experiments examples lib misc docs]
23
- KEYWORDED_FILEPATTERN = /
24
- ^(?:
25
- (?:meta)?rakefile.* # Rakefiles
26
- |
27
- .*\.(?:rb|c|h|js|html|css|template|erb|page) # Source file extensions
28
- |
29
- readme|install|todo
30
- )$/ix
31
-
32
- COMMIT_MSG_FILE = 'commit-msg.txt'
33
-
34
- SVN_TRUNK_DIR = 'trunk' unless defined?( SVN_TRUNK_DIR )
35
- SVN_RELEASES_DIR = 'branches' unless defined?( SVN_RELEASES_DIR )
36
- SVN_BRANCHES_DIR = 'branches' unless defined?( SVN_BRANCHES_DIR )
37
- SVN_TAGS_DIR = 'tags' unless defined?( SVN_TAGS_DIR )
38
-
39
- FILE_INDENT = " " * 12
40
- LOG_INDENT = " " * 3
41
-
42
-
43
-
44
- ###
45
- ### Subversion-specific Helpers
46
- ###
47
-
48
- ### Return a new tag for the given time
49
- def make_new_tag( time=Time.now )
50
- return time.strftime( TAG_TIMESTAMP_FORMAT )
51
- end
52
-
53
-
54
- ### Get the subversion information for the current working directory as
55
- ### a hash.
56
- def get_svn_info( dir='.' )
57
- return {} unless File.directory?( File.join(dir, '.svn') )
58
- info = IO.read( '|-' ) or exec 'svn', 'info', dir
59
- return YAML.load( info ) # 'svn info' outputs valid YAML! Yay!
60
- rescue NotImplementedError
61
- trace "No fork(), proceeding without svn info..."
62
- return {}
63
- end
64
-
65
-
66
- ### Get a list of the objects registered with subversion under the specified directory and
67
- ### return them as an Array of Pathame objects.
68
- def get_svn_filelist( dir='.' )
69
- list = IO.read( '|-' ) or exec 'svn', 'st', '-v', '--ignore-externals', dir
70
-
71
- # Split into lines, filter out the unknowns, and grab the filenames as Pathnames
72
- # :FIXME: This will break if we ever put in a file with spaces in its name. This
73
- # will likely be the least of our worries if we do so, however, so it's not worth
74
- # the additional complexity to make it handle that case. If we do need that, there's
75
- # always the --xml output for 'svn st'...
76
- return list.split( $/ ).
77
- reject {|line| line =~ /^(\?|(\s*|--- .*)$)/ }.
78
- collect {|fn| Pathname(fn[/\S+$/]) }
79
- end
80
-
81
- ### Return the URL to the repository root for the specified +dir+.
82
- def get_svn_repo_root( dir='.' )
83
- info = get_svn_info( dir )
84
- return info['Repository Root']
85
- end
86
-
87
-
88
- ### Return the Subversion URL to the given +dir+.
89
- def get_svn_url( dir='.' )
90
- info = get_svn_info( dir )
91
- return info['URL']
92
- end
93
-
94
-
95
- ### Return the path of the specified +dir+ under the svn root of the
96
- ### checkout.
97
- def get_svn_path( dir='.' )
98
- root = get_svn_repo_root( dir )
99
- url = get_svn_url( dir )
100
-
101
- return url.sub( root + '/', '' )
102
- end
103
-
104
-
105
- ### Return the keywords for the specified array of +files+ as a Hash keyed by filename.
106
- def get_svn_keyword_map( *files )
107
- files.flatten!
108
- files.push( '.' ) if files.empty?
109
-
110
- cmd = ['svn', 'pg', 'svn:keywords', *files]
111
-
112
- # trace "Executing: svn pg svn:keywords " + files.join(' ')
113
- output = IO.read( '|-' ) or exec( 'svn', 'pg', 'svn:keywords', *files )
114
-
115
- kwmap = {}
116
- output.split( "\n" ).each do |line|
117
- next if line !~ /\s+-\s+/
118
- path, keywords = line.split( /\s+-\s+/, 2 )
119
- kwmap[ path ] = keywords.split
120
- end
121
-
122
- return kwmap
123
- end
124
-
125
-
126
- ### Return the latest revision number of the specified +dir+ as an Integer.
127
- def get_svn_rev( dir='.' )
128
- info = get_svn_info( dir )
129
- return info['Revision']
130
- end
131
-
132
-
133
- ### Return the latest revision number of the specified +dir+ as an Integer.
134
- def get_last_changed_rev( dir='.' )
135
- info = get_svn_info( dir )
136
- return info['Last Changed Rev']
137
- end
138
-
139
-
140
- ### Return a list of the entries at the specified Subversion url. If
141
- ### no +url+ is specified, it will default to the list in the URL
142
- ### corresponding to the current working directory.
143
- def svn_ls( url=nil )
144
- url ||= get_svn_url()
145
- list = IO.read( '|-' ) or exec 'svn', 'ls', url
146
-
147
- trace 'svn ls of %s: %p' % [url, list] if $trace
148
-
149
- return [] if list.nil? || list.empty?
150
- return list.split( $INPUT_RECORD_SEPARATOR )
151
- end
152
-
153
-
154
- ### Return the URL of the latest timestamp in the tags directory.
155
- def get_latest_svn_timestamp_tag
156
- rooturl = get_svn_repo_root()
157
- tagsurl = rooturl + "/#{SVN_TAGS_DIR}"
158
-
159
- tags = svn_ls( tagsurl ).grep( TAG_TIMESTAMP_PATTERN ).sort
160
- return nil if tags.nil? || tags.empty?
161
- return tagsurl + '/' + tags.last
162
- end
163
-
164
-
165
- ### Get a subversion diff of the specified targets and return it. If no targets are
166
- ### specified, the current directory will be diffed instead.
167
- def get_svn_diff( *targets )
168
- targets << BASEDIR if targets.empty?
169
- trace "Getting svn diff for targets: %p" % [targets]
170
- log = IO.read( '|-' ) or exec 'svn', 'diff', *(targets.flatten)
171
-
172
- return log
173
- end
174
-
175
-
176
- ### Get a subversion status as an Array of tuples of the form:
177
- ### [ <status>, <path> ]
178
- def get_svn_status( *targets )
179
- targets << BASEDIR if targets.empty?
180
- trace "Getting svn status for targets: %p" % [targets]
181
- status = IO.read( '|-' ) or exec 'svn', 'st', '--ignore-externals', *(targets.flatten)
182
- entries = status.split( /\n/ ).
183
- select {|line| line !~ /^(\s*|--- .*)$/ }.
184
- collect do |line|
185
- flag, path = line.strip.split( /\s+/, 2 )
186
- [ flag, Pathname.new(path) ]
187
- end
188
-
189
- return entries
190
- end
191
-
192
-
193
- ### Return the URL of the latest timestamp in the tags directory.
194
- def get_latest_release_tag
195
- rooturl = get_svn_repo_root()
196
- releaseurl = rooturl + "/#{SVN_RELEASES_DIR}"
197
-
198
- tags = svn_ls( releaseurl ).grep( RELEASE_VERSION_PATTERN ).sort_by do |tag|
199
- tag[RELEASE_VERSION_PATTERN].split('.').collect {|i| Integer(i) }
200
- end
201
- return nil if tags.empty?
202
70
 
203
- return releaseurl + '/' + tags.last
204
- end
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
+
205
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
206
110
 
207
- ### Extract a diff from the specified subversion working +dir+ and return it.
208
- def make_svn_commit_log( dir='.' )
209
- diff = IO.read( '|-' ) or exec 'svn', 'diff'
210
- fail "No differences." if diff.empty?
211
111
 
212
- return diff
213
- end
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
214
120
 
215
121
 
216
- ### Extract the svn log from the specified subversion working +dir+,
217
- ### starting from rev +start+ and ending with rev +finish+, and return it.
218
- def make_svn_log( dir='.', start='PREV', finish='HEAD' )
219
- trace "svn log -r#{start}:#{finish} #{dir}"
220
- log = IO.read( '|-' ) or exec 'svn', 'log', "-r#{start}:#{finish}", dir
221
- fail "No log between #{start} and #{finish}." if log.empty?
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?
222
126
 
223
- return log
224
- end
127
+ cmd = ['svn', 'pg', 'svn:keywords', *files]
225
128
 
129
+ # trace "Executing: svn pg svn:keywords " + files.join(' ')
130
+ output = IO.read( '|-' ) or exec( 'svn', 'pg', 'svn:keywords', *files )
226
131
 
227
- ### Extract the verbose XML svn log from the specified subversion working +dir+,
228
- ### starting from rev +start+ and ending with rev +finish+, and return it.
229
- def make_xml_svn_log( dir='.', start='PREV', finish='HEAD' )
230
- trace "svn log --xml --verbose -r#{start}:#{finish} #{dir}"
231
- log = IO.read( '|-' ) or exec 'svn', 'log', '--verbose', '--xml', "-r#{start}:#{finish}", dir
232
- fail "No log between #{start} and #{finish}." if log.empty?
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
233
138
 
234
- return log
235
- end
139
+ return kwmap
140
+ end
236
141
 
237
142
 
238
- ### Create a changelog from the subversion log of the specified +dir+ and return it.
239
- def make_svn_changelog( dir='.' )
240
- require 'xml/libxml'
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
241
149
 
242
- changelog = ''
243
- path_prefix = '/' + get_svn_path( dir ) + '/'
244
150
 
245
- xmllog = make_xml_svn_log( dir, 0 )
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
246
156
 
247
- parser = XML::Parser.string( xmllog )
248
- root = parser.parse.root
249
- root.find( '//log/logentry' ).to_a.reverse.each do |entry|
250
- trace "Making a changelog entry for r%s" % [ entry['revision'] ]
251
157
 
252
- added = []
253
- deleted = []
254
- changed = []
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
255
164
 
256
- entry.find( 'paths/path').each do |path|
257
- pathname = path.content
258
- pathname.sub!( path_prefix , '' ) if pathname.count('/') > 1
165
+ trace 'svn ls of %s: %p' % [url, list] if $trace
259
166
 
260
- case path['action']
261
- when 'A', 'R'
262
- if path['copyfrom-path']
263
- verb = path['action'] == 'A' ? 'renamed' : 'copied'
264
- added << "%s\n#{FILE_INDENT}-> #{verb} from %s@r%s" % [
265
- pathname,
266
- path['copyfrom-path'],
267
- path['copyfrom-rev'],
268
- ]
269
- else
270
- added << "%s (new)" % [ pathname ]
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) ]
271
217
  end
272
-
273
- when 'M'
274
- changed << pathname
275
-
276
- when 'D'
277
- deleted << pathname
278
-
279
- else
280
- log "Unknown action %p in rev %d" % [ path['action'], entry['revision'] ]
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) }
281
230
  end
282
-
283
- end
284
-
285
- date = Time.parse( entry.find_first('date').content )
286
-
287
- # cvs2svn doesn't set 'author'
288
- author = 'unknown'
289
- if entry.find_first( 'author' )
290
- author = entry.find_first( 'author' ).content
291
- end
292
-
293
- msg = entry.find_first( 'msg' ).content
294
- rev = entry['revision']
295
-
296
- changelog << "-- #{date.rfc2822} by #{author} (r#{rev}) -----\n"
297
- changelog << " Added: " << humanize_file_list(added) << "\n" unless added.empty?
298
- changelog << " Changed: " << humanize_file_list(changed) << "\n" unless changed.empty?
299
- changelog << " Deleted: " << humanize_file_list(deleted) << "\n" unless deleted.empty?
300
- changelog << "\n"
301
-
302
- indent = msg[/^(\s*)/] + LOG_INDENT
303
-
304
- changelog << indent << msg.strip.gsub(/\n\s*/m, "\n#{indent}")
305
- changelog << "\n\n\n"
306
- end
307
-
308
- return changelog
309
- end
231
+ return nil if tags.empty?
310
232
 
233
+ return releaseurl + '/' + tags.last
234
+ end
311
235
 
312
- ### Returns a human-scannable file list by joining and truncating the list if it's too long.
313
- def humanize_file_list( list, indent=FILE_INDENT )
314
- listtext = list[0..5].join( "\n#{indent}" )
315
- if list.length > 5
316
- listtext << " (and %d other/s)" % [ list.length - 5 ]
317
- end
318
-
319
- return listtext
320
- end
321
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}"
322
241
 
323
- ### Add the list of +pathnames+ to the svn:ignore list.
324
- def svn_ignore_files( *pathnames )
325
- pathnames.flatten!
242
+ return svn_ls( branchesurl )
243
+ end
326
244
 
327
- map = pathnames.inject({}) do |map,path|
328
- map[ path.dirname ] ||= []
329
- map[ path.dirname ] << path.basename
330
- map
331
- end
332
245
 
333
- trace "Ignoring %d files in %d directories." % [ pathnames.length, map.length ]
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?
334
250
 
335
- map.each do |dir, files|
336
- trace " %s: %p" % [ dir, files ]
337
- io = open( '|-' ) or exec 'svn', 'pg', 'svn:ignore', dir
338
- ignorelist = io.read.strip
339
- ignorelist << "\n" << files.join("\n")
340
- system 'svn', 'ps', 'svn:ignore', ignorelist, dir
341
- end
342
- end
251
+ return diff
252
+ end
343
253
 
344
254
 
345
- ### Delete the files in the given +filelist+ after confirming with the user.
346
- def delete_extra_files( filelist )
347
- description = humanize_file_list( filelist, ' ' )
348
- log "Files to delete:\n ", description
349
- ask_for_confirmation( "Really delete them?", false ) do
350
- filelist.each do |f|
351
- rm_rf( f, :verbose => true )
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
352
263
  end
353
- end
354
- end
355
264
 
356
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'] ]
357
290
 
358
- ###
359
- ### Tasks
360
- ###
291
+ added = []
292
+ deleted = []
293
+ changed = []
361
294
 
362
- desc "Subversion tasks"
363
- namespace :svn do
295
+ entry.find( 'paths/path').each do |path|
296
+ pathname = path.content
297
+ pathname.sub!( path_prefix , '' ) if pathname.count('/') > 1
364
298
 
365
- desc "Copy the HEAD revision of the current #{SVN_TRUNK_DIR}/ to #{SVN_TAGS_DIR}/ with a " +
366
- "current timestamp."
367
- task :tag do
368
- svninfo = get_svn_info()
369
- tag = make_new_tag()
370
- svntrunk = svninfo['Repository Root'] + "/#{SVN_TRUNK_DIR}"
371
- svntagdir = svninfo['Repository Root'] + "/#{SVN_TAGS_DIR}"
372
- svntag = svntagdir + '/' + tag
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
373
311
 
374
- desc = "Tagging trunk as #{svntag}"
375
- ask_for_confirmation( desc ) do
376
- msg = prompt_with_default( "Commit log: ", "Tagging for code push" )
377
- run 'svn', 'cp', '-m', msg, svntrunk, svntag
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
378
348
  end
379
- end
380
349
 
381
350
 
382
- desc "Copy the HEAD revision of the current #{SVN_TRUNK_DIR}/ to #{SVN_BRANCHES_DIR} with a " +
383
- "user-specified name."
384
- task :branch, [:name] do |task, args|
385
- unless args.name
386
- args.name = prompt( "Branch name" ) or abort
387
- end
388
-
389
- svninfo = get_svn_info()
390
- svntrunk = Pathname.new( svninfo['Repository Root'] ) + SVN_TRUNK_DIR
391
- svnbranchdir = Pathname.new( svninfo['Repository Root'] ) + SVN_BRANCHES_DIR
392
- svnbranch = svnbranchdir + args.name
393
-
394
- desc = "Making a new branch: #{svnbranch}"
395
- ask_for_confirmation( desc ) do
396
- msg = prompt_with_default( "Commit log: ", "Making a '#{args.name}' branch" )
397
- run 'svn', 'cp', '-m', msg, svntrunk, svnbranch
398
- ask_for_confirmation( "Switch to the new branch?", false ) do
399
- run 'svn', 'sw', svnbranch
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 ]
400
356
  end
357
+
358
+ return listtext
401
359
  end
402
- end
403
-
404
-
405
- desc "Switch to the trunk if the working copy isn't there already."
406
- task :trunk do
407
- svninfo = get_svn_info()
408
- svntrunk = Pathname.new( svninfo['Repository Root'] ) + SVN_TRUNK_DIR
409
-
410
- if svninfo['URL'] != svntrunk.to_s
411
- log "Switching to #{svntrunk}"
412
- run 'svn', 'sw', svntrunk
413
- else
414
- log "You are already on trunk (#{svntrunk})"
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
415
381
  end
416
- end
417
-
418
-
419
- desc "Copy the most recent tag to #{SVN_RELEASES_DIR}/#{PKG_VERSION}"
420
- task :release do
421
- last_tag = get_latest_svn_timestamp_tag()
422
- svninfo = get_svn_info()
423
- svnroot = Pathname.new( svninfo['Repository Root'] )
424
- svntrunk = svnroot + SVN_TRUNK_DIR
425
- svnrel = svnroot + SVN_RELEASES_DIR
426
- release = PKG_VERSION
427
- svnrelease = svnrel + release
428
-
429
- unless svn_ls( svnrel.dirname ).include?( svnrel.basename.to_s + '/' )
430
- log "Releases path #{svnrel} does not exist."
431
- ask_for_confirmation( "To continue I'll need to create it." ) do
432
- run 'svn', 'mkdir', svnrel, '-m', 'Creating releases/ directory'
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
433
392
  end
434
- else
435
- trace "Found release dir #{svnrel}"
436
393
  end
437
394
 
438
- releases = svn_ls( svnrel ).collect {|name| name.sub(%r{/$}, '') }
439
- trace "Releases: %p" % [releases]
440
- if releases.include?( release )
441
- error "Version #{release} already has a branch (#{svnrelease}). Did you mean " +
442
- "to increment the version in #{VERSION_FILE}?"
443
- fail
444
- else
445
- trace "No #{release} version currently exists"
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
446
420
  end
447
-
448
- desc = "Tagging trunk as #{svnrelease}..."
449
- ask_for_confirmation( desc ) do
450
- msg = prompt_with_default( "Commit log: ", "Branching for release" )
451
- run 'svn', 'cp', '-m', msg, svntrunk, svnrelease
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
452
444
  end
453
- end
454
445
 
455
- ### Task for debugging the #get_target_args helper
456
- task :show_targets do
457
- $stdout.puts "Targets from ARGV (%p): %p" % [ARGV, get_target_args()]
458
- end
459
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('/') }
460
450
 
461
- desc "Generate a commit log"
462
- task :commitlog => [COMMIT_MSG_FILE]
463
-
464
- desc "Show the (pre-edited) commit log for the current directory"
465
- task :show_commitlog do
466
- puts make_svn_commit_log()
467
- end
468
-
451
+ unless args.name
452
+ log "Branches are:\n" + branches.collect {|br| " #{br}" }.join( "\n" )
469
453
 
470
- file COMMIT_MSG_FILE do
471
- diff = make_svn_commit_log()
472
-
473
- File.open( COMMIT_MSG_FILE, File::WRONLY|File::EXCL|File::CREAT ) do |fh|
474
- fh.print( diff )
475
- end
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
476
465
 
477
- editor = ENV['EDITOR'] || ENV['VISUAL'] || DEFAULT_EDITOR
478
- system editor, COMMIT_MSG_FILE
479
- unless $?.success?
480
- fail "Editor exited uncleanly."
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
481
470
  end
482
- end
471
+ task :sw => :switch
483
472
 
484
473
 
485
- desc "Update from Subversion"
486
- task :update do
487
- run 'svn', 'up', '--ignore-externals'
488
- end
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
489
486
 
490
487
 
491
- desc "Add/ignore any files that are unknown in the working copy"
492
- task :newfiles do
493
- log "Checking for new files..."
494
- entries = get_svn_status()
495
-
496
- unless entries.empty?
497
- files_to_add = []
498
- files_to_ignore = []
499
- files_to_delete = []
500
-
501
- entries.find_all {|entry| entry[0] == '?'}.each do |entry|
502
- action = prompt_with_default( " #{entry[1]}: (a)dd, (i)gnore, (s)kip (d)elete", 's' )
503
- case action
504
- when 'a'
505
- files_to_add << entry[1]
506
- when 'i'
507
- files_to_ignore << entry[1]
508
- when 'd'
509
- files_to_delete << entry[1]
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'
510
502
  end
503
+ else
504
+ trace "Found release dir #{svnrel}"
511
505
  end
512
-
513
- unless files_to_add.empty?
514
- run 'svn', 'add', *files_to_add
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
515
  end
516
-
517
- unless files_to_ignore.empty?
518
- svn_ignore_files( *files_to_ignore )
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
519
521
  end
522
+ end
520
523
 
521
- unless files_to_delete.empty?
522
- delete_extra_files( files_to_delete )
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
523
569
  end
524
570
  end
525
- end
526
- task :add => :newfiles
527
-
528
-
529
- desc "Check in all the changes in your current working copy"
530
- task :checkin => ['svn:update', 'svn:newfiles', 'test', 'svn:fix_keywords', COMMIT_MSG_FILE] do
531
- targets = get_target_args()
532
- $stderr.puts '---', File.read( COMMIT_MSG_FILE ), '---'
533
- ask_for_confirmation( "Continue with checkin?" ) do
534
- run 'svn', 'ci', '-F', COMMIT_MSG_FILE, targets
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
535
588
  rm_f COMMIT_MSG_FILE
536
589
  end
537
- end
538
- task :commit => :checkin
539
- task :ci => :checkin
540
-
541
-
542
- task :clean do
543
- rm_f COMMIT_MSG_FILE
544
- end
545
590
 
546
-
547
- desc "Check and fix any missing keywords for any files in the project which need them"
548
- task :fix_keywords do
549
- log "Checking subversion keywords..."
550
- paths = get_svn_filelist( BASEDIR ).
551
- select {|path| path.file? && path.to_s =~ KEYWORDED_FILEPATTERN }
552
-
553
- trace "Looking at %d paths for keywords:\n %p" % [paths.length, paths]
554
- kwmap = get_svn_keyword_map( paths )
555
-
556
- buf = ''
557
- PP.pp( kwmap, buf, 132 )
558
- trace "keyword map is: %s" % [buf]
559
-
560
- files_needing_fixups = paths.find_all do |path|
561
- (kwmap[path.to_s] & DEFAULT_KEYWORDS) != DEFAULT_KEYWORDS
562
- end
563
-
564
- unless files_needing_fixups.empty?
565
- $stderr.puts "Files needing keyword fixes: ",
566
- files_needing_fixups.collect {|f|
567
- " %s: %s" % [f, kwmap[f] ? kwmap[f].join(' ') : "(no keywords)"]
568
- }
569
- ask_for_confirmation( "Will add default keywords to these files." ) do
570
- run 'svn', 'ps', 'svn:keywords', DEFAULT_KEYWORDS.join(' '), *files_needing_fixups
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."
571
619
  end
572
- else
573
- log "Keywords are all up to date."
574
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
+
575
647
  end
576
648
 
577
-
578
- task :debug_helpers do
579
- methods = [
580
- :get_last_changed_rev,
581
- :get_latest_release_tag,
582
- :get_latest_svn_timestamp_tag,
583
- :get_svn_diff,
584
- :get_svn_filelist,
585
- :get_svn_info,
586
- :get_svn_keyword_map,
587
- :get_svn_path,
588
- :get_svn_repo_root,
589
- :get_svn_rev,
590
- :get_svn_status,
591
- :get_svn_url,
592
- :svn_ls,
593
- ]
594
- maxlen = methods.collect {|sym| sym.to_s.length }.max
595
-
596
- methods.each do |meth|
597
- res = send( meth )
598
- puts "%*s => %p" % [ maxlen, colorize(meth.to_s, :cyan), res ]
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()
599
662
  end
663
+ else
664
+ trace "Not defining subversion tasks: no #{SVN_DOTDIR}"
600
665
  end
601
- end
666
+
667
+ end # unless defined?( SVN_DOTDIR )
602
668