rsanheim-echoe 3

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