Class | Echoe |
In: |
lib/echoe/extensions.rb
lib/echoe/platform.rb lib/echoe.rb lib/echoe/extensions.rb lib/echoe/platform.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." 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.
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 |
gem_bin | [RW] | best left alone |
gem_bin | [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 |
spec_pattern | [RW] | user-configurable |
spec_pattern | [RW] | user-configurable |
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 163 163: def initialize(name, _version = nil) 164: # Defaults 165: 166: self.name = name 167: self.project = name.downcase 168: self.changelog = "CHANGELOG" 169: self.url = "" 170: self.author = "" 171: self.email = "" 172: self.clean_pattern = ["pkg", "doc", 'build/*', '**/coverage', '**/*.o', '**/*.so', '**/*.a', '**/*.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"] 173: self.test_pattern = File.exist?("test/test_all.rb") ? "test/test_all.rb" : ['test/**/test_*.rb', 'test/**/*_test.rb'] 174: self.spec_pattern = "spec/**/*_spec.rb" 175: self.ignore_pattern = /^(pkg|doc)|\.svn|CVS|\.bzr|\.DS|\.git/ 176: 177: self.changelog_patterns = { 178: :version => [ 179: /^\s*v([\d\.]+)(\.|\s|$)/, 180: /\s*\*\s*([\d\.]+)\s*\*\s*$/ 181: ], 182: :changes => [ 183: /^\s*v([\d\.]+\. .*)/, 184: /\*\s*[\d\.]+\s*\*\s*(.*)\*\s*[\d\.]+\s*\*$/m 185: ] 186: } 187: 188: self.description = "" 189: self.summary = "" 190: self.install_message = nil 191: self.executable_pattern = /^bin\// 192: self.has_rdoc = true 193: self.use_sudo = !Platform.windows? 194: self.gem_bin = "gem#{Platform.suffix}" 195: self.rcov_options = [] 196: self.rdoc_pattern = /^(lib|bin|tasks|ext)|^README|^CHANGELOG|^TODO|^LICENSE|^COPYING$/ 197: 198: self.gemspec_format = :ruby 199: 200: title = (name.downcase == name ? name.capitalize : name) 201: self.rdoc_options = ['--line-numbers', '--inline-source', '--title', title] 202: 203: readme = Dir['*'].detect { |filename| filename =~ /^readme/i } 204: self.rdoc_options += ['--main', readme] if readme 205: 206: self.runtime_dependencies = [] 207: self.development_dependencies = [] # These appear to not work at all 208: self.manifest_name = "Manifest" 209: self.extension_pattern = ["ext/**/extconf.rb", "ext/extconf.rb"] 210: self.private_key = ENV['GEM_PRIVATE_KEY'] 211: self.require_signed = false 212: self.certificate_chain = ENV['GEM_CERTIFICATE_CHAIN'].to_s.split(/\,\s*/).compact 213: 214: self.need_gem = true 215: self.need_tar_gz = true 216: self.need_tgz = false 217: self.need_zip = false 218: self.platform = $platform 219: 220: self.include_rakefile = true 221: self.include_gemspec = true 222: self.gemspec_name = "#{name}.gemspec" 223: self.retain_gemspec = false 224: self.rakefile_name = "Rakefile" 225: self.rubygems_version = ">= 1.2" 226: 227: yield self if block_given? 228: 229: # legacy compatibility 230: self.runtime_dependencies = dependencies if dependencies and runtime_dependencies.empty? 231: self.runtime_dependencies = extra_deps if extra_deps and runtime_dependencies.empty? 232: self.project = rubyforge_name if rubyforge_name 233: self.rdoc_pattern = rdoc_files if rdoc_files 234: self.extension_pattern = extensions if extensions 235: 236: # read manifest 237: begin 238: self.files = File.readlines(manifest_name).map { |x| x.strip } + 239: [(gemspec_name if include_gemspec)] + 240: [(rakefile_name if include_rakefile)] 241: self.files = files.compact.uniq 242: rescue Errno::ENOENT 243: unless ARGV.include? "manifest" 244: puts "Missing manifest. You can build one with 'rake manifest'." 245: exit 1 246: else 247: self.files = [] 248: end 249: end 250: 251: # snag version and changeset 252: self.version ||= _version 253: unless version 254: if File.exist? changelog 255: parsed = Array(changelog_patterns[:version]).map do |pattern| 256: open(changelog) do |log| 257: log.read[pattern, 1] 258: end 259: end.compact.first 260: raise "Could not parse version from #{changelog}" unless parsed 261: self.version = parsed.chomp(".").strip 262: else 263: raise "No #{changelog} found, and no version supplied in Rakefile." 264: end 265: end 266: 267: unless self.changes 268: self.changes = if File.exist? changelog 269: Array(changelog_patterns[:changes]).map do |pattern| 270: open(changelog) do |log| 271: log.read[pattern, 1] 272: end 273: end.compact.first or "" 274: else 275: "" 276: end 277: end 278: 279: # set some post-defaults 280: self.certificate_chain = Array(certificate_chain).map {|file| File.expand_path(file)} 281: self.private_key = File.expand_path(private_key) if private_key 282: self.description = summary if description.empty? 283: self.summary = description if summary.empty? 284: self.clean_pattern = apply_pattern(clean_pattern) 285: self.extension_pattern = apply_pattern(extension_pattern, files) 286: self.ignore_pattern = apply_pattern(ignore_pattern) 287: self.rdoc_pattern = apply_pattern(rdoc_pattern, files) - [manifest_name] 288: self.executable_pattern = apply_pattern(executable_pattern, files) 289: self.test_pattern = apply_pattern(test_pattern) 290: self.spec_pattern = apply_pattern(spec_pattern) 291: 292: define_tasks 293: end
# File lib/echoe.rb, line 163 163: def initialize(name, _version = nil) 164: # Defaults 165: 166: self.name = name 167: self.project = name.downcase 168: self.changelog = "CHANGELOG" 169: self.url = "" 170: self.author = "" 171: self.email = "" 172: self.clean_pattern = ["pkg", "doc", 'build/*', '**/coverage', '**/*.o', '**/*.so', '**/*.a', '**/*.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"] 173: self.test_pattern = File.exist?("test/test_all.rb") ? "test/test_all.rb" : ['test/**/test_*.rb', 'test/**/*_test.rb'] 174: self.spec_pattern = "spec/**/*_spec.rb" 175: self.ignore_pattern = /^(pkg|doc)|\.svn|CVS|\.bzr|\.DS|\.git/ 176: 177: self.changelog_patterns = { 178: :version => [ 179: /^\s*v([\d\.]+)(\.|\s|$)/, 180: /\s*\*\s*([\d\.]+)\s*\*\s*$/ 181: ], 182: :changes => [ 183: /^\s*v([\d\.]+\. .*)/, 184: /\*\s*[\d\.]+\s*\*\s*(.*)\*\s*[\d\.]+\s*\*$/m 185: ] 186: } 187: 188: self.description = "" 189: self.summary = "" 190: self.install_message = nil 191: self.executable_pattern = /^bin\// 192: self.has_rdoc = true 193: self.use_sudo = !Platform.windows? 194: self.gem_bin = "gem#{Platform.suffix}" 195: self.rcov_options = [] 196: self.rdoc_pattern = /^(lib|bin|tasks|ext)|^README|^CHANGELOG|^TODO|^LICENSE|^COPYING$/ 197: 198: self.gemspec_format = :ruby 199: 200: title = (name.downcase == name ? name.capitalize : name) 201: self.rdoc_options = ['--line-numbers', '--inline-source', '--title', title] 202: 203: readme = Dir['*'].detect { |filename| filename =~ /^readme/i } 204: self.rdoc_options += ['--main', readme] if readme 205: 206: self.runtime_dependencies = [] 207: self.development_dependencies = [] # These appear to not work at all 208: self.manifest_name = "Manifest" 209: self.extension_pattern = ["ext/**/extconf.rb", "ext/extconf.rb"] 210: self.private_key = ENV['GEM_PRIVATE_KEY'] 211: self.require_signed = false 212: self.certificate_chain = ENV['GEM_CERTIFICATE_CHAIN'].to_s.split(/\,\s*/).compact 213: 214: self.need_gem = true 215: self.need_tar_gz = true 216: self.need_tgz = false 217: self.need_zip = false 218: self.platform = $platform 219: 220: self.include_rakefile = true 221: self.include_gemspec = true 222: self.gemspec_name = "#{name}.gemspec" 223: self.retain_gemspec = false 224: self.rakefile_name = "Rakefile" 225: self.rubygems_version = ">= 1.2" 226: 227: yield self if block_given? 228: 229: # legacy compatibility 230: self.runtime_dependencies = dependencies if dependencies and runtime_dependencies.empty? 231: self.runtime_dependencies = extra_deps if extra_deps and runtime_dependencies.empty? 232: self.project = rubyforge_name if rubyforge_name 233: self.rdoc_pattern = rdoc_files if rdoc_files 234: self.extension_pattern = extensions if extensions 235: 236: # read manifest 237: begin 238: self.files = File.readlines(manifest_name).map { |x| x.strip } + 239: [(gemspec_name if include_gemspec)] + 240: [(rakefile_name if include_rakefile)] 241: self.files = files.compact.uniq 242: rescue Errno::ENOENT 243: unless ARGV.include? "manifest" 244: puts "Missing manifest. You can build one with 'rake manifest'." 245: exit 1 246: else 247: self.files = [] 248: end 249: end 250: 251: # snag version and changeset 252: self.version ||= _version 253: unless version 254: if File.exist? changelog 255: parsed = Array(changelog_patterns[:version]).map do |pattern| 256: open(changelog) do |log| 257: log.read[pattern, 1] 258: end 259: end.compact.first 260: raise "Could not parse version from #{changelog}" unless parsed 261: self.version = parsed.chomp(".").strip 262: else 263: raise "No #{changelog} found, and no version supplied in Rakefile." 264: end 265: end 266: 267: unless self.changes 268: self.changes = if File.exist? changelog 269: Array(changelog_patterns[:changes]).map do |pattern| 270: open(changelog) do |log| 271: log.read[pattern, 1] 272: end 273: end.compact.first or "" 274: else 275: "" 276: end 277: end 278: 279: # set some post-defaults 280: self.certificate_chain = Array(certificate_chain).map {|file| File.expand_path(file)} 281: self.private_key = File.expand_path(private_key) if private_key 282: self.description = summary if description.empty? 283: self.summary = description if summary.empty? 284: self.clean_pattern = apply_pattern(clean_pattern) 285: self.extension_pattern = apply_pattern(extension_pattern, files) 286: self.ignore_pattern = apply_pattern(ignore_pattern) 287: self.rdoc_pattern = apply_pattern(rdoc_pattern, files) - [manifest_name] 288: self.executable_pattern = apply_pattern(executable_pattern, files) 289: self.test_pattern = apply_pattern(test_pattern) 290: self.spec_pattern = apply_pattern(spec_pattern) 291: 292: define_tasks 293: 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 295 295: def apply_pattern(pattern, files = nil) 296: files ||= Dir['**/**'] 297: case pattern 298: when String, Array 299: files & (Array(pattern).map do |p| 300: Dir.glob(p) 301: end.flatten) 302: when Regexp 303: files.select do |file| 304: file =~ pattern 305: end 306: when FileList 307: pattern.each do |ignorefile| 308: ignorefiles = File.open(ignorefile).to_a.map(&:chomp) 309: files = files.select do |file| 310: ignorefiles.map { |i| File.fnmatch(i, file) }.include?(true) 311: end 312: end 313: files 314: else 315: [] 316: end 317: end
# File lib/echoe.rb, line 295 295: def apply_pattern(pattern, files = nil) 296: files ||= Dir['**/**'] 297: case pattern 298: when String, Array 299: files & (Array(pattern).map do |p| 300: Dir.glob(p) 301: end.flatten) 302: when Regexp 303: files.select do |file| 304: file =~ pattern 305: end 306: when FileList 307: pattern.each do |ignorefile| 308: ignorefiles = File.open(ignorefile).to_a.map(&:chomp) 309: files = files.select do |file| 310: ignorefiles.map { |i| File.fnmatch(i, file) }.include?(true) 311: end 312: end 313: files 314: else 315: [] 316: end 317: end
# File lib/echoe.rb, line 319 319: def define_tasks 320: 321: ### Packaging and Installing 322: 323: self.spec = Gem::Specification.new do |s| 324: s.name = name 325: s.version = version 326: # s.specification_version = 3 327: s.summary = summary 328: s.author = Array(author).join(", ") 329: s.email = email 330: s.homepage = url 331: s.rubyforge_project = project 332: s.post_install_message = install_message if install_message 333: s.description = description 334: s.required_ruby_version = ruby_version 335: s.required_rubygems_version = rubygems_version if rubygems_version 336: s.platform = platform 337: s.rdoc_options = rdoc_options 338: s.extra_rdoc_files = rdoc_pattern 339: 340: if private_key and File.exist? private_key 341: s.signing_key = private_key 342: s.cert_chain = certificate_chain 343: end 344: 345: runtime_dependencies.each do |dep| 346: dep = dep.split(" ") if dep.is_a? String 347: s.add_runtime_dependency(*dep) 348: end 349: 350: development_dependencies.each do |dep| 351: dep = dep.split(" ") if dep.is_a? String 352: s.add_development_dependency(*dep) 353: end 354: 355: s.files = files 356: 357: s.bindir = if executable_pattern.any? 358: executable_pattern[0].split("/")[0] 359: else 360: "bin" 361: end 362: 363: s.executables = executable_pattern.map do |file| 364: file[(s.bindir.length + 1)..-1] 365: end 366: 367: dirs = Dir['{lib,ext}'] 368: s.extensions = extension_pattern if extension_pattern.any? 369: s.require_paths = dirs unless dirs.empty? 370: s.has_rdoc = has_rdoc 371: 372: if File.exist? "test/test_all.rb" 373: s.test_file = "test/test_all.rb" 374: else 375: s.test_files = test_pattern 376: end 377: 378: if eval 379: s.instance_eval &eval 380: end 381: 382: end 383: 384: self.lib_files = spec.files.grep(/^lib/) 385: self.bin_files = spec.files.grep(/^bin/) 386: self.test_files = spec.files.grep(/^test/) 387: 388: Rake::GemPackageTask.new(spec) do |pkg| 389: pkg.need_tar = @need_tgz 390: pkg.need_tar_gz = @need_tar_gz 391: pkg.need_zip = @need_zip 392: end 393: 394: desc "Display Echoe's knowledge of your system" 395: task :details do 396: (self.instance_variables.sort - ['@spec']).each do |var| 397: puts "#{var}: #{instance_variable_get(var).inspect}" 398: end 399: end 400: 401: desc "Builds the .gemspec" 402: task :build_gemspec do 403: # Construct the gemspec file, if needed. 404: if include_gemspec 405: File.open(gemspec_name, 'w') do |f| 406: case gemspec_format 407: when :yaml 408: spec.to_yaml.split("\n").each do |line| 409: # Don't publish any information about the private key or certificate chain 410: f.puts line unless line =~ /signing_key|cert_chain|\.pem/ 411: end 412: when :ruby 413: f.puts spec.to_ruby 414: else 415: raise "Unknown gemspec format #{gemspec_format.inspect}. Supported formats: :ruby and :yaml" 416: end 417: end 418: end 419: puts "Gemspec generated" 420: end 421: 422: # Chain it to the gemspec task prerequisite 423: task gemspec_name.to_sym => [:build_gemspec] 424: 425: desc "Generates manifest & gemspec in one go" 426: task :build => [:manifest, :build_gemspec] 427: 428: task :package do 429: # Chain some cleanup tasks to the default :package task. 430: # Remove the gemfile if it wasn't actually requested. 431: unless @need_gem 432: puts " Gem file not requested. Removed." 433: system "rm pkg/*.gem" 434: end 435: # Remove the generated gemspec once the packaging is done, to discourage people from modifying it by hand. 436: if include_gemspec and File.exist? gemspec_name and not retain_gemspec 437: File.delete gemspec_name 438: end 439: 440: # Test signing status 441: if private_key and File.exist? private_key 442: puts "Signing gem." 443: else 444: raise "Key required, but not found. Maybe you forget to set ENV['GEM_PRIVATE_KEY']?" if require_signed 445: puts "Private key not found; gem will not be signed." 446: end 447: puts "Targeting \"#{platform}\" platform." 448: end 449: 450: desc 'Install the gem' 451: task :install => [:clean, :package, :uninstall] do 452: system "#{'sudo' if use_sudo} #{gem_bin} install pkg/*.gem -P MediumSecurity --no-update-sources" 453: end 454: 455: namespace :install do 456: desc 'Install the gem including development dependencies' 457: task :development => [:clean, :package, :uninstall] do 458: system "#{'sudo' if use_sudo} #{gem_bin} install pkg/*.gem -P MediumSecurity --no-update-sources --development" 459: end 460: end 461: 462: desc 'Uninstall the gem' 463: task :uninstall do 464: system "#{'sudo' if use_sudo} #{gem_bin} uninstall #{name} -a -I -x" 465: end 466: 467: desc 'Package and upload the release to Rubyforge' 468: task :release => [:clean, :package] do |t| 469: 470: say "\n" 471: if agree "Release #{name}-#{version} to Rubyforge? " 472: pkg = "pkg/#{name}-#{version}" 473: pkg_gem = pkg + ".gem" 474: pkg_tar = pkg + ".tgz" 475: pkg_tar_gz = pkg + ".tar.gz" 476: pkg_zip = pkg + ".zip" 477: 478: rf = RubyForge.new.configure 479: puts "Logging in" 480: rf.login 481: 482: c = rf.userconfig 483: c["release_notes"] = description if description 484: c["release_changes"] = changes if changes 485: c["preformatted"] = false 486: 487: files = [(@need_tgz ? pkg_tar : nil), 488: (@need_tar_gz ? pkg_tar_gz : nil), 489: (@need_zip ? pkg_zip : nil), 490: (@need_gem ? pkg_gem : nil)].compact 491: 492: puts "Releasing #{name} v. #{version}" 493: self.version = self.version.to_s.ljust(3) 494: 495: rf.add_release project, name, version, *files 496: end 497: 498: end 499: 500: ### Extension building 501: 502: task :lib do 503: directory "lib" 504: end 505: 506: if extension_pattern.any? 507: 508: desc "Compile the binary extension module" 509: task :compile => [:lib] do 510: extension_pattern.each do |extension| 511: ext_dir = File.dirname(extension) 512: lib_target = nil 513: Dir.chdir(ext_dir) do 514: ruby File.basename(extension) 515: system(RUBY_PLATFORM =~ /win32/ ? 'nmake' : 'make') 516: lib_target = open('Makefile').readlines.grep(/target_prefix = /).first.split('=').last.chomp("\n").strip 517: end 518: Dir["#{ext_dir}/*.#{Config::CONFIG['DLEXT']}"].each do |file| 519: dir = "lib/#{lib_target}/".gsub('//', '/') 520: mkdir_p dir 521: cp file, dir 522: end 523: end 524: end 525: 526: task :test => [:compile] if test_pattern.any? 527: task :spec => :compile if spec_pattern.any? 528: 529: end 530: 531: ### Cross-platform targets 532: 533: Gem::Specification::PLATFORM_CROSS_TARGETS.each do |target| 534: task target do 535: reset_target target 536: end 537: end 538: 539: ### Documentation 540: 541: Rake::RDocTask.new(:docs) do |rd| 542: # rd.main = Dir['*'].detect {|f| f =~ /^readme/i} 543: rd.options += Array(rdoc_options) 544: 545: rd.rdoc_dir = 'doc' 546: rd.rdoc_files.push(*rdoc_pattern) 547: 548: if rdoc_template 549: rd.template = rdoc_template 550: elsif ENV['RDOC_TEMPLATE'] 551: rd.template = ENV['RDOC_TEMPLATE'] 552: end 553: end 554: 555: task :doc => [:redocs] 556: 557: desc "Publish documentation to #{docs_host ? "'#{docs_host}'" : "rubyforge"}" 558: task :publish_docs => [:clean, :docs] do 559: 560: local_dir = 'doc' 561: remote_dir_name = project 562: remote_dir_name += "/#{name}" if project != name 563: 564: unless docs_host 565: config = YAML.load(File.read(File.expand_path("~/.rubyforge/user-config.yml"))) 566: pub = Rake::SshDirPublisher.new "#{config["username"]}@rubyforge.org", 567: "/var/www/gforge-projects/#{remote_dir_name}", 568: local_dir 569: if project != name then 570: def pub.upload 571: begin 572: super 573: rescue 574: # project directory probably doesn't exist, transfer as a whole 575: cmd = "scp -qr #{local_dir} #{host}:#{remote_dir}" 576: puts "Uploading: #{cmd}" 577: system(cmd) 578: end 579: end 580: end 581: pub.upload 582: else 583: # you may need ssh keys configured for this to work 584: host, dir = docs_host.split(":") 585: dir.chomp!("/") 586: 587: # XXX too dangerous? 588: cmd = "ssh #{host} 'rm -rf #{dir}/#{remote_dir_name}'" 589: puts "Deleting existing docs: #{cmd}" 590: system(cmd) 591: 592: cmd = "scp -qr #{local_dir} #{host}:#{dir}/#{remote_dir_name}" 593: puts "Uploading: #{cmd}" 594: system(cmd) 595: end 596: end 597: 598: desc 'Generate a release announcement, edit it, and post it to Rubyforge.' 599: task :announce do 600: 601: filename = "/tmp/#{name}_#{version}_announcement.txt" 602: 603: if !File.exist?(filename) or agree "Overwrite existing announcement file? " 604: File.open(filename, 'w') do |f| 605: f.write "Subject: #{name.capitalize} #{version}\n\n" 606: f.write "#{name.capitalize} has been updated to #{version}. #{name.capitalize} is #{summary.uncapitalize}\n\n" 607: unless changes.empty? 608: f.write "Changes in this version: " 609: if changes.include?("\n") 610: f.write(changes) 611: else 612: f.write(changes.sub(/^\s*[\w\d\.]+\s+/, '').uncapitalize) 613: end 614: f.write("\n\n") 615: end 616: f.write "More information is available at #{url} .\n\n" unless url.empty? 617: end 618: end 619: 620: begin 621: editor = ENV['EDITOR'] || 'nano' 622: system("#{editor} #{filename}") or raise "Editor '#{editor}' failed to start" 623: puts File.open(filename).read 624: end while !agree "Done editing? " 625: 626: if agree "Publish announcement to Rubyforge? " 627: File.open(filename).readlines.detect { |line| line =~ /Subject: (.*)/ } 628: subject = $1 or raise "Subject line seems to have disappeared" 629: 630: body = File.open(filename).readlines.reject { |line| line =~ /Subject: / }.join.gsub("\n\n\n", "\n\n") 631: 632: rf = RubyForge.new.configure 633: rf.login 634: rf.post_news(project, subject, body) 635: puts "Published." 636: File.delete filename 637: end 638: end 639: 640: ### Clean 641: 642: desc 'Clean up auto-generated files' 643: task :clean do 644: puts "Cleaning" 645: clean_pattern.each do |file| 646: if File.exist?(file) 647: puts "- #{file}" 648: rm_rf file 649: end 650: end 651: end 652: 653: ### Manifest 654: 655: desc "Build a Manifest list" 656: task :manifest => [:clean] do 657: puts "Building Manifest" 658: old_files = files 659: files = [] 660: Dir['**/**'].sort.each do |file| 661: next unless file 662: next if ignore_pattern.include?(file) 663: next if File.directory?(file) 664: next if !include_rakefile and file == rakefile_name 665: files << file 666: end 667: 668: files << rakefile_name if include_rakefile 669: files << manifest_name 670: files.uniq! 671: 672: File.open(manifest_name, 'w').puts(files) 673: 674: (files | old_files).sort.each do |file| 675: next if file == gemspec_name 676: sign = " " 677: if old_files.include?(file) and !files.include?(file) 678: sign = "-" 679: elsif files.include?(file) and !old_files.include?(file) 680: sign = "+" 681: end 682: puts "#{sign} #{file}" 683: end 684: end 685: 686: task :build_manifest => :manifest 687: 688: ### Testing 689: 690: if test_pattern.any? 691: 692: Rake::TestTask.new(:test_inner) do |t| 693: t.libs = ['lib', 'ext', 'bin', 'test'] 694: t.test_files = test_pattern 695: t.verbose = true 696: end 697: 698: desc "Run the test suite" 699: task :test do 700: if File.exist? 'test/setup.rb' 701: Echoe.silence do 702: puts "Setting up test environment" 703: system("ruby test/setup.rb") 704: end 705: end 706: begin 707: test = Rake::Task[:test_inner] 708: if test.respond_to? :already_invoked= 709: # Method provided by MultiRails 710: test.already_invoked = false 711: end 712: test.invoke 713: ensure 714: if File.exist? 'test/teardown.rb' 715: Echoe.silence do 716: puts "Tearing down test environment" 717: system("ruby test/teardown.rb") 718: end 719: end 720: end 721: end 722: 723: task :default => :test 724: end 725: 726: if spec_pattern.any? 727: desc "Run the spec suite" 728: Spec::Rake::SpecTask.new('spec') do |t| 729: t.spec_files = spec_pattern 730: end 731: 732: task :default => :spec 733: end 734: 735: if defined? Rcov 736: Rcov::RcovTask.new(:coverage) do |t| 737: t.test_files = test_pattern 738: t.rcov_opts << rcov_options if rcov_options 739: t.verbose = true 740: end 741: task :rcov => :coverage 742: end 743: 744: end
# File lib/echoe.rb, line 319 319: def define_tasks 320: 321: ### Packaging and Installing 322: 323: self.spec = Gem::Specification.new do |s| 324: s.name = name 325: s.version = version 326: # s.specification_version = 3 327: s.summary = summary 328: s.author = Array(author).join(", ") 329: s.email = email 330: s.homepage = url 331: s.rubyforge_project = project 332: s.post_install_message = install_message if install_message 333: s.description = description 334: s.required_ruby_version = ruby_version 335: s.required_rubygems_version = rubygems_version if rubygems_version 336: s.platform = platform 337: s.rdoc_options = rdoc_options 338: s.extra_rdoc_files = rdoc_pattern 339: 340: if private_key and File.exist? private_key 341: s.signing_key = private_key 342: s.cert_chain = certificate_chain 343: end 344: 345: runtime_dependencies.each do |dep| 346: dep = dep.split(" ") if dep.is_a? String 347: s.add_runtime_dependency(*dep) 348: end 349: 350: development_dependencies.each do |dep| 351: dep = dep.split(" ") if dep.is_a? String 352: s.add_development_dependency(*dep) 353: end 354: 355: s.files = files 356: 357: s.bindir = if executable_pattern.any? 358: executable_pattern[0].split("/")[0] 359: else 360: "bin" 361: end 362: 363: s.executables = executable_pattern.map do |file| 364: file[(s.bindir.length + 1)..-1] 365: end 366: 367: dirs = Dir['{lib,ext}'] 368: s.extensions = extension_pattern if extension_pattern.any? 369: s.require_paths = dirs unless dirs.empty? 370: s.has_rdoc = has_rdoc 371: 372: if File.exist? "test/test_all.rb" 373: s.test_file = "test/test_all.rb" 374: else 375: s.test_files = test_pattern 376: end 377: 378: if eval 379: s.instance_eval &eval 380: end 381: 382: end 383: 384: self.lib_files = spec.files.grep(/^lib/) 385: self.bin_files = spec.files.grep(/^bin/) 386: self.test_files = spec.files.grep(/^test/) 387: 388: Rake::GemPackageTask.new(spec) do |pkg| 389: pkg.need_tar = @need_tgz 390: pkg.need_tar_gz = @need_tar_gz 391: pkg.need_zip = @need_zip 392: end 393: 394: desc "Display Echoe's knowledge of your system" 395: task :details do 396: (self.instance_variables.sort - ['@spec']).each do |var| 397: puts "#{var}: #{instance_variable_get(var).inspect}" 398: end 399: end 400: 401: desc "Builds the .gemspec" 402: task :build_gemspec do 403: # Construct the gemspec file, if needed. 404: if include_gemspec 405: File.open(gemspec_name, 'w') do |f| 406: case gemspec_format 407: when :yaml 408: spec.to_yaml.split("\n").each do |line| 409: # Don't publish any information about the private key or certificate chain 410: f.puts line unless line =~ /signing_key|cert_chain|\.pem/ 411: end 412: when :ruby 413: f.puts spec.to_ruby 414: else 415: raise "Unknown gemspec format #{gemspec_format.inspect}. Supported formats: :ruby and :yaml" 416: end 417: end 418: end 419: puts "Gemspec generated" 420: end 421: 422: # Chain it to the gemspec task prerequisite 423: task gemspec_name.to_sym => [:build_gemspec] 424: 425: desc "Generates manifest & gemspec in one go" 426: task :build => [:manifest, :build_gemspec] 427: 428: task :package do 429: # Chain some cleanup tasks to the default :package task. 430: # Remove the gemfile if it wasn't actually requested. 431: unless @need_gem 432: puts " Gem file not requested. Removed." 433: system "rm pkg/*.gem" 434: end 435: # Remove the generated gemspec once the packaging is done, to discourage people from modifying it by hand. 436: if include_gemspec and File.exist? gemspec_name and not retain_gemspec 437: File.delete gemspec_name 438: end 439: 440: # Test signing status 441: if private_key and File.exist? private_key 442: puts "Signing gem." 443: else 444: raise "Key required, but not found. Maybe you forget to set ENV['GEM_PRIVATE_KEY']?" if require_signed 445: puts "Private key not found; gem will not be signed." 446: end 447: puts "Targeting \"#{platform}\" platform." 448: end 449: 450: desc 'Install the gem' 451: task :install => [:clean, :package, :uninstall] do 452: system "#{'sudo' if use_sudo} #{gem_bin} install pkg/*.gem -P MediumSecurity --no-update-sources" 453: end 454: 455: namespace :install do 456: desc 'Install the gem including development dependencies' 457: task :development => [:clean, :package, :uninstall] do 458: system "#{'sudo' if use_sudo} #{gem_bin} install pkg/*.gem -P MediumSecurity --no-update-sources --development" 459: end 460: end 461: 462: desc 'Uninstall the gem' 463: task :uninstall do 464: system "#{'sudo' if use_sudo} #{gem_bin} uninstall #{name} -a -I -x" 465: end 466: 467: desc 'Package and upload the release to Rubyforge' 468: task :release => [:clean, :package] do |t| 469: 470: say "\n" 471: if agree "Release #{name}-#{version} to Rubyforge? " 472: pkg = "pkg/#{name}-#{version}" 473: pkg_gem = pkg + ".gem" 474: pkg_tar = pkg + ".tgz" 475: pkg_tar_gz = pkg + ".tar.gz" 476: pkg_zip = pkg + ".zip" 477: 478: rf = RubyForge.new.configure 479: puts "Logging in" 480: rf.login 481: 482: c = rf.userconfig 483: c["release_notes"] = description if description 484: c["release_changes"] = changes if changes 485: c["preformatted"] = false 486: 487: files = [(@need_tgz ? pkg_tar : nil), 488: (@need_tar_gz ? pkg_tar_gz : nil), 489: (@need_zip ? pkg_zip : nil), 490: (@need_gem ? pkg_gem : nil)].compact 491: 492: puts "Releasing #{name} v. #{version}" 493: self.version = self.version.to_s.ljust(3) 494: 495: rf.add_release project, name, version, *files 496: end 497: 498: end 499: 500: ### Extension building 501: 502: task :lib do 503: directory "lib" 504: end 505: 506: if extension_pattern.any? 507: 508: desc "Compile the binary extension module" 509: task :compile => [:lib] do 510: extension_pattern.each do |extension| 511: ext_dir = File.dirname(extension) 512: lib_target = nil 513: Dir.chdir(ext_dir) do 514: ruby File.basename(extension) 515: system(RUBY_PLATFORM =~ /win32/ ? 'nmake' : 'make') 516: lib_target = open('Makefile').readlines.grep(/target_prefix = /).first.split('=').last.chomp("\n").strip 517: end 518: Dir["#{ext_dir}/*.#{Config::CONFIG['DLEXT']}"].each do |file| 519: dir = "lib/#{lib_target}/".gsub('//', '/') 520: mkdir_p dir 521: cp file, dir 522: end 523: end 524: end 525: 526: task :test => [:compile] if test_pattern.any? 527: task :spec => :compile if spec_pattern.any? 528: 529: end 530: 531: ### Cross-platform targets 532: 533: Gem::Specification::PLATFORM_CROSS_TARGETS.each do |target| 534: task target do 535: reset_target target 536: end 537: end 538: 539: ### Documentation 540: 541: Rake::RDocTask.new(:docs) do |rd| 542: # rd.main = Dir['*'].detect {|f| f =~ /^readme/i} 543: rd.options += Array(rdoc_options) 544: 545: rd.rdoc_dir = 'doc' 546: rd.rdoc_files.push(*rdoc_pattern) 547: 548: if rdoc_template 549: rd.template = rdoc_template 550: elsif ENV['RDOC_TEMPLATE'] 551: rd.template = ENV['RDOC_TEMPLATE'] 552: end 553: end 554: 555: task :doc => [:redocs] 556: 557: desc "Publish documentation to #{docs_host ? "'#{docs_host}'" : "rubyforge"}" 558: task :publish_docs => [:clean, :docs] do 559: 560: local_dir = 'doc' 561: remote_dir_name = project 562: remote_dir_name += "/#{name}" if project != name 563: 564: unless docs_host 565: config = YAML.load(File.read(File.expand_path("~/.rubyforge/user-config.yml"))) 566: pub = Rake::SshDirPublisher.new "#{config["username"]}@rubyforge.org", 567: "/var/www/gforge-projects/#{remote_dir_name}", 568: local_dir 569: if project != name then 570: def pub.upload 571: begin 572: super 573: rescue 574: # project directory probably doesn't exist, transfer as a whole 575: cmd = "scp -qr #{local_dir} #{host}:#{remote_dir}" 576: puts "Uploading: #{cmd}" 577: system(cmd) 578: end 579: end 580: end 581: pub.upload 582: else 583: # you may need ssh keys configured for this to work 584: host, dir = docs_host.split(":") 585: dir.chomp!("/") 586: 587: # XXX too dangerous? 588: cmd = "ssh #{host} 'rm -rf #{dir}/#{remote_dir_name}'" 589: puts "Deleting existing docs: #{cmd}" 590: system(cmd) 591: 592: cmd = "scp -qr #{local_dir} #{host}:#{dir}/#{remote_dir_name}" 593: puts "Uploading: #{cmd}" 594: system(cmd) 595: end 596: end 597: 598: desc 'Generate a release announcement, edit it, and post it to Rubyforge.' 599: task :announce do 600: 601: filename = "/tmp/#{name}_#{version}_announcement.txt" 602: 603: if !File.exist?(filename) or agree "Overwrite existing announcement file? " 604: File.open(filename, 'w') do |f| 605: f.write "Subject: #{name.capitalize} #{version}\n\n" 606: f.write "#{name.capitalize} has been updated to #{version}. #{name.capitalize} is #{summary.uncapitalize}\n\n" 607: unless changes.empty? 608: f.write "Changes in this version: " 609: if changes.include?("\n") 610: f.write(changes) 611: else 612: f.write(changes.sub(/^\s*[\w\d\.]+\s+/, '').uncapitalize) 613: end 614: f.write("\n\n") 615: end 616: f.write "More information is available at #{url} .\n\n" unless url.empty? 617: end 618: end 619: 620: begin 621: editor = ENV['EDITOR'] || 'nano' 622: system("#{editor} #{filename}") or raise "Editor '#{editor}' failed to start" 623: puts File.open(filename).read 624: end while !agree "Done editing? " 625: 626: if agree "Publish announcement to Rubyforge? " 627: File.open(filename).readlines.detect { |line| line =~ /Subject: (.*)/ } 628: subject = $1 or raise "Subject line seems to have disappeared" 629: 630: body = File.open(filename).readlines.reject { |line| line =~ /Subject: / }.join.gsub("\n\n\n", "\n\n") 631: 632: rf = RubyForge.new.configure 633: rf.login 634: rf.post_news(project, subject, body) 635: puts "Published." 636: File.delete filename 637: end 638: end 639: 640: ### Clean 641: 642: desc 'Clean up auto-generated files' 643: task :clean do 644: puts "Cleaning" 645: clean_pattern.each do |file| 646: if File.exist?(file) 647: puts "- #{file}" 648: rm_rf file 649: end 650: end 651: end 652: 653: ### Manifest 654: 655: desc "Build a Manifest list" 656: task :manifest => [:clean] do 657: puts "Building Manifest" 658: old_files = files 659: files = [] 660: Dir['**/**'].sort.each do |file| 661: next unless file 662: next if ignore_pattern.include?(file) 663: next if File.directory?(file) 664: next if !include_rakefile and file == rakefile_name 665: files << file 666: end 667: 668: files << rakefile_name if include_rakefile 669: files << manifest_name 670: files.uniq! 671: 672: File.open(manifest_name, 'w').puts(files) 673: 674: (files | old_files).sort.each do |file| 675: next if file == gemspec_name 676: sign = " " 677: if old_files.include?(file) and !files.include?(file) 678: sign = "-" 679: elsif files.include?(file) and !old_files.include?(file) 680: sign = "+" 681: end 682: puts "#{sign} #{file}" 683: end 684: end 685: 686: task :build_manifest => :manifest 687: 688: ### Testing 689: 690: if test_pattern.any? 691: 692: Rake::TestTask.new(:test_inner) do |t| 693: t.libs = ['lib', 'ext', 'bin', 'test'] 694: t.test_files = test_pattern 695: t.verbose = true 696: end 697: 698: desc "Run the test suite" 699: task :test do 700: if File.exist? 'test/setup.rb' 701: Echoe.silence do 702: puts "Setting up test environment" 703: system("ruby test/setup.rb") 704: end 705: end 706: begin 707: test = Rake::Task[:test_inner] 708: if test.respond_to? :already_invoked= 709: # Method provided by MultiRails 710: test.already_invoked = false 711: end 712: test.invoke 713: ensure 714: if File.exist? 'test/teardown.rb' 715: Echoe.silence do 716: puts "Tearing down test environment" 717: system("ruby test/teardown.rb") 718: end 719: end 720: end 721: end 722: 723: task :default => :test 724: end 725: 726: if spec_pattern.any? 727: desc "Run the spec suite" 728: Spec::Rake::SpecTask.new('spec') do |t| 729: t.spec_files = spec_pattern 730: end 731: 732: task :default => :spec 733: end 734: 735: if defined? Rcov 736: Rcov::RcovTask.new(:coverage) do |t| 737: t.test_files = test_pattern 738: t.rcov_opts << rcov_options if rcov_options 739: t.verbose = true 740: end 741: task :rcov => :coverage 742: end 743: 744: end