Chrononaut-echoe 3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,697 @@
1
+ require 'rubygems'
2
+
3
+ require 'rubyforge'
4
+
5
+ require 'rake'
6
+ require 'rake/clean'
7
+ require 'rake/contrib/sshpublisher'
8
+ require 'rake/gempackagetask'
9
+ require 'rake/rdoctask'
10
+ require 'rake/testtask'
11
+ require 'rbconfig'
12
+ require 'open-uri'
13
+ require 'highline/import'
14
+
15
+ begin
16
+ require 'rcov/rcovtask'
17
+ rescue LoadError
18
+ end
19
+
20
+ begin
21
+ require 'load_multi_rails_rake_tasks'
22
+ rescue LoadError
23
+ end
24
+
25
+ $LOAD_PATH << File.dirname(__FILE__)
26
+ require 'echoe/platform'
27
+ require 'echoe/extensions'
28
+ require 'echoe/client'
29
+
30
+ =begin rdoc
31
+
32
+ Echoe includes some optional accessors for more advanced gem configuration.
33
+
34
+ For example, a simple <tt>Rakefile</tt> might look like this:
35
+
36
+ require 'echoe'
37
+
38
+ Echoe.new("uncapitalizer") do |p|
39
+ p.author = "Evan Weaver"
40
+ p.summary = "A library that uncapitalizes strings. It's awesome."
41
+ p.url = "http://www.uncapitalizer.com"
42
+ p.docs_host = "uncapitalizer.com:~/www/files/doc/"
43
+ p.runtime_dependencies = ["string_tools >=1.4.0"]
44
+ end
45
+
46
+ See below for the full list.
47
+
48
+ == Signing gems
49
+
50
+ Echoe supports signing gems. First, create yourself a public and private key:
51
+ gem cert --build you@yourmail.com
52
+
53
+ Move them somewhere secret, and add the following environment variables in your <tt>.bash_profile</tt> or similar:
54
+ export GEM_PRIVATE_KEY='/secret/path/to/gem-private_key.pem'
55
+ export GEM_CERTIFICATE_CHAIN='/secret/path/to/gem-public_cert.pem'
56
+
57
+ Make sure your environment is up-to-date:
58
+ source ~/.bash_profile
59
+
60
+ Upload your <tt>public_cert.pem</tt> file to your website or Rubyforge project, and tell your users to add that certificate to their system via:
61
+ gem cert --add /path/to/public_cert.pem
62
+
63
+ Finally, package and release your project as normal. Now users can install your gem via:
64
+ sudo gem install gemname -P HighSecurity
65
+
66
+ Note that you can also set the key and certificate locations in the Rakefile itself. Finally, you can add <tt>p.require_signed = true</tt> to your <tt>Rakefile</tt> so that you don't accidentally release an unsigned gem if your key is missing.
67
+
68
+ == Metadependencies
69
+
70
+ Echoe does not force packages to depend on Echoe itself. Instead, it generates a <tt>gemspec</tt> from your <tt>Rakefile</tt> and includes that. Downstream repackagers can use the <tt>gemspec</tt> as-is to build new versions of your gem even without Echoe.
71
+
72
+ However, Echoe is added to the <tt>development_dependencies</tt> array so that users can automatically install it via <tt>gem install --development</tt> if they prefer. You can override this behavior by setting <tt>p.development_dependencies = []</tt>.
73
+
74
+ == Cross-packaging
75
+
76
+ Echoe supports platform Rake targets to allow you to cross-package your gems. Just write the spec assuming <tt>RUBY_PLATFORM</tt> will be what you need it to be for each architecture, and then invoke Rake with the platform name when you're cross-packaging.
77
+
78
+ For example, on JRuby, <tt>rake package</tt> will build a generic <tt>-ruby</tt> type gem. But if you want to include a Java-specific extension, you can do one of two things. You can package from within JRuby by checking if <tt>RUBY_PLATFORM =~ /java/</tt> and setting <tt>p.platform = jruby</tt>, or you can run <tt>rake java package</tt>, which will set <tt>RUBY_PLATFORM</tt> and <tt>p.platform</tt> for you.
79
+
80
+ This way you can run <tt>rake java package</tt>, <tt>rake aix install</tt>, or whatever task you need and Echoe will behave just like you're packaging from within the target platform.
81
+
82
+ == Test environment setup and teardown
83
+
84
+ For some applications, you may need to setup and teardown environment state for the entire test suite. This is especially common for integration tests that may need to spawn an external daemon. To support this, you can add a file <tt>tests/setup.rb</tt> and it will be silently executed before the entire suite runs. Add a similar file <tt>tests/teardown.rb</tt> in your app to be executed at the end of the entire run.
85
+
86
+ Note; these files will only get executed if you run the tests via <tt>rake</tt>. Also, you can set the environment variable <tt>VERBOSE=1</tt> to not hide the setup/teardown output.
87
+
88
+ == Accessor options
89
+
90
+ Descriptive options:
91
+
92
+ * <tt>author</tt> - Your name.
93
+ * <tt>email</tt> - Your email address.
94
+ * <tt>description</tt> - A more detailed description of the library.
95
+ * <tt>summary</tt> - A shorter description of the library.
96
+ * <tt>url</tt> - A url for the library.
97
+ * <tt>install_message</tt> - A message to display after the gem is installed.
98
+
99
+ Versioning options:
100
+
101
+ * <tt>version</tt> - A string for the version number. Parsed from CHANGELOG otherwise.
102
+ * <tt>changes</tt> - A string describing the most recent changes. Parsed from CHANGELOG otherwise.
103
+
104
+ Common packaging options:
105
+
106
+ * <tt>runtime_dependencies</tt> - An array of runtime dependencies for this gem. For example, <tt>['mongrel', 'activesupport >= 2.0.2']</tt>.
107
+ * <tt>development_dependencies</tt> - An array of development dependencies for this gem. For example, <tt>['rake >=0.7.1']</tt>.
108
+ * <tt>extension_pattern</tt> - A filename array, glob array, or regex for extension files that need to be run at install time. Defaults to <tt>"ext/**/extconf.rb"</tt>.
109
+
110
+ Testing options:
111
+
112
+ * <tt>clean_pattern</tt> - A filename array, glob array, or regex for files that should be removed when <tt>rake clean</tt> is run.
113
+ * <tt>test_pattern</tt> - A filename array, glob array, or regex for test runners. Overridden by <tt>"test/test_all.rb"</tt>, if it exists.
114
+ * <tt>rcov_options</tt> - Any extra flags to pass to RCov when coverage reports are run.
115
+
116
+ Uncommon packaging options:
117
+ * <tt>platform</tt> - What platform this gem is for.
118
+ * <tt>manifest_name</tt> - The name of the manifest file. Defaults to <tt>Manifest</tt>.
119
+ * <tt>need_gem</tt> - Whether to generate a gem package. Defaults to <tt>true</tt>.
120
+ * <tt>need_tar_gz</tt> - Whether to generate a <tt>.tar.gz</tt> package. Defaults to <tt>true</tt>.
121
+ * <tt>need_tgz</tt> - Whether to generate a <tt>.tgz</tt> package. Defaults to <tt>false</tt>.
122
+ * <tt>need_zip</tt> - Whether to generate a <tt>.zip</tt> package. Defaults to <tt>false</tt>.
123
+ * <tt>include_rakefile</tt> - Include the Rakefile directly within the package. Defaults to <tt>true</tt>.
124
+ * <tt>include_gemspec</tt> - Include the generated gemspec file within the package. Defaults to <tt>true</tt>.
125
+ * <tt>ruby_version</tt> - Version string for which Ruby to require (for example, <tt>'>= 1.8.4'</tt>.
126
+ * <tt>eval</tt> - Accepts a proc to be evaluated in the context of the Gem::Specification object. This allows you to set more unusual gemspec options.
127
+ * <tt>ignore_pattern</tt> - A filename array, glob array, or regex for pathnames that should be ignored when building the manifest.
128
+ * <tt>executable_pattern</tt> - A filename array, glob array, or regex for files that should be installed as wrapped executables.
129
+
130
+ Security options:
131
+
132
+ * <tt>private_key</tt> - The path to your gem private key. Defaults to ENV['GEM_PRIVATE_KEY'], if available. This accessor is not published in the resulting gemspec.
133
+ * <tt>certificate_chain</tt> - An array representing your certificate authorization chain. If no one else has signed your certificate, just set it to your own cert. Defaults to ENV['GEM_CERTIFICATE_CHAIN'], if available. This accessor is not published in the resulting gemspec.
134
+ * <tt>require_signed</tt> - Force Echoe to refuse to package your gem if it's not properly signed. Default false.
135
+
136
+ Publishing options:
137
+
138
+ * <tt>project</tt> - The name of the Rubyforge project to upload to. Defaults to the name of the gem.
139
+ * <tt>docs_host</tt> - A host and filesystem path to publish the documentation to. Defaults to the Rubyforge project.
140
+
141
+ Documentation options:
142
+
143
+ * <tt>rdoc_pattern</tt> - A filename array, glob array, or regex for filenames that should be passed to RDoc.
144
+ * <tt>rdoc_template</tt> - A path to an RDoc template. Defaults to the generic template.
145
+
146
+ =end
147
+
148
+ class Echoe
149
+
150
+ # user-configurable
151
+ attr_accessor :author, :changes, :clean_pattern, :description, :email, :runtime_dependencies, :development_dependencies, :need_tgz, :need_tar_gz, :need_gem, :need_zip, :rdoc_pattern, :project, :summary, :test_pattern, :url, :version, :docs_host, :rdoc_template, :manifest_name, :install_message, :extension_pattern, :private_key, :certificate_chain, :require_signed, :ruby_version, :platform, :ignore_pattern, :executable_pattern, :changelog, :rcov_options
152
+
153
+ # best left alone
154
+ attr_accessor :name, :lib_files, :test_files, :bin_files, :spec, :rdoc_options, :rubyforge_name, :has_rdoc, :include_gemspec, :include_rakefile, :gemspec_name, :eval, :files, :changelog_patterns, :rubygems_version, :use_sudo
155
+
156
+ # legacy
157
+ attr_accessor :extra_deps, :rdoc_files, :extensions, :dependencies
158
+
159
+ def initialize(name, _version = nil)
160
+ # Defaults
161
+
162
+ self.name = name
163
+ self.project = name.downcase
164
+ self.changelog = "CHANGELOG"
165
+ self.url = ""
166
+ self.author = ""
167
+ self.email = ""
168
+ self.clean_pattern = ["pkg", "doc", 'build/*', '**/coverage', '**/*.o', '**/*.so', '**/*.a', 'lib/*-*', '**/*.log', "{ext,lib}/*.{bundle,so,obj,pdb,lib,def,exp}", "ext/Makefile", "{ext,lib}/**/*.{bundle,so,obj,pdb,lib,def,exp}", "ext/**/Makefile", "pkg", "*.gem", ".config"]
169
+ self.test_pattern = File.exist?("test/test_all.rb") ? "test/test_all.rb" : ['test/**/test_*.rb', 'test/**/*_test.rb']
170
+ self.ignore_pattern = /^(pkg|doc)|\.svn|CVS|\.bzr|\.DS|\.git/
171
+
172
+ self.changelog_patterns = {
173
+ :version => [
174
+ /^\s*v([\d\.]+)(\.|\s|$)/,
175
+ /\s*\*\s*([\d\.]+)\s*\*\s*$/
176
+ ],
177
+ :changes => [
178
+ /^\s*v([\d\.]+\. .*)/,
179
+ /\*\s*[\d\.]+\s*\*\s*(.*)\*\s*[\d\.]+\s*\*$/m
180
+ ]
181
+ }
182
+
183
+ self.description = ""
184
+ self.summary = ""
185
+ self.install_message = nil
186
+ self.executable_pattern = /^bin\//
187
+ self.has_rdoc = true
188
+ self.use_sudo = RUBY_PLATFORM !~ /mswin32|cygwin/
189
+ self.rcov_options = []
190
+ self.rdoc_pattern = /^(lib|bin|tasks|ext)|^README|^CHANGELOG|^TODO|^LICENSE|^COPYING$/
191
+
192
+ title = (name.downcase == name ? name.capitalize : name)
193
+ self.rdoc_options = ['--line-numbers', '--inline-source', '--title', title]
194
+
195
+ readme = Dir['*'].detect { |filename| filename =~ /^readme/i }
196
+ self.rdoc_options += ['--main', readme] if readme
197
+
198
+ self.runtime_dependencies = []
199
+ self.development_dependencies = ["echoe"]
200
+ self.manifest_name = "Manifest"
201
+ self.extension_pattern = ["ext/**/extconf.rb", "ext/extconf.rb"]
202
+ self.private_key = ENV['GEM_PRIVATE_KEY']
203
+ self.require_signed = false
204
+ self.certificate_chain = ENV['GEM_CERTIFICATE_CHAIN'].to_s.split(/\,\s*/).compact
205
+
206
+ self.need_gem = true
207
+ self.need_tar_gz = true
208
+ self.need_tgz = false
209
+ self.need_zip = false
210
+ self.platform = $platform
211
+
212
+ self.include_rakefile = true
213
+ self.include_gemspec = true
214
+ self.gemspec_name = "#{name}.gemspec"
215
+ self.rubygems_version = ">= 1.2"
216
+
217
+ yield self if block_given?
218
+
219
+ # legacy compatibility
220
+ self.runtime_dependencies = dependencies if dependencies and runtime_dependencies.empty?
221
+ self.runtime_dependencies = extra_deps if extra_deps and runtime_dependencies.empty?
222
+ self.project = rubyforge_name if rubyforge_name
223
+ self.rdoc_pattern = rdoc_files if rdoc_files
224
+ self.extension_pattern = extensions if extensions
225
+
226
+ # read manifest
227
+ begin
228
+ self.files = File.read(manifest_name).split +
229
+ [(gemspec_name if include_gemspec)] +
230
+ [("Rakefile" if include_rakefile)]
231
+ self.files = files.compact.uniq
232
+ rescue Errno::ENOENT
233
+ unless ARGV.include? "manifest"
234
+ puts "Missing manifest. You can build one with 'rake manifest'."
235
+ exit
236
+ else
237
+ self.files = []
238
+ end
239
+ end
240
+
241
+ # snag version and changeset
242
+ self.version ||= _version
243
+ unless version
244
+ if File.exist? changelog
245
+ parsed = Array(changelog_patterns[:version]).map do |pattern|
246
+ open(changelog) do |log|
247
+ log.read[pattern, 1]
248
+ end
249
+ end.compact.first
250
+ raise "Could not parse version from #{changelog}" unless parsed
251
+ self.version = parsed.chomp(".").strip
252
+ else
253
+ raise "No #{changelog} found, and no version supplied in Rakefile."
254
+ end
255
+ end
256
+
257
+ self.changes = if File.exist? changelog
258
+ Array(changelog_patterns[:changes]).map do |pattern|
259
+ open(changelog) do |log|
260
+ log.read[pattern, 1]
261
+ end
262
+ end.compact.first or ""
263
+ else
264
+ ""
265
+ end
266
+
267
+ # set some post-defaults
268
+ self.certificate_chain = Array(certificate_chain).map {|file| File.expand_path(file)}
269
+ self.private_key = File.expand_path(private_key) if private_key
270
+ self.description = summary if description.empty?
271
+ self.summary = description if summary.empty?
272
+ self.clean_pattern = apply_pattern(clean_pattern)
273
+ self.extension_pattern = apply_pattern(extension_pattern, files)
274
+ self.ignore_pattern = apply_pattern(ignore_pattern)
275
+ self.rdoc_pattern = apply_pattern(rdoc_pattern, files) - [manifest_name]
276
+ self.executable_pattern = apply_pattern(executable_pattern, files)
277
+ self.test_pattern = apply_pattern(test_pattern)
278
+
279
+ define_tasks
280
+ end
281
+
282
+ def apply_pattern(pattern, files = nil)
283
+ files ||= Dir['**/**']
284
+ case pattern
285
+ when String, Array
286
+ files & (Array(pattern).map do |p|
287
+ Dir.glob(p)
288
+ end.flatten)
289
+ when Regexp
290
+ files.select do |file|
291
+ file =~ pattern
292
+ end
293
+ when FileList
294
+ pattern.each do |ignorefile|
295
+ ignorefiles = File.open(ignorefile).to_a.map(&:chomp)
296
+ files = files.select do |file|
297
+ ignorefiles.map { |i| File.fnmatch(i, file) }.include?(true)
298
+ end
299
+ end
300
+ files
301
+ else
302
+ []
303
+ end
304
+ end
305
+
306
+ def define_tasks
307
+
308
+ ### Packaging and Installing
309
+
310
+ self.spec = Gem::Specification.new do |s|
311
+ s.name = name
312
+ s.version = version
313
+ # s.specification_version = 3
314
+ s.summary = summary
315
+ s.author = Array(author).join(", ")
316
+ s.email = email
317
+ s.homepage = url
318
+ s.rubyforge_project = project
319
+ s.post_install_message = install_message if install_message
320
+ s.description = description
321
+ s.required_ruby_version = ruby_version
322
+ s.required_rubygems_version = rubygems_version if rubygems_version
323
+ s.platform = platform
324
+ s.rdoc_options = rdoc_options
325
+ s.extra_rdoc_files = rdoc_pattern
326
+
327
+ if private_key and File.exist? private_key
328
+ s.signing_key = private_key
329
+ s.cert_chain = certificate_chain
330
+ end
331
+
332
+ runtime_dependencies.each do |dep|
333
+ dep = dep.split(" ") if dep.is_a? String
334
+ s.add_runtime_dependency(*dep)
335
+ end
336
+
337
+ development_dependencies.each do |dep|
338
+ dep = dep.split(" ") if dep.is_a? String
339
+ s.add_development_dependency(*dep)
340
+ end
341
+
342
+ s.files = files
343
+
344
+ s.bindir = if executable_pattern.any?
345
+ executable_pattern[0].split("/")[0]
346
+ else
347
+ "bin"
348
+ end
349
+
350
+ s.executables = executable_pattern.map do |file|
351
+ file[(s.bindir.length + 1)..-1]
352
+ end
353
+
354
+ dirs = Dir['{lib,ext}']
355
+ s.extensions = extension_pattern if extension_pattern.any?
356
+ s.require_paths = dirs unless dirs.empty?
357
+ s.has_rdoc = has_rdoc
358
+
359
+ if File.exist? "test/test_all.rb"
360
+ s.test_file = "test/test_all.rb"
361
+ else
362
+ s.test_files = test_pattern
363
+ end
364
+
365
+ if eval
366
+ s.instance_eval &eval
367
+ end
368
+
369
+ end
370
+
371
+ self.lib_files = spec.files.grep(/^lib/)
372
+ self.bin_files = spec.files.grep(/^bin/)
373
+ self.test_files = spec.files.grep(/^test/)
374
+
375
+ Rake::GemPackageTask.new(spec) do |pkg|
376
+ pkg.need_tar = @need_tgz
377
+ pkg.need_tar_gz = @need_tar_gz
378
+ pkg.need_zip = @need_zip
379
+ end
380
+
381
+ task :build_gemspec do
382
+ # Construct the gemspec file, if needed.
383
+ if include_gemspec
384
+ File.open(gemspec_name, 'w') do |f|
385
+ spec.to_yaml.split("\n").each do |line|
386
+ # Don't publish any information about the private key or certificate chain
387
+ f.puts line unless line =~ /signing_key|cert_chain|\.pem/
388
+ end
389
+ end
390
+ end
391
+ end
392
+
393
+ # Chain it to the gemspec task prerequisite
394
+ task gemspec_name.to_sym => [:build_gemspec]
395
+
396
+ task :package do
397
+ # Chain some cleanup tasks to the default :package task.
398
+ # Remove the gemfile if it wasn't actually requested.
399
+ unless @need_gem
400
+ puts " Gem file not requested. Removed."
401
+ system "rm pkg/*.gem"
402
+ end
403
+ # Remove the generated gemspec once the packaging is done, to discourage people from modifying it by hand.
404
+ if include_gemspec and File.exist? gemspec_name
405
+ File.delete gemspec_name
406
+ end
407
+
408
+ # Test signing status
409
+ if private_key and File.exist? private_key
410
+ puts "Signing gem."
411
+ else
412
+ raise "Key required, but not found. Maybe you forget to set ENV['GEM_PRIVATE_KEY']?" if require_signed
413
+ puts "Private key not found; gem will not be signed."
414
+ end
415
+ puts "Targeting \"#{platform}\" platform."
416
+ end
417
+
418
+ desc 'Install the gem'
419
+ task :install => [:clean, :package, :uninstall] do
420
+ system "#{'sudo' if use_sudo} gem install pkg/*.gem -P MediumSecurity --no-update-sources"
421
+ end
422
+
423
+ namespace :install do
424
+ desc 'Install the gem including development dependencies'
425
+ task :development => [:clean, :package, :uninstall] do
426
+ system "#{'sudo' if use_sudo} gem install pkg/*.gem -P MediumSecurity --no-update-sources --development"
427
+ end
428
+ end
429
+
430
+ desc 'Uninstall the gem'
431
+ task :uninstall do
432
+ system "#{'sudo' if use_sudo} gem uninstall #{name} -a -I -x"
433
+ end
434
+
435
+ desc 'Package and upload the release to Rubyforge'
436
+ task :release => [:clean, :package] do |t|
437
+
438
+ say "\n"
439
+ if agree "Release #{name}-#{version} to Rubyforge? "
440
+ pkg = "pkg/#{name}-#{version}"
441
+ pkg_gem = pkg + ".gem"
442
+ pkg_tar = pkg + ".tgz"
443
+ pkg_tar_gz = pkg + ".tar.gz"
444
+ pkg_zip = pkg + ".zip"
445
+
446
+ rf = RubyForge.new.configure
447
+ puts "Logging in"
448
+ rf.login
449
+
450
+ c = rf.userconfig
451
+ c["release_notes"] = description if description
452
+ c["release_changes"] = changes if changes
453
+ c["preformatted"] = false
454
+
455
+ files = [(@need_tgz ? pkg_tar : nil),
456
+ (@need_tar_gz ? pkg_tar_gz : nil),
457
+ (@need_zip ? pkg_zip : nil),
458
+ (@need_gem ? pkg_gem : nil)].compact
459
+
460
+ puts "Releasing #{name} v. #{version}"
461
+ self.version = self.version.ljust(3)
462
+
463
+ rf.add_release project, name, version, *files
464
+ end
465
+
466
+ end
467
+
468
+ ### Extension building
469
+
470
+ task :lib do
471
+ directory "lib"
472
+ end
473
+
474
+ if extension_pattern.any?
475
+
476
+ desc "Compile the binary extension module"
477
+ task :compile => [:lib] do
478
+ extension_pattern.each do |extension|
479
+ ext_dir = File.dirname(extension)
480
+ lib_target = nil
481
+ Dir.chdir(ext_dir) do
482
+ ruby File.basename(extension)
483
+ system(PLATFORM =~ /win32/ ? 'nmake' : 'make')
484
+ lib_target = open('Makefile').readlines.grep(/target_prefix = /).first.split('=').last.chomp("\n").strip
485
+ end
486
+ Dir["#{ext_dir}/*.#{Config::CONFIG['DLEXT']}"].each do |file|
487
+ dir = "lib/#{lib_target}/".gsub('//', '/')
488
+ mkdir_p dir
489
+ cp file, dir
490
+ end
491
+ end
492
+ end
493
+
494
+ task :test => [:compile]
495
+
496
+ end
497
+
498
+ ### Cross-platform targets
499
+
500
+ Gem::Specification::PLATFORM_CROSS_TARGETS.each do |target|
501
+ task target do
502
+ reset_target target
503
+ end
504
+ end
505
+
506
+ ### Documentation
507
+
508
+ Rake::RDocTask.new(:docs) do |rd|
509
+ # rd.main = Dir['*'].detect {|f| f =~ /^readme/i}
510
+ rd.options += Array(rdoc_options)
511
+
512
+ rd.rdoc_dir = 'doc'
513
+ rd.rdoc_files.push(*rdoc_pattern)
514
+
515
+ if rdoc_template
516
+ rd.template = rdoc_template
517
+ elsif ENV['RDOC_TEMPLATE']
518
+ rd.template = ENV['RDOC_TEMPLATE']
519
+ end
520
+ end
521
+
522
+ task :doc => [:redocs]
523
+
524
+ desc "Publish documentation to #{docs_host ? "'#{docs_host}'" : "rubyforge"}"
525
+ task :publish_docs => [:clean, :docs] do
526
+
527
+ local_dir = 'doc'
528
+ remote_dir_name = project
529
+ remote_dir_name += "/#{name}" if project != name
530
+
531
+ unless docs_host
532
+ config = YAML.load(File.read(File.expand_path("~/.rubyforge/user-config.yml")))
533
+ pub = Rake::SshDirPublisher.new "#{config["username"]}@rubyforge.org",
534
+ "/var/www/gforge-projects/#{remote_dir_name}",
535
+ local_dir
536
+ if project != name then
537
+ def pub.upload
538
+ begin
539
+ super
540
+ rescue
541
+ # project directory probably doesn't exist, transfer as a whole
542
+ cmd = "scp -qr #{local_dir} #{host}:#{remote_dir}"
543
+ puts "Uploading: #{cmd}"
544
+ system(cmd)
545
+ end
546
+ end
547
+ end
548
+ pub.upload
549
+ else
550
+ # you may need ssh keys configured for this to work
551
+ host, dir = docs_host.split(":")
552
+ dir.chomp!("/")
553
+
554
+ # XXX too dangerous?
555
+ cmd = "ssh #{host} 'rm -rf #{dir}/#{remote_dir_name}'"
556
+ puts "Deleting existing docs: #{cmd}"
557
+ system(cmd)
558
+
559
+ cmd = "scp -qr #{local_dir} #{host}:#{dir}/#{remote_dir_name}"
560
+ puts "Uploading: #{cmd}"
561
+ system(cmd)
562
+ end
563
+ end
564
+
565
+ desc 'Generate a release announcement, edit it, and post it to Rubyforge.'
566
+ task :announce do
567
+
568
+ filename = "/tmp/#{name}_#{version}_announcement.txt"
569
+
570
+ if !File.exist?(filename) or agree "Overwrite existing announcement file? "
571
+ File.open(filename, 'w') do |f|
572
+ f.write "Subject: #{name.capitalize} #{version}\n\n"
573
+ f.write "#{name.capitalize} has been updated to #{version}. #{name.capitalize} is #{summary.uncapitalize}\n\n"
574
+ f.write "Changes in this version: #{changes.sub(/^\s*[\w\d\.]+\s+/, '').uncapitalize}\n\n" unless changes.empty?
575
+ f.write "More information is available at #{url} .\n\n" unless url.empty?
576
+ end
577
+ end
578
+
579
+ begin
580
+ editor = ENV['EDITOR'] || 'nano'
581
+ system("#{editor} #{filename}") or raise "Editor '#{editor}' failed to start"
582
+ puts File.open(filename).read
583
+ end while !agree "Done editing? "
584
+
585
+ if agree "Publish announcement to Rubyforge? "
586
+ File.open(filename).readlines.detect { |line| line =~ /Subject: (.*)/ }
587
+ subject = $1 or raise "Subject line seems to have disappeared"
588
+
589
+ body = File.open(filename).readlines.reject { |line| line =~ /Subject: / }.join.gsub("\n\n\n", "\n\n")
590
+
591
+ rf = RubyForge.new.configure
592
+ rf.login
593
+ rf.post_news(project, subject, body)
594
+ puts "Published."
595
+ File.delete filename
596
+ end
597
+ end
598
+
599
+ ### Clean
600
+
601
+ desc 'Clean up auto-generated files'
602
+ task :clean do
603
+ puts "Cleaning"
604
+ clean_pattern.each do |file|
605
+ if File.exist?(file)
606
+ puts "- #{file}"
607
+ rm_rf file
608
+ end
609
+ end
610
+ end
611
+
612
+ ### Manifest
613
+
614
+ desc "Build a Manifest list"
615
+ task :manifest => [:clean] do
616
+ puts "Building Manifest"
617
+ old_files = files
618
+ files = []
619
+ Dir['**/**'].each do |file|
620
+ next unless file
621
+ next if ignore_pattern.include?(file)
622
+ next if File.directory?(file)
623
+ next if !include_rakefile and file == "Rakefile"
624
+ files << file
625
+ end
626
+
627
+ files << "Rakefile" if include_rakefile
628
+ files << manifest_name
629
+ files.uniq!
630
+
631
+ File.open(manifest_name, 'w').puts(files)
632
+
633
+ (files | old_files).sort.each do |file|
634
+ next if file == gemspec_name
635
+ sign = " "
636
+ if old_files.include?(file) and !files.include?(file)
637
+ sign = "-"
638
+ elsif files.include?(file) and !old_files.include?(file)
639
+ sign = "+"
640
+ end
641
+ puts "#{sign} #{file}"
642
+ end
643
+ end
644
+
645
+ task :build_manifest => :manifest
646
+
647
+ ### Testing
648
+
649
+ if test_pattern.any?
650
+
651
+ Rake::TestTask.new(:test_inner) do |t|
652
+ t.libs = ['lib', 'ext', 'bin', 'test']
653
+ t.test_files = test_pattern
654
+ t.verbose = true
655
+ end
656
+
657
+ desc "Run the test suite"
658
+ task :test do
659
+ if File.exist? 'test/setup.rb'
660
+ Echoe.silence do
661
+ puts "Setting up test environment"
662
+ system("ruby test/setup.rb")
663
+ end
664
+ end
665
+ begin
666
+ test = Rake::Task[:test_inner]
667
+ if test.respond_to? :already_invoked=
668
+ # Method provided by MultiRails
669
+ test.already_invoked = false
670
+ end
671
+ test.invoke
672
+ ensure
673
+ if File.exist? 'test/teardown.rb'
674
+ Echoe.silence do
675
+ puts "Tearing down test environment"
676
+ system("ruby test/teardown.rb")
677
+ end
678
+ end
679
+ end
680
+ end
681
+
682
+ end
683
+
684
+ task :default => :test
685
+
686
+ if defined? Rcov
687
+ Rcov::RcovTask.new(:coverage) do |t|
688
+ t.test_files = test_pattern
689
+ t.rcov_opts << rcov_options if rcov_options
690
+ t.verbose = true
691
+ end
692
+ task :rcov => :coverage
693
+ end
694
+
695
+ end
696
+ end
697
+