| 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