Class | Echoe |
In: |
lib/echoe/extensions.rb
lib/echoe.rb lib/echoe/extensions.rb lib/echoe.rb |
Parent: | Object |
Echoe includes some optional accessors for more advanced gem configuration.
For example, a simple Rakefile might look like this:
require 'echoe' Echoe.new("uncapitalizer") do |p| p.author = "Evan Weaver" p.summary = "A library that uncapitalizes strings. It's awesome." p.url = "http://www.uncapitalizer.com" p.docs_host = "uncapitalizer.com:~/www/files/doc/" p.runtime_dependencies = ["string_tools >=1.4.0"] end
See below for the full list.
Echoe supports signing gems. First, create yourself a public and private key:
gem cert --build you@yourmail.com
Move them somewhere secret, and add the following environment variables in your .bash_profile or similar:
export GEM_PRIVATE_KEY='/secret/path/to/gem-private_key.pem' export GEM_CERTIFICATE_CHAIN='/secret/path/to/gem-public_cert.pem'
Make sure your environment is up-to-date:
source ~/.bash_profile
Upload your public_cert.pem file to your website or Rubyforge project, and tell your users to add that certificate to their system via:
gem cert --add /path/to/public_cert.pem
Finally, package and release your project as normal. Now users can install your gem via:
sudo gem install gemname -P HighSecurity
Note that you can also set the key and certificate locations in the Rakefile itself. Finally, you can add p.require_signed = true to your Rakefile so that you don‘t accidentally release an unsigned gem if your key is missing.
Echoe does not force packages to depend on Echoe itself. Instead, it generates a gemspec from your Rakefile and includes that. Downstream repackagers can use the gemspec as-is to build new versions of your gem even without Echoe.
However, Echoe is added to the development_dependencies array so that users can automatically install it via gem install —development if they prefer. You can override this behavior by setting p.development_dependencies = [].
Echoe supports platform Rake targets to allow you to cross-package your gems. Just write the spec assuming RUBY_PLATFORM will be what you need it to be for each architecture, and then invoke Rake with the platform name when you‘re cross-packaging.
For example, on JRuby, rake package will build a generic -ruby 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 RUBY_PLATFORM =~ /java/ and setting p.platform = jruby, or you can run rake java package, which will set RUBY_PLATFORM and p.platform for you.
This way you can run rake java package, rake aix install, or whatever task you need and Echoe will behave just like you‘re packaging from within the target platform.
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 tests/setup.rb and it will be silently executed before the entire suite runs. Add a similar file tests/teardown.rb in your app to be executed at the end of the entire run.
Note; these files will only get executed if you run the tests via rake. Also, you can set the environment variable VERBOSE=1 to not hide the setup/teardown output.
Descriptive options:
Versioning options:
Common packaging options:
Testing options:
Uncommon packaging options:
Security options:
Publishing options:
Documentation options:
author | [RW] | user-configurable |
author | [RW] | user-configurable |
bin_files | [RW] | best left alone |
bin_files | [RW] | best left alone |
certificate_chain | [RW] | user-configurable |
certificate_chain | [RW] | user-configurable |
changelog | [RW] | user-configurable |
changelog | [RW] | user-configurable |
changelog_patterns | [RW] | best left alone |
changelog_patterns | [RW] | best left alone |
changes | [RW] | user-configurable |
changes | [RW] | user-configurable |
clean_pattern | [RW] | user-configurable |
clean_pattern | [RW] | user-configurable |
dependencies | [RW] | legacy |
dependencies | [RW] | legacy |
description | [RW] | user-configurable |
description | [RW] | user-configurable |
development_dependencies | [RW] | user-configurable |
development_dependencies | [RW] | user-configurable |
docs_host | [RW] | user-configurable |
docs_host | [RW] | user-configurable |
[RW] | user-configurable | |
[RW] | user-configurable | |
eval | [RW] | best left alone |
eval | [RW] | best left alone |
executable_pattern | [RW] | user-configurable |
executable_pattern | [RW] | user-configurable |
extension_pattern | [RW] | user-configurable |
extension_pattern | [RW] | user-configurable |
extensions | [RW] | legacy |
extensions | [RW] | legacy |
extra_deps | [RW] | legacy |
extra_deps | [RW] | legacy |
files | [RW] | best left alone |
files | [RW] | best left alone |
gemspec_format | [RW] | user-configurable |
gemspec_format | [RW] | user-configurable |
gemspec_name | [RW] | best left alone |
gemspec_name | [RW] | best left alone |
has_rdoc | [RW] | best left alone |
has_rdoc | [RW] | best left alone |
ignore_pattern | [RW] | user-configurable |
ignore_pattern | [RW] | user-configurable |
include_gemspec | [RW] | best left alone |
include_gemspec | [RW] | best left alone |
include_rakefile | [RW] | best left alone |
include_rakefile | [RW] | best left alone |
install_message | [RW] | user-configurable |
install_message | [RW] | user-configurable |
lib_files | [RW] | best left alone |
lib_files | [RW] | best left alone |
manifest_name | [RW] | user-configurable |
manifest_name | [RW] | user-configurable |
name | [RW] | best left alone |
name | [RW] | best left alone |
need_gem | [RW] | user-configurable |
need_gem | [RW] | user-configurable |
need_tar_gz | [RW] | user-configurable |
need_tar_gz | [RW] | user-configurable |
need_tgz | [RW] | user-configurable |
need_tgz | [RW] | user-configurable |
need_zip | [RW] | user-configurable |
need_zip | [RW] | user-configurable |
platform | [RW] | user-configurable |
platform | [RW] | user-configurable |
private_key | [RW] | user-configurable |
private_key | [RW] | user-configurable |
project | [RW] | user-configurable |
project | [RW] | user-configurable |
rakefile_name | [RW] | best left alone |
rakefile_name | [RW] | best left alone |
rcov_options | [RW] | user-configurable |
rcov_options | [RW] | user-configurable |
rdoc_files | [RW] | legacy |
rdoc_files | [RW] | legacy |
rdoc_options | [RW] | best left alone |
rdoc_options | [RW] | best left alone |
rdoc_pattern | [RW] | user-configurable |
rdoc_pattern | [RW] | user-configurable |
rdoc_template | [RW] | user-configurable |
rdoc_template | [RW] | user-configurable |
require_signed | [RW] | user-configurable |
require_signed | [RW] | user-configurable |
retain_gemspec | [RW] | best left alone |
retain_gemspec | [RW] | best left alone |
ruby_version | [RW] | user-configurable |
ruby_version | [RW] | user-configurable |
rubyforge_name | [RW] | best left alone |
rubyforge_name | [RW] | best left alone |
rubygems_version | [RW] | best left alone |
rubygems_version | [RW] | best left alone |
runtime_dependencies | [RW] | user-configurable |
runtime_dependencies | [RW] | user-configurable |
spec | [RW] | best left alone |
spec | [RW] | best left alone |
summary | [RW] | user-configurable |
summary | [RW] | user-configurable |
test_files | [RW] | best left alone |
test_files | [RW] | best left alone |
test_pattern | [RW] | user-configurable |
test_pattern | [RW] | user-configurable |
url | [RW] | user-configurable |
url | [RW] | user-configurable |
use_sudo | [RW] | best left alone |
use_sudo | [RW] | best left alone |
version | [RW] | user-configurable |
version | [RW] | user-configurable |
# File lib/echoe.rb, line 159 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: self.gemspec_format = :ruby 193: 194: title = (name.downcase == name ? name.capitalize : name) 195: self.rdoc_options = ['--line-numbers', '--inline-source', '--title', title] 196: 197: readme = Dir['*'].detect { |filename| filename =~ /^readme/i } 198: self.rdoc_options += ['--main', readme] if readme 199: 200: self.runtime_dependencies = [] 201: self.development_dependencies = ["echoe"] 202: self.manifest_name = "Manifest" 203: self.extension_pattern = ["ext/**/extconf.rb", "ext/extconf.rb"] 204: self.private_key = ENV['GEM_PRIVATE_KEY'] 205: self.require_signed = false 206: self.certificate_chain = ENV['GEM_CERTIFICATE_CHAIN'].to_s.split(/\,\s*/).compact 207: 208: self.need_gem = true 209: self.need_tar_gz = true 210: self.need_tgz = false 211: self.need_zip = false 212: self.platform = $platform 213: 214: self.include_rakefile = true 215: self.include_gemspec = true 216: self.gemspec_name = "#{name}.gemspec" 217: self.retain_gemspec = false 218: self.rakefile_name = "Rakefile" 219: self.rubygems_version = ">= 1.2" 220: 221: yield self if block_given? 222: 223: # legacy compatibility 224: self.runtime_dependencies = dependencies if dependencies and runtime_dependencies.empty? 225: self.runtime_dependencies = extra_deps if extra_deps and runtime_dependencies.empty? 226: self.project = rubyforge_name if rubyforge_name 227: self.rdoc_pattern = rdoc_files if rdoc_files 228: self.extension_pattern = extensions if extensions 229: 230: # read manifest 231: begin 232: self.files = File.read(manifest_name).split + 233: [(gemspec_name if include_gemspec)] + 234: [(rakefile_name if include_rakefile)] 235: self.files = files.compact.uniq 236: rescue Errno::ENOENT 237: unless ARGV.include? "manifest" 238: puts "Missing manifest. You can build one with 'rake manifest'." 239: exit 1 240: else 241: self.files = [] 242: end 243: end 244: 245: # snag version and changeset 246: self.version ||= _version 247: unless version 248: if File.exist? changelog 249: parsed = Array(changelog_patterns[:version]).map do |pattern| 250: open(changelog) do |log| 251: log.read[pattern, 1] 252: end 253: end.compact.first 254: raise "Could not parse version from #{changelog}" unless parsed 255: self.version = parsed.chomp(".").strip 256: else 257: raise "No #{changelog} found, and no version supplied in Rakefile." 258: end 259: end 260: 261: self.changes = if File.exist? changelog 262: Array(changelog_patterns[:changes]).map do |pattern| 263: open(changelog) do |log| 264: log.read[pattern, 1] 265: end 266: end.compact.first or "" 267: else 268: "" 269: end 270: 271: # set some post-defaults 272: self.certificate_chain = Array(certificate_chain).map {|file| File.expand_path(file)} 273: self.private_key = File.expand_path(private_key) if private_key 274: self.description = summary if description.empty? 275: self.summary = description if summary.empty? 276: self.clean_pattern = apply_pattern(clean_pattern) 277: self.extension_pattern = apply_pattern(extension_pattern, files) 278: self.ignore_pattern = apply_pattern(ignore_pattern) 279: self.rdoc_pattern = apply_pattern(rdoc_pattern, files) - [manifest_name] 280: self.executable_pattern = apply_pattern(executable_pattern, files) 281: self.test_pattern = apply_pattern(test_pattern) 282: 283: define_tasks 284: end
# File lib/echoe.rb, line 159 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: self.gemspec_format = :ruby 193: 194: title = (name.downcase == name ? name.capitalize : name) 195: self.rdoc_options = ['--line-numbers', '--inline-source', '--title', title] 196: 197: readme = Dir['*'].detect { |filename| filename =~ /^readme/i } 198: self.rdoc_options += ['--main', readme] if readme 199: 200: self.runtime_dependencies = [] 201: self.development_dependencies = ["echoe"] 202: self.manifest_name = "Manifest" 203: self.extension_pattern = ["ext/**/extconf.rb", "ext/extconf.rb"] 204: self.private_key = ENV['GEM_PRIVATE_KEY'] 205: self.require_signed = false 206: self.certificate_chain = ENV['GEM_CERTIFICATE_CHAIN'].to_s.split(/\,\s*/).compact 207: 208: self.need_gem = true 209: self.need_tar_gz = true 210: self.need_tgz = false 211: self.need_zip = false 212: self.platform = $platform 213: 214: self.include_rakefile = true 215: self.include_gemspec = true 216: self.gemspec_name = "#{name}.gemspec" 217: self.retain_gemspec = false 218: self.rakefile_name = "Rakefile" 219: self.rubygems_version = ">= 1.2" 220: 221: yield self if block_given? 222: 223: # legacy compatibility 224: self.runtime_dependencies = dependencies if dependencies and runtime_dependencies.empty? 225: self.runtime_dependencies = extra_deps if extra_deps and runtime_dependencies.empty? 226: self.project = rubyforge_name if rubyforge_name 227: self.rdoc_pattern = rdoc_files if rdoc_files 228: self.extension_pattern = extensions if extensions 229: 230: # read manifest 231: begin 232: self.files = File.read(manifest_name).split + 233: [(gemspec_name if include_gemspec)] + 234: [(rakefile_name if include_rakefile)] 235: self.files = files.compact.uniq 236: rescue Errno::ENOENT 237: unless ARGV.include? "manifest" 238: puts "Missing manifest. You can build one with 'rake manifest'." 239: exit 1 240: else 241: self.files = [] 242: end 243: end 244: 245: # snag version and changeset 246: self.version ||= _version 247: unless version 248: if File.exist? changelog 249: parsed = Array(changelog_patterns[:version]).map do |pattern| 250: open(changelog) do |log| 251: log.read[pattern, 1] 252: end 253: end.compact.first 254: raise "Could not parse version from #{changelog}" unless parsed 255: self.version = parsed.chomp(".").strip 256: else 257: raise "No #{changelog} found, and no version supplied in Rakefile." 258: end 259: end 260: 261: self.changes = if File.exist? changelog 262: Array(changelog_patterns[:changes]).map do |pattern| 263: open(changelog) do |log| 264: log.read[pattern, 1] 265: end 266: end.compact.first or "" 267: else 268: "" 269: end 270: 271: # set some post-defaults 272: self.certificate_chain = Array(certificate_chain).map {|file| File.expand_path(file)} 273: self.private_key = File.expand_path(private_key) if private_key 274: self.description = summary if description.empty? 275: self.summary = description if summary.empty? 276: self.clean_pattern = apply_pattern(clean_pattern) 277: self.extension_pattern = apply_pattern(extension_pattern, files) 278: self.ignore_pattern = apply_pattern(ignore_pattern) 279: self.rdoc_pattern = apply_pattern(rdoc_pattern, files) - [manifest_name] 280: self.executable_pattern = apply_pattern(executable_pattern, files) 281: self.test_pattern = apply_pattern(test_pattern) 282: 283: define_tasks 284: end
# File lib/echoe/extensions.rb, line 14 14: def self.silence 15: if !ENV['VERBOSE'] 16: stdout, stderr = $stdout.clone, $stderr.clone 17: $stdout.reopen(File.new('/tmp/stdout.echoe', 'w')) 18: $stderr.reopen(File.new('/tmp/stderr.echoe', 'w')) 19: begin 20: yield 21: ensure 22: $stdout.reopen(stdout) 23: $stderr.reopen(stderr) 24: end 25: else 26: yield 27: end 28: end
# File lib/echoe/extensions.rb, line 14 14: def self.silence 15: if !ENV['VERBOSE'] 16: stdout, stderr = $stdout.clone, $stderr.clone 17: $stdout.reopen(File.new('/tmp/stdout.echoe', 'w')) 18: $stderr.reopen(File.new('/tmp/stderr.echoe', 'w')) 19: begin 20: yield 21: ensure 22: $stdout.reopen(stdout) 23: $stderr.reopen(stderr) 24: end 25: else 26: yield 27: end 28: end
# File lib/echoe.rb, line 286 286: def apply_pattern(pattern, files = nil) 287: files ||= Dir['**/**'] 288: case pattern 289: when String, Array 290: files & (Array(pattern).map do |p| 291: Dir.glob(p) 292: end.flatten) 293: when Regexp 294: files.select do |file| 295: file =~ pattern 296: end 297: when FileList 298: pattern.each do |ignorefile| 299: ignorefiles = File.open(ignorefile).to_a.map(&:chomp) 300: files = files.select do |file| 301: ignorefiles.map { |i| File.fnmatch(i, file) }.include?(true) 302: end 303: end 304: files 305: else 306: [] 307: end 308: end
# File lib/echoe.rb, line 286 286: def apply_pattern(pattern, files = nil) 287: files ||= Dir['**/**'] 288: case pattern 289: when String, Array 290: files & (Array(pattern).map do |p| 291: Dir.glob(p) 292: end.flatten) 293: when Regexp 294: files.select do |file| 295: file =~ pattern 296: end 297: when FileList 298: pattern.each do |ignorefile| 299: ignorefiles = File.open(ignorefile).to_a.map(&:chomp) 300: files = files.select do |file| 301: ignorefiles.map { |i| File.fnmatch(i, file) }.include?(true) 302: end 303: end 304: files 305: else 306: [] 307: end 308: end
# File lib/echoe.rb, line 310 310: def define_tasks 311: 312: ### Packaging and Installing 313: 314: self.spec = Gem::Specification.new do |s| 315: s.name = name 316: s.version = version 317: # s.specification_version = 3 318: s.summary = summary 319: s.author = Array(author).join(", ") 320: s.email = email 321: s.homepage = url 322: s.rubyforge_project = project 323: s.post_install_message = install_message if install_message 324: s.description = description 325: s.required_ruby_version = ruby_version 326: s.required_rubygems_version = rubygems_version if rubygems_version 327: s.platform = platform 328: s.rdoc_options = rdoc_options 329: s.extra_rdoc_files = rdoc_pattern 330: 331: if private_key and File.exist? private_key 332: s.signing_key = private_key 333: s.cert_chain = certificate_chain 334: end 335: 336: runtime_dependencies.each do |dep| 337: dep = dep.split(" ") if dep.is_a? String 338: s.add_runtime_dependency(*dep) 339: end 340: 341: development_dependencies.each do |dep| 342: dep = dep.split(" ") if dep.is_a? String 343: s.add_development_dependency(*dep) 344: end 345: 346: s.files = files 347: 348: s.bindir = if executable_pattern.any? 349: executable_pattern[0].split("/")[0] 350: else 351: "bin" 352: end 353: 354: s.executables = executable_pattern.map do |file| 355: file[(s.bindir.length + 1)..-1] 356: end 357: 358: dirs = Dir['{lib,ext}'] 359: s.extensions = extension_pattern if extension_pattern.any? 360: s.require_paths = dirs unless dirs.empty? 361: s.has_rdoc = has_rdoc 362: 363: if File.exist? "test/test_all.rb" 364: s.test_file = "test/test_all.rb" 365: else 366: s.test_files = test_pattern 367: end 368: 369: if eval 370: s.instance_eval &eval 371: end 372: 373: end 374: 375: self.lib_files = spec.files.grep(/^lib/) 376: self.bin_files = spec.files.grep(/^bin/) 377: self.test_files = spec.files.grep(/^test/) 378: 379: Rake::GemPackageTask.new(spec) do |pkg| 380: pkg.need_tar = @need_tgz 381: pkg.need_tar_gz = @need_tar_gz 382: pkg.need_zip = @need_zip 383: end 384: 385: task :build_gemspec do 386: # Construct the gemspec file, if needed. 387: if include_gemspec 388: File.open(gemspec_name, 'w') do |f| 389: case gemspec_format 390: when :yaml 391: spec.to_yaml.split("\n").each do |line| 392: # Don't publish any information about the private key or certificate chain 393: f.puts line unless line =~ /signing_key|cert_chain|\.pem/ 394: end 395: when :ruby 396: f.puts spec.to_ruby 397: else 398: raise "Unknown gemspec format #{gemspec_format.inspect}. Supported formats: :ruby and :yaml" 399: end 400: end 401: end 402: end 403: 404: # Chain it to the gemspec task prerequisite 405: task gemspec_name.to_sym => [:build_gemspec] 406: 407: task :package do 408: # Chain some cleanup tasks to the default :package task. 409: # Remove the gemfile if it wasn't actually requested. 410: unless @need_gem 411: puts " Gem file not requested. Removed." 412: system "rm pkg/*.gem" 413: end 414: # Remove the generated gemspec once the packaging is done, to discourage people from modifying it by hand. 415: if include_gemspec and File.exist? gemspec_name and not retain_gemspec 416: File.delete gemspec_name 417: end 418: 419: # Test signing status 420: if private_key and File.exist? private_key 421: puts "Signing gem." 422: else 423: raise "Key required, but not found. Maybe you forget to set ENV['GEM_PRIVATE_KEY']?" if require_signed 424: puts "Private key not found; gem will not be signed." 425: end 426: puts "Targeting \"#{platform}\" platform." 427: end 428: 429: desc 'Install the gem' 430: task :install => [:clean, :package, :uninstall] do 431: system "#{'sudo' if use_sudo} gem install pkg/*.gem -P MediumSecurity --no-update-sources" 432: end 433: 434: namespace :install do 435: desc 'Install the gem including development dependencies' 436: task :development => [:clean, :package, :uninstall] do 437: system "#{'sudo' if use_sudo} gem install pkg/*.gem -P MediumSecurity --no-update-sources --development" 438: end 439: end 440: 441: desc 'Uninstall the gem' 442: task :uninstall do 443: system "#{'sudo' if use_sudo} gem uninstall #{name} -a -I -x" 444: end 445: 446: desc 'Package and upload the release to Rubyforge' 447: task :release => [:clean, :package] do |t| 448: 449: say "\n" 450: if agree "Release #{name}-#{version} to Rubyforge? " 451: pkg = "pkg/#{name}-#{version}" 452: pkg_gem = pkg + ".gem" 453: pkg_tar = pkg + ".tgz" 454: pkg_tar_gz = pkg + ".tar.gz" 455: pkg_zip = pkg + ".zip" 456: 457: rf = RubyForge.new.configure 458: puts "Logging in" 459: rf.login 460: 461: c = rf.userconfig 462: c["release_notes"] = description if description 463: c["release_changes"] = changes if changes 464: c["preformatted"] = false 465: 466: files = [(@need_tgz ? pkg_tar : nil), 467: (@need_tar_gz ? pkg_tar_gz : nil), 468: (@need_zip ? pkg_zip : nil), 469: (@need_gem ? pkg_gem : nil)].compact 470: 471: puts "Releasing #{name} v. #{version}" 472: self.version = self.version.ljust(3) 473: 474: rf.add_release project, name, version, *files 475: end 476: 477: end 478: 479: ### Extension building 480: 481: task :lib do 482: directory "lib" 483: end 484: 485: if extension_pattern.any? 486: 487: desc "Compile the binary extension module" 488: task :compile => [:lib] do 489: extension_pattern.each do |extension| 490: ext_dir = File.dirname(extension) 491: lib_target = nil 492: Dir.chdir(ext_dir) do 493: ruby File.basename(extension) 494: system(PLATFORM =~ /win32/ ? 'nmake' : 'make') 495: lib_target = open('Makefile').readlines.grep(/target_prefix = /).first.split('=').last.chomp("\n").strip 496: end 497: Dir["#{ext_dir}/*.#{Config::CONFIG['DLEXT']}"].each do |file| 498: dir = "lib/#{lib_target}/".gsub('//', '/') 499: mkdir_p dir 500: cp file, dir 501: end 502: end 503: end 504: 505: task :test => [:compile] 506: 507: end 508: 509: ### Cross-platform targets 510: 511: Gem::Specification::PLATFORM_CROSS_TARGETS.each do |target| 512: task target do 513: reset_target target 514: end 515: end 516: 517: ### Documentation 518: 519: Rake::RDocTask.new(:docs) do |rd| 520: # rd.main = Dir['*'].detect {|f| f =~ /^readme/i} 521: rd.options += Array(rdoc_options) 522: 523: rd.rdoc_dir = 'doc' 524: rd.rdoc_files.push(*rdoc_pattern) 525: 526: if rdoc_template 527: rd.template = rdoc_template 528: elsif ENV['RDOC_TEMPLATE'] 529: rd.template = ENV['RDOC_TEMPLATE'] 530: end 531: end 532: 533: task :doc => [:redocs] 534: 535: desc "Publish documentation to #{docs_host ? "'#{docs_host}'" : "rubyforge"}" 536: task :publish_docs => [:clean, :docs] do 537: 538: local_dir = 'doc' 539: remote_dir_name = project 540: remote_dir_name += "/#{name}" if project != name 541: 542: unless docs_host 543: config = YAML.load(File.read(File.expand_path("~/.rubyforge/user-config.yml"))) 544: pub = Rake::SshDirPublisher.new "#{config["username"]}@rubyforge.org", 545: "/var/www/gforge-projects/#{remote_dir_name}", 546: local_dir 547: if project != name then 548: def pub.upload 549: begin 550: super 551: rescue 552: # project directory probably doesn't exist, transfer as a whole 553: cmd = "scp -qr #{local_dir} #{host}:#{remote_dir}" 554: puts "Uploading: #{cmd}" 555: system(cmd) 556: end 557: end 558: end 559: pub.upload 560: else 561: # you may need ssh keys configured for this to work 562: host, dir = docs_host.split(":") 563: dir.chomp!("/") 564: 565: # XXX too dangerous? 566: cmd = "ssh #{host} 'rm -rf #{dir}/#{remote_dir_name}'" 567: puts "Deleting existing docs: #{cmd}" 568: system(cmd) 569: 570: cmd = "scp -qr #{local_dir} #{host}:#{dir}/#{remote_dir_name}" 571: puts "Uploading: #{cmd}" 572: system(cmd) 573: end 574: end 575: 576: desc 'Generate a release announcement, edit it, and post it to Rubyforge.' 577: task :announce do 578: 579: filename = "/tmp/#{name}_#{version}_announcement.txt" 580: 581: if !File.exist?(filename) or agree "Overwrite existing announcement file? " 582: File.open(filename, 'w') do |f| 583: f.write "Subject: #{name.capitalize} #{version}\n\n" 584: f.write "#{name.capitalize} has been updated to #{version}. #{name.capitalize} is #{summary.uncapitalize}\n\n" 585: f.write "Changes in this version: #{changes.sub(/^\s*[\w\d\.]+\s+/, '').uncapitalize}\n\n" unless changes.empty? 586: f.write "More information is available at #{url} .\n\n" unless url.empty? 587: end 588: end 589: 590: begin 591: editor = ENV['EDITOR'] || 'nano' 592: system("#{editor} #{filename}") or raise "Editor '#{editor}' failed to start" 593: puts File.open(filename).read 594: end while !agree "Done editing? " 595: 596: if agree "Publish announcement to Rubyforge? " 597: File.open(filename).readlines.detect { |line| line =~ /Subject: (.*)/ } 598: subject = $1 or raise "Subject line seems to have disappeared" 599: 600: body = File.open(filename).readlines.reject { |line| line =~ /Subject: / }.join.gsub("\n\n\n", "\n\n") 601: 602: rf = RubyForge.new.configure 603: rf.login 604: rf.post_news(project, subject, body) 605: puts "Published." 606: File.delete filename 607: end 608: end 609: 610: ### Clean 611: 612: desc 'Clean up auto-generated files' 613: task :clean do 614: puts "Cleaning" 615: clean_pattern.each do |file| 616: if File.exist?(file) 617: puts "- #{file}" 618: rm_rf file 619: end 620: end 621: end 622: 623: ### Manifest 624: 625: desc "Build a Manifest list" 626: task :manifest => [:clean] do 627: puts "Building Manifest" 628: old_files = files 629: files = [] 630: Dir['**/**'].each do |file| 631: next unless file 632: next if ignore_pattern.include?(file) 633: next if File.directory?(file) 634: next if !include_rakefile and file == rakefile_name 635: files << file 636: end 637: 638: files << rakefile_name if include_rakefile 639: files << manifest_name 640: files.uniq! 641: 642: File.open(manifest_name, 'w').puts(files) 643: 644: (files | old_files).sort.each do |file| 645: next if file == gemspec_name 646: sign = " " 647: if old_files.include?(file) and !files.include?(file) 648: sign = "-" 649: elsif files.include?(file) and !old_files.include?(file) 650: sign = "+" 651: end 652: puts "#{sign} #{file}" 653: end 654: end 655: 656: task :build_manifest => :manifest 657: 658: ### Testing 659: 660: if test_pattern.any? 661: 662: Rake::TestTask.new(:test_inner) do |t| 663: t.libs = ['lib', 'ext', 'bin', 'test'] 664: t.test_files = test_pattern 665: t.verbose = true 666: end 667: 668: desc "Run the test suite" 669: task :test do 670: if File.exist? 'test/setup.rb' 671: Echoe.silence do 672: puts "Setting up test environment" 673: system("ruby test/setup.rb") 674: end 675: end 676: begin 677: test = Rake::Task[:test_inner] 678: if test.respond_to? :already_invoked= 679: # Method provided by MultiRails 680: test.already_invoked = false 681: end 682: test.invoke 683: ensure 684: if File.exist? 'test/teardown.rb' 685: Echoe.silence do 686: puts "Tearing down test environment" 687: system("ruby test/teardown.rb") 688: end 689: end 690: end 691: end 692: 693: end 694: 695: task :default => :test 696: 697: if defined? Rcov 698: Rcov::RcovTask.new(:coverage) do |t| 699: t.test_files = test_pattern 700: t.rcov_opts << rcov_options if rcov_options 701: t.verbose = true 702: end 703: task :rcov => :coverage 704: end 705: 706: end
# File lib/echoe.rb, line 310 310: def define_tasks 311: 312: ### Packaging and Installing 313: 314: self.spec = Gem::Specification.new do |s| 315: s.name = name 316: s.version = version 317: # s.specification_version = 3 318: s.summary = summary 319: s.author = Array(author).join(", ") 320: s.email = email 321: s.homepage = url 322: s.rubyforge_project = project 323: s.post_install_message = install_message if install_message 324: s.description = description 325: s.required_ruby_version = ruby_version 326: s.required_rubygems_version = rubygems_version if rubygems_version 327: s.platform = platform 328: s.rdoc_options = rdoc_options 329: s.extra_rdoc_files = rdoc_pattern 330: 331: if private_key and File.exist? private_key 332: s.signing_key = private_key 333: s.cert_chain = certificate_chain 334: end 335: 336: runtime_dependencies.each do |dep| 337: dep = dep.split(" ") if dep.is_a? String 338: s.add_runtime_dependency(*dep) 339: end 340: 341: development_dependencies.each do |dep| 342: dep = dep.split(" ") if dep.is_a? String 343: s.add_development_dependency(*dep) 344: end 345: 346: s.files = files 347: 348: s.bindir = if executable_pattern.any? 349: executable_pattern[0].split("/")[0] 350: else 351: "bin" 352: end 353: 354: s.executables = executable_pattern.map do |file| 355: file[(s.bindir.length + 1)..-1] 356: end 357: 358: dirs = Dir['{lib,ext}'] 359: s.extensions = extension_pattern if extension_pattern.any? 360: s.require_paths = dirs unless dirs.empty? 361: s.has_rdoc = has_rdoc 362: 363: if File.exist? "test/test_all.rb" 364: s.test_file = "test/test_all.rb" 365: else 366: s.test_files = test_pattern 367: end 368: 369: if eval 370: s.instance_eval &eval 371: end 372: 373: end 374: 375: self.lib_files = spec.files.grep(/^lib/) 376: self.bin_files = spec.files.grep(/^bin/) 377: self.test_files = spec.files.grep(/^test/) 378: 379: Rake::GemPackageTask.new(spec) do |pkg| 380: pkg.need_tar = @need_tgz 381: pkg.need_tar_gz = @need_tar_gz 382: pkg.need_zip = @need_zip 383: end 384: 385: task :build_gemspec do 386: # Construct the gemspec file, if needed. 387: if include_gemspec 388: File.open(gemspec_name, 'w') do |f| 389: case gemspec_format 390: when :yaml 391: spec.to_yaml.split("\n").each do |line| 392: # Don't publish any information about the private key or certificate chain 393: f.puts line unless line =~ /signing_key|cert_chain|\.pem/ 394: end 395: when :ruby 396: f.puts spec.to_ruby 397: else 398: raise "Unknown gemspec format #{gemspec_format.inspect}. Supported formats: :ruby and :yaml" 399: end 400: end 401: end 402: end 403: 404: # Chain it to the gemspec task prerequisite 405: task gemspec_name.to_sym => [:build_gemspec] 406: 407: task :package do 408: # Chain some cleanup tasks to the default :package task. 409: # Remove the gemfile if it wasn't actually requested. 410: unless @need_gem 411: puts " Gem file not requested. Removed." 412: system "rm pkg/*.gem" 413: end 414: # Remove the generated gemspec once the packaging is done, to discourage people from modifying it by hand. 415: if include_gemspec and File.exist? gemspec_name and not retain_gemspec 416: File.delete gemspec_name 417: end 418: 419: # Test signing status 420: if private_key and File.exist? private_key 421: puts "Signing gem." 422: else 423: raise "Key required, but not found. Maybe you forget to set ENV['GEM_PRIVATE_KEY']?" if require_signed 424: puts "Private key not found; gem will not be signed." 425: end 426: puts "Targeting \"#{platform}\" platform." 427: end 428: 429: desc 'Install the gem' 430: task :install => [:clean, :package, :uninstall] do 431: system "#{'sudo' if use_sudo} gem install pkg/*.gem -P MediumSecurity --no-update-sources" 432: end 433: 434: namespace :install do 435: desc 'Install the gem including development dependencies' 436: task :development => [:clean, :package, :uninstall] do 437: system "#{'sudo' if use_sudo} gem install pkg/*.gem -P MediumSecurity --no-update-sources --development" 438: end 439: end 440: 441: desc 'Uninstall the gem' 442: task :uninstall do 443: system "#{'sudo' if use_sudo} gem uninstall #{name} -a -I -x" 444: end 445: 446: desc 'Package and upload the release to Rubyforge' 447: task :release => [:clean, :package] do |t| 448: 449: say "\n" 450: if agree "Release #{name}-#{version} to Rubyforge? " 451: pkg = "pkg/#{name}-#{version}" 452: pkg_gem = pkg + ".gem" 453: pkg_tar = pkg + ".tgz" 454: pkg_tar_gz = pkg + ".tar.gz" 455: pkg_zip = pkg + ".zip" 456: 457: rf = RubyForge.new.configure 458: puts "Logging in" 459: rf.login 460: 461: c = rf.userconfig 462: c["release_notes"] = description if description 463: c["release_changes"] = changes if changes 464: c["preformatted"] = false 465: 466: files = [(@need_tgz ? pkg_tar : nil), 467: (@need_tar_gz ? pkg_tar_gz : nil), 468: (@need_zip ? pkg_zip : nil), 469: (@need_gem ? pkg_gem : nil)].compact 470: 471: puts "Releasing #{name} v. #{version}" 472: self.version = self.version.ljust(3) 473: 474: rf.add_release project, name, version, *files 475: end 476: 477: end 478: 479: ### Extension building 480: 481: task :lib do 482: directory "lib" 483: end 484: 485: if extension_pattern.any? 486: 487: desc "Compile the binary extension module" 488: task :compile => [:lib] do 489: extension_pattern.each do |extension| 490: ext_dir = File.dirname(extension) 491: lib_target = nil 492: Dir.chdir(ext_dir) do 493: ruby File.basename(extension) 494: system(PLATFORM =~ /win32/ ? 'nmake' : 'make') 495: lib_target = open('Makefile').readlines.grep(/target_prefix = /).first.split('=').last.chomp("\n").strip 496: end 497: Dir["#{ext_dir}/*.#{Config::CONFIG['DLEXT']}"].each do |file| 498: dir = "lib/#{lib_target}/".gsub('//', '/') 499: mkdir_p dir 500: cp file, dir 501: end 502: end 503: end 504: 505: task :test => [:compile] 506: 507: end 508: 509: ### Cross-platform targets 510: 511: Gem::Specification::PLATFORM_CROSS_TARGETS.each do |target| 512: task target do 513: reset_target target 514: end 515: end 516: 517: ### Documentation 518: 519: Rake::RDocTask.new(:docs) do |rd| 520: # rd.main = Dir['*'].detect {|f| f =~ /^readme/i} 521: rd.options += Array(rdoc_options) 522: 523: rd.rdoc_dir = 'doc' 524: rd.rdoc_files.push(*rdoc_pattern) 525: 526: if rdoc_template 527: rd.template = rdoc_template 528: elsif ENV['RDOC_TEMPLATE'] 529: rd.template = ENV['RDOC_TEMPLATE'] 530: end 531: end 532: 533: task :doc => [:redocs] 534: 535: desc "Publish documentation to #{docs_host ? "'#{docs_host}'" : "rubyforge"}" 536: task :publish_docs => [:clean, :docs] do 537: 538: local_dir = 'doc' 539: remote_dir_name = project 540: remote_dir_name += "/#{name}" if project != name 541: 542: unless docs_host 543: config = YAML.load(File.read(File.expand_path("~/.rubyforge/user-config.yml"))) 544: pub = Rake::SshDirPublisher.new "#{config["username"]}@rubyforge.org", 545: "/var/www/gforge-projects/#{remote_dir_name}", 546: local_dir 547: if project != name then 548: def pub.upload 549: begin 550: super 551: rescue 552: # project directory probably doesn't exist, transfer as a whole 553: cmd = "scp -qr #{local_dir} #{host}:#{remote_dir}" 554: puts "Uploading: #{cmd}" 555: system(cmd) 556: end 557: end 558: end 559: pub.upload 560: else 561: # you may need ssh keys configured for this to work 562: host, dir = docs_host.split(":") 563: dir.chomp!("/") 564: 565: # XXX too dangerous? 566: cmd = "ssh #{host} 'rm -rf #{dir}/#{remote_dir_name}'" 567: puts "Deleting existing docs: #{cmd}" 568: system(cmd) 569: 570: cmd = "scp -qr #{local_dir} #{host}:#{dir}/#{remote_dir_name}" 571: puts "Uploading: #{cmd}" 572: system(cmd) 573: end 574: end 575: 576: desc 'Generate a release announcement, edit it, and post it to Rubyforge.' 577: task :announce do 578: 579: filename = "/tmp/#{name}_#{version}_announcement.txt" 580: 581: if !File.exist?(filename) or agree "Overwrite existing announcement file? " 582: File.open(filename, 'w') do |f| 583: f.write "Subject: #{name.capitalize} #{version}\n\n" 584: f.write "#{name.capitalize} has been updated to #{version}. #{name.capitalize} is #{summary.uncapitalize}\n\n" 585: f.write "Changes in this version: #{changes.sub(/^\s*[\w\d\.]+\s+/, '').uncapitalize}\n\n" unless changes.empty? 586: f.write "More information is available at #{url} .\n\n" unless url.empty? 587: end 588: end 589: 590: begin 591: editor = ENV['EDITOR'] || 'nano' 592: system("#{editor} #{filename}") or raise "Editor '#{editor}' failed to start" 593: puts File.open(filename).read 594: end while !agree "Done editing? " 595: 596: if agree "Publish announcement to Rubyforge? " 597: File.open(filename).readlines.detect { |line| line =~ /Subject: (.*)/ } 598: subject = $1 or raise "Subject line seems to have disappeared" 599: 600: body = File.open(filename).readlines.reject { |line| line =~ /Subject: / }.join.gsub("\n\n\n", "\n\n") 601: 602: rf = RubyForge.new.configure 603: rf.login 604: rf.post_news(project, subject, body) 605: puts "Published." 606: File.delete filename 607: end 608: end 609: 610: ### Clean 611: 612: desc 'Clean up auto-generated files' 613: task :clean do 614: puts "Cleaning" 615: clean_pattern.each do |file| 616: if File.exist?(file) 617: puts "- #{file}" 618: rm_rf file 619: end 620: end 621: end 622: 623: ### Manifest 624: 625: desc "Build a Manifest list" 626: task :manifest => [:clean] do 627: puts "Building Manifest" 628: old_files = files 629: files = [] 630: Dir['**/**'].each do |file| 631: next unless file 632: next if ignore_pattern.include?(file) 633: next if File.directory?(file) 634: next if !include_rakefile and file == rakefile_name 635: files << file 636: end 637: 638: files << rakefile_name if include_rakefile 639: files << manifest_name 640: files.uniq! 641: 642: File.open(manifest_name, 'w').puts(files) 643: 644: (files | old_files).sort.each do |file| 645: next if file == gemspec_name 646: sign = " " 647: if old_files.include?(file) and !files.include?(file) 648: sign = "-" 649: elsif files.include?(file) and !old_files.include?(file) 650: sign = "+" 651: end 652: puts "#{sign} #{file}" 653: end 654: end 655: 656: task :build_manifest => :manifest 657: 658: ### Testing 659: 660: if test_pattern.any? 661: 662: Rake::TestTask.new(:test_inner) do |t| 663: t.libs = ['lib', 'ext', 'bin', 'test'] 664: t.test_files = test_pattern 665: t.verbose = true 666: end 667: 668: desc "Run the test suite" 669: task :test do 670: if File.exist? 'test/setup.rb' 671: Echoe.silence do 672: puts "Setting up test environment" 673: system("ruby test/setup.rb") 674: end 675: end 676: begin 677: test = Rake::Task[:test_inner] 678: if test.respond_to? :already_invoked= 679: # Method provided by MultiRails 680: test.already_invoked = false 681: end 682: test.invoke 683: ensure 684: if File.exist? 'test/teardown.rb' 685: Echoe.silence do 686: puts "Tearing down test environment" 687: system("ruby test/teardown.rb") 688: end 689: end 690: end 691: end 692: 693: end 694: 695: task :default => :test 696: 697: if defined? Rcov 698: Rcov::RcovTask.new(:coverage) do |t| 699: t.test_files = test_pattern 700: t.rcov_opts << rcov_options if rcov_options 701: t.verbose = true 702: end 703: task :rcov => :coverage 704: end 705: 706: end