task/gemgem.rb in dm-is-reflective-1.3.1 vs task/gemgem.rb in dm-is-reflective-1.3.2

- old
+ new

@@ -1,109 +1,152 @@ -require 'pathname' - module Gemgem class << self - attr_accessor :dir, :spec + attr_accessor :dir, :spec, :submodules, :spec_create end module_function + def gem_tag ; "#{spec.name}-#{spec.version}" ; end + def gem_path ; "#{pkg_dir}/#{gem_tag}.gem" ; end + def spec_path ; "#{dir}/#{spec.name}.gemspec" ; end + def pkg_dir ; "#{dir}/pkg" ; end + def escaped_dir; @escaped_dir ||= Regexp.escape(dir); end + + def init dir, options={}, &block + self.dir = dir + ENV['RUBYLIB'] = "#{dir}/lib:#{ENV['RUBYLIB']}" + ENV['PATH'] = "#{dir}/bin:#{ENV['PATH']}" + self.submodules = options[:submodules] || [] + self.spec_create = block + + $LOAD_PATH.unshift("#{dir}/lib", *submodules_libs) + end + def create - yield(spec = Gem::Specification.new{ |s| + spec = Gem::Specification.new do |s| s.authors = ['Lin Jen-Shin (godfat)'] s.email = ['godfat (XD) godfat.org'] s.description = description.join s.summary = description.first - s.license = readme['LICENSE'].sub(/.+\n\n/, '').lines.first.strip + s.license = license - s.rubygems_version = Gem::VERSION - s.date = Time.now.strftime('%Y-%m-%d') - s.files = gem_files - s.test_files = gem_files.grep(%r{^test/(.+?/)*test_.+?\.rb$}) - s.executables = Dir['bin/*'].map{ |f| File.basename(f) } - s.require_paths = %w[lib] - }) + s.date = Time.now.strftime('%Y-%m-%d') + s.files = gem_files + s.test_files = test_files + s.executables = bin_files + end + spec_create.call(spec) spec.homepage ||= "https://github.com/godfat/#{spec.name}" - spec + self.spec = spec end - def readme - path = %w[README.md README].find{ |name| - File.exist?("#{Gemgem.dir}/#{name}") - } - @readme ||= - if path - ps = "##{File.read(path)}". - scan(/((#+)[^\n]+\n\n.+?(?=(\n\n\2[^#\n]+\n)|\Z))/m).map(&:first) - ps.inject('HEADER' => ps.first){ |r, s, i| - r[s[/\w+/]] = s - r - } - else - {} - end + def gem_install + require 'rubygems/commands/install_command' + # read ~/.gemrc + Gem.use_paths(Gem.configuration[:gemhome], Gem.configuration[:gempath]) + Gem::Command.extra_args = Gem.configuration[:gem] + + # setup install options + cmd = Gem::Commands::InstallCommand.new + cmd.handle_options([]) + + # install + install = Gem::Installer.new(gem_path, cmd.options) + install.install + puts "\e[35mGem installed: \e[33m#{strip_path(install.gem_dir)}\e[0m" end - def description - @description ||= (readme['DESCRIPTION']||'').sub(/.+\n\n/, '').lines + def gem_spec + create + write end - def changes - path = %w[CHANGES.md CHANGES].find{ |name| - File.exist?("#{Gemgem.dir}/#{name}") - } - @changes ||= - if path - date = '\d+{4}\-\d+{2}\-\d{2}' - File.read(path).match( - /([^\n]+#{date}\n\n(.+?))(?=\n\n[^\n]+#{date}\n|\Z)/m)[1] - else - '' - end + def gem_build + require 'fileutils' + require 'rubygems/package' + gem = nil + Dir.chdir(dir) do + gem = Gem::Package.build(Gem::Specification.load(spec_path)) + FileUtils.mkdir_p(pkg_dir) + FileUtils.mv(gem, pkg_dir) # gem is relative path, but might be ok + end + puts "\e[35mGem built: \e[33m#{strip_path("#{pkg_dir}/#{gem}")}\e[0m" end - def ann_md - "#{readme['HEADER'].sub(/([\w\-]+)/, "[\\1](#{spec.homepage})")}\n\n" \ - "##{readme['DESCRIPTION'][/[^\n]+\n\n[^\n]+/]}\n\n" \ - "### CHANGES:\n\n" \ - "###{changes}\n\n" \ - "##{readme['INSTALLATION']}\n\n" + - if readme['SYNOPSIS'] then "##{readme['SYNOPSIS'][/[^\n]+\n\n[^\n]+/]}" - else '' end + def gem_release + sh_git('tag', gem_tag) + sh_git('push') + sh_git('push', '--tags') + sh_gem('push', gem_path) end - def ann_html - gem 'nokogiri' - gem 'kramdown' + def gem_check + unless git('status', '--porcelain').empty? + puts("\e[35mWorking copy is not clean.\e[0m") + exit(3) + end - IO.popen('kramdown', 'r+') do |md| - md.puts Gemgem.ann_md - md.close_write - require 'nokogiri' - html = Nokogiri::XML.parse("<gemgem>#{md.read}</gemgem>") - html.css('*').each{ |n| n.delete('id') } - html.root.children.to_html + ver = spec.version.to_s + + if ENV['VERSION'].nil? + puts("\e[35mExpected " \ + "\e[33mVERSION\e[35m=\e[33m#{ver}\e[0m") + exit(1) + + elsif ENV['VERSION'] != ver + puts("\e[35mExpected \e[33mVERSION\e[35m=\e[33m#{ver} " \ + "\e[35mbut got\n " \ + "\e[33mVERSION\e[35m=\e[33m#{ENV['VERSION']}\e[0m") + exit(2) end end - def ann_email - "#{readme['HEADER'].sub(/([\w\-]+)/, "\\1 <#{spec.homepage}>")}\n\n" \ - "#{readme['DESCRIPTION']}\n\n" \ - "#{readme['INSTALLATION']}\n\n" + - if readme['SYNOPSIS'] then "##{readme['SYNOPSIS']}\n\n" else '' end + - "## CHANGES:\n\n" \ - "##{changes}\n\n" + def test + return if test_files.empty? + + if ENV['COV'] || ENV['CI'] + require 'simplecov' + if ENV['CI'] + begin + require 'coveralls' + SimpleCov.formatter = Coveralls::SimpleCov::Formatter + rescue LoadError => e + puts "Cannot load coveralls, skip: #{e}" + end + end + SimpleCov.start do + add_filter('test/') + add_filter('test.rb') + submodules_libs.each(&method(:add_filter)) + end + end + + test_files.each{ |file| require "#{dir}/#{file[0..-4]}" } end - def gem_tag - "#{spec.name}-#{spec.version}" + def clean + return if ignored_files.empty? + + require 'fileutils' + trash = File.expand_path("~/.Trash/#{spec.name}") + puts "Move the following files into: \e[35m#{strip_path(trash)}\e[33m" + + ignored_files.each do |file| + from = "#{dir}/#{file}" + to = "#{trash}/#{File.dirname(file)}" + puts strip_path(from) + + FileUtils.mkdir_p(to) + FileUtils.mv(from, to) + end + + print "\e[0m" end def write - File.open("#{dir}/#{spec.name}.gemspec", 'w'){ |f| - f << split_lines(spec.to_ruby) } + File.open(spec_path, 'w'){ |f| f << split_lines(spec.to_ruby) } end def split_lines ruby ruby.gsub(/(.+?)\s*=\s*\[(.+?)\]/){ |s| if $2.index(',') @@ -112,157 +155,184 @@ s end } end + def strip_path path + strip_home_path(strip_cwd_path(path)) + end + + def strip_home_path path + path.sub(/\A#{Regexp.escape(ENV['HOME'])}\//, '~/') + end + + def strip_cwd_path path + path.sub(/\A#{Regexp.escape(Dir.pwd)}\//, '') + end + + def submodules_libs + submodules.map{ |path| "#{dir}/#{path}/lib" } + end + + def git *args + `git --git-dir=#{dir}/.git #{args.join(' ')}` + end + + def sh_git *args + Rake.sh('git', "--git-dir=#{dir}/.git", *args) + end + + def sh_gem *args + Rake.sh(Gem.ruby, '-S', 'gem', *args) + end + + def glob path=dir + Dir.glob("#{path}/**/*", File::FNM_DOTMATCH) + end + + def readme + @readme ||= + if (path = "#{Gemgem.dir}/README.md") && File.exist?(path) + ps = "##{File.read(path)}". + scan(/((#+)[^\n]+\n\n.+?(?=(\n\n\2[^#\n]+\n)|\Z))/m).map(&:first) + ps.inject('HEADER' => ps.first){ |r, s, i| + r[s[/\w+/]] = s + r + } + else + {} + end + end + + def description + # JRuby String#lines is returning an enumerator + @description ||= (readme['DESCRIPTION']||'').sub(/.+\n\n/, '').lines.to_a + end + + def license + readme['LICENSE'].sub(/.+\n\n/, '').lines.first. + split(/[()]/).map(&:strip).reject(&:empty?).last + end + def all_files - @all_files ||= find_files(Pathname.new(dir)).map{ |file| - if file.to_s =~ %r{\.git/|\.git$} - nil + @all_files ||= fold_files(glob).sort + end + + def fold_files files + files.inject([]){ |r, path| + if File.file?(path) && path !~ %r{/\.git(/|$)} && + (rpath = path[%r{^#{escaped_dir}/(.*$)}, 1]) + r << rpath + elsif File.symlink?(path) # walk into symlinks... + r.concat(fold_files(glob(File.expand_path(path, + File.readlink(path))))) else - file.to_s + r end - }.compact.sort + } end def gem_files - @gem_files ||= all_files - ignored_files + @gem_files ||= all_files.reject{ |f| + f =~ submodules_pattern || + (f =~ ignored_pattern && !git_files.include?(f)) + } end - def ignored_files - @ignored_file ||= all_files.select{ |path| ignore_patterns.find{ |ignore| - path =~ ignore && !git_files.include?(path)}} + def test_files + @test_files ||= gem_files.grep(%r{^test/(.+?/)*test_.+?\.rb$}) end + def bin_files + @bin_files ||= gem_files.grep(%r{^bin/}).map{ |f| File.basename(f) } + end + def git_files @git_files ||= if File.exist?("#{dir}/.git") - `git ls-files`.split("\n") + git('ls-files').split("\n") else [] end end - # protected - def find_files path - path.children.select(&:file?).map{|file| file.to_s[(dir.size+1)..-1]} + - path.children.select(&:directory?).map{|dir| find_files(dir)}.flatten + def ignored_files + @ignored_files ||= all_files.grep(ignored_pattern) end - def ignore_patterns - @ignore_files ||= expand_patterns( - gitignore.split("\n").reject{ |pattern| - pattern.strip == '' - }).map{ |pattern| %r{^([^/]+/)*?#{Regexp.escape(pattern)}(/[^/]+)*?$} } + def ignored_pattern + @ignored_pattern ||= if gitignore.empty? + /^$/ + else + Regexp.new(expand_patterns(gitignore).join('|')) + end end + def submodules_pattern + @submodules_pattern ||= if submodules.empty? + /^$/ + else + Regexp.new(submodules.map{ |path| + "^#{Regexp.escape(path)}/" }.join('|')) + end + end + def expand_patterns pathes - pathes.map{ |path| - if path !~ /\*/ - path - else - expand_patterns( - Dir[path] + - Pathname.new(File.dirname(path)).children.select(&:directory?). - map{ |prefix| "#{prefix}/#{File.basename(path)}" }) - end - }.flatten + # http://git-scm.com/docs/gitignore + pathes.flat_map{ |path| + # we didn't implement negative pattern for now + Regexp.escape(path).sub(%r{^/}, '^').gsub(/\\\*/, '[^/]*') + } end def gitignore - if File.exist?(path = "#{dir}/.gitignore") - File.read(path) - else - '' - end + @gitignore ||= if File.exist?(path = "#{dir}/.gitignore") + File.read(path).lines. + reject{ |l| l == /^\s*(#|\s+$)/ }.map(&:strip) + else + [] + end end end namespace :gem do desc 'Install gem' task :install => [:build] do - sh("#{Gem.ruby} -S gem install pkg/#{Gemgem.gem_tag}.gem") + Gemgem.gem_install end desc 'Build gem' task :build => [:spec] do - sh("#{Gem.ruby} -S gem build #{Gemgem.spec.name}.gemspec") - sh("mkdir -p pkg") - sh("mv #{Gemgem.gem_tag}.gem pkg/") + Gemgem.gem_build end +desc 'Generate gemspec' +task :spec do + Gemgem.gem_spec +end + desc 'Release gem' task :release => [:spec, :check, :build] do - sh("git tag #{Gemgem.gem_tag}") - sh("git push") - sh("git push --tags") - sh("#{Gem.ruby} -S gem push pkg/#{Gemgem.gem_tag}.gem") + Gemgem.gem_release end task :check do - ver = Gemgem.spec.version.to_s - - if ENV['VERSION'].nil? - puts("\e[35mExpected " \ - "\e[33mVERSION\e[35m=\e[33m#{ver}\e[0m") - exit(1) - - elsif ENV['VERSION'] != ver - puts("\e[35mExpected \e[33mVERSION\e[35m=\e[33m#{ver} " \ - "\e[35mbut got\n " \ - "\e[33mVERSION\e[35m=\e[33m#{ENV['VERSION']}\e[0m") - exit(2) - end + Gemgem.gem_check end end # of gem namespace -desc 'Run tests in memory' +desc 'Run tests' task :test do - require 'bacon' - Bacon.extend(Bacon::TestUnitOutput) - Bacon.summary_on_exit - $LOAD_PATH.unshift('lib') - Dir['./test/**/test_*.rb'].each{ |file| require file[0..-4] } + Gemgem.test end -desc 'Run tests with shell' -task 'test:shell', :RUBY_OPTS do |t, args| - files = Dir['test/**/test_*.rb'].join(' ') - - cmd = [Gem.ruby, args[:RUBY_OPTS], - '-I', 'lib', '-S', 'bacon', '--quiet', files] - - sh(cmd.compact.join(' ')) -end - -desc 'Generate ann markdown' -task 'ann:md' => ['gem:spec'] do - puts Gemgem.ann_md -end - -desc 'Generate ann html' -task 'ann:html' => ['gem:spec'] do - puts Gemgem.ann_html -end - -desc 'Generate ann email' -task 'ann:email' => ['gem:spec'] do - puts Gemgem.ann_email -end - -desc 'Generate rdoc' -task :doc => ['gem:spec'] do - sh("yardoc -o rdoc --main README.md" \ - " --files #{Gemgem.spec.extra_rdoc_files.join(',')}") -end - -desc 'Remove ignored files' +desc 'Trash ignored files' task :clean => ['gem:spec'] do - trash = "~/.Trash/#{Gemgem.spec.name}/" - sh "mkdir -p #{trash}" unless File.exist?(File.expand_path(trash)) - Gemgem.ignored_files.each{ |file| sh "mv #{file} #{trash}" } + Gemgem.clean end task :default do - puts `#{Gem.ruby} -S #{$PROGRAM_NAME} -T` + # Is there a reliable way to do this in the current process? + # It failed miserably before between Rake versions... + exec "#{Gem.ruby} -S #{$PROGRAM_NAME} -f #{Rake.application.rakefile} -T" end