class GemHadar
def self.start_simplecov
def self.start_simplecov defined? SimpleCov or return filter = "#{File.basename(File.dirname(caller.first))}/" SimpleCov.start do add_filter filter end end
def ask?(prompt, pattern)
def ask?(prompt, pattern) STDOUT.print prompt answer = STDIN.gets.chomp if answer =~ pattern $~ end end
def assert_valid_link(name, orig_url)
def assert_valid_link(name, orig_url) developing and return orig_url url = orig_url begin response = Net::HTTP.get_response(URI.parse(url)) url = response['location'] end while response.is_a?(Net::HTTPRedirection) response.is_a?(Net::HTTPOK) or fail "#{orig_url.inspect} for #{name} has to be a valid link" orig_url end
def build_task
def build_task desc 'Build task (builds all packages for a release)' task :build => build_task_dependencies end
def clean(*args)
def clean(*args) if args.empty? CLEAN else CLEAN.include(*args) end end
def clobber(*args)
def clobber(*args) if args.empty? CLOBBER else CLOBBER.include(*args) end end
def compile_task
def compile_task for file in extensions dir = File.dirname(file) clean File.join(dir, 'Makefile'), File.join(dir, '*.{bundle,o,so}') end desc "Compile extensions: #{extensions * ', '}" task :compile do for file in extensions dir, file = File.split(file) cd dir do ruby file sh make end end end end
def create_all_tasks
def create_all_tasks default_task build_task rvm_task version_task version_show_task version_diff_task gem_hadar_update_task gemspec_task gems_install_task doc_task if test_dir test_task rcov_task end spec_task package_task yard_task install_library_task version_bump_task version_tag_task push_task write_ignore_file write_gemfile if extensions.full? compile_task task :prepare_install => :compile else task :prepare_install end self end
def default_task
def default_task desc 'Default task' task :default => default_task_dependencies end
def dependency(*args)
def dependency(*args) @dependencies << args end
def development_dependency(*args)
def development_dependency(*args) @development_dependencies << args end
def doc_task
def doc_task clean 'doc' desc "Creating documentation" task :doc do sh 'yard doc' cmd = 'yardoc' if readme cmd << " --readme '#{readme}'" end cmd << ' - ' << doc_files * ' ' sh cmd end end
def gem_files
def gem_files (files.to_a - package_ignore_files.to_a) end
def gem_hadar_update_task
def gem_hadar_update_task namespace :gem_hadar do desc 'Update gem_hadar a different version' task :update do answer = ask?("Which gem_hadar version? ", /^((?:\d+.){2}(?:\d+))$/) unless answer warn "Invalid version specification!" exit 1 end gem_hadar_version = answer[0] filename = "#{name}.gemspec" old_data = File.read(filename) new_data = old_data.gsub( /(add_(?:development_)?dependency\(%q<gem_hadar>, \["~> )([\d.]+)("\])/ ) { "#$1#{gem_hadar_version}#$3" } if old_data == new_data warn "#{filename.inspect} already depends on gem_hadar "\ "#{gem_hadar_version} => Do nothing." else warn "Upgrading #{filename.inspect} to #{gem_hadar_version}." secure_write(filename) do |spec| spec.write new_data end end end end end
def gem_push_task
def gem_push_task namespace :gem do path = "pkg/#{name_version}.gem" desc "Push gem file #{File.basename(path)} to rubygems" if developing task :push => :build do puts "Skipping push to rubygems while developing mode is enabled." end else task :push => :build do if File.exist?(path) if ask?("Do you really want to push #{path.inspect} to rubygems? "\ "(yes/NO) ", /\Ayes\z/i) then key = ENV['GEM_HOST_API_KEY'].full? { |k| "--key #{k} " } sh "gem push #{key}#{path}" else exit 1 end else warn "Cannot push gem to rubygems: #{path.inspect} doesn't exist." exit 1 end end end end end
def gems_install_task(&block)
def gems_install_task(&block) block ||= proc { sh 'bundle install' } desc 'Install all gems from the Gemfile' namespace :gems do task :install => :gemspec , &block end end
def gemspec
def gemspec Gem::Specification.new do |s| s.name = name s.version = ::Gem::Version.new(version) s.author = author s.email = email s.homepage = assert_valid_link(:homepage, homepage) s.summary = summary s.description = description gem_files.full? { |f| s.files = Array(f) } test_files.full? { |t| s.test_files = Array(t) } extensions.full? { |e| s.extensions = Array(e) } bindir.full? { |b| s.bindir = b } executables.full? { |e| s.executables = Array(e) } licenses.full? { |l| s.licenses = Array(licenses) } post_install_message.full? { |m| s.post_install_message = m } required_ruby_version.full? { |v| s.required_ruby_version = v } s.add_development_dependency('gem_hadar', "~> #{VERSION[/\A\d+\.\d+/, 0]}") for d in @development_dependencies s.add_development_dependency(*d) end for d in @dependencies if s.respond_to?(:add_runtime_dependency) s.add_runtime_dependency(*d) else s.add_dependency(*d) end end require_paths.full? { |r| s.require_paths = Array(r) } if title s.rdoc_options << '--title' << title else s.rdoc_options << '--title' << "#{name.camelize} - #{summary}" end if readme s.rdoc_options << '--main' << readme s.extra_rdoc_files << readme end doc_files.full? { |df| s.extra_rdoc_files.concat Array(df) } end end
def gemspec_task
def gemspec_task desc 'Create a gemspec file' task :gemspec => :version do filename = "#{name}.gemspec" warn "Writing to #{filename.inspect} for #{version}" secure_write(filename, gemspec.to_ruby) end end
def git_remote
def git_remote ENV.fetch('GIT_REMOTE', 'origin').split(/\s+/).first end
def git_remotes
def git_remotes remotes = ENV['GIT_REMOTE'].full?(:split, /\s+/) remotes or remotes = `git remote`.lines.map(&:chomp) remotes end
def git_remotes_task
def git_remotes_task task :git_remotes do puts git_remotes.map { |r| url = `git remote get-url #{r.inspect}`.chomp "#{r} #{url}" } end end
def has_to_be_set(name)
def has_to_be_set(name) fail "#{self.class}: #{name} has to be set for gem" end
def ignore(*args)
def ignore(*args) if args.empty? ignore_files else args.each { |a| ignore_files << a } end end
def initialize(&block)
def initialize(&block) @dependencies = [] @development_dependencies = [] block and instance_eval(&block) end
def install_library(&block)
def install_library(&block) @install_library_block = lambda do desc 'Install executable/library into site_ruby directories' task :install => :prepare_install, &block end end
def install_library_task
def install_library_task @install_library_block.full?(:call) end
def master_prepare_task
def master_prepare_task namespace :master do desc "Prepare a remote git repository for this project" task :prepare do puts "Create a new remote git repository for #{name.inspect}" remote_name = ask?('Name (default: origin) ? ', /^.+$/). full?(:[], 0) || 'origin' dir = ask?("Directory (default: /git/#{name}.git)? ", /^.+$/). full?(:[], 0) || "/git/#{name}.git" ssh_account = ask?('SSH account (format: login@host)? ', /^[^@]+@[^@]+/). full?(:[], 0) || exit(1) sh "ssh #{ssh_account} 'git init --bare #{dir}'" sh "git remote add -m master #{remote_name} #{ssh_account}:#{dir}" end end end
def master_push_task
def master_push_task namespace :master do git_remotes.each do |gr| namespace gr.to_sym do desc "Push master to git remote #{gr}" task :push do sh "git push #{gr} master" end end end desc "Push master #{version} to all git remotes: #{git_remotes * ' '}" task :push => git_remotes.map { |gr| :"master:#{gr}:push" } end end
def package_ignore(*args)
def package_ignore(*args) if args.empty? package_ignore_files else args.each { |a| package_ignore_files << a } end end
def package_task
def package_task clean 'pkg' Gem::PackageTask.new(gemspec) do |pkg| pkg.need_tar = true pkg.package_files += gem_files end end
def push_task
def push_task master_prepare_task version_push_task master_push_task gem_push_task git_remotes_task task :modified do changed_files = `git status --porcelain`.gsub(/^\s*\S\s+/, '').lines unless changed_files.empty? warn "There are still modified files:\n#{changed_files * ''}" exit 1 end end desc "Push master and version #{version} all git remotes: #{git_remotes * ' '}" task :push => %i[ modified build master:push version:push gem:push ] end
def rcov_task
def rcov_task if defined?(::Rcov) rt = ::Rcov::RcovTask.new(:run_rcov) do |t| t.libs << test_dir t.libs.concat require_paths.to_a t.libs.uniq! t.test_files = test_files t.verbose = true t.rcov_opts = %W[-x '\\b#{test_dir}\/' -x '\\bgems\/'] end desc 'Run the rcov code coverage tests' task :rcov => [ (:compile if extensions.full?), rt.name ].compact clobber 'coverage' else desc 'Run the rcov code coverage tests' task :rcov do warn "rcov doesn't work for some reason, have you tried 'gem install rcov'?" end end end
def require_path(path = nil)
def require_path(path = nil) if path self.require_paths = Set[path] else require_paths.first end end
def rvm(&block)
def rvm(&block) if block @rvm = RvmConfig.new(&block) elsif !@rvm @rvm = RvmConfig.new { } end @rvm end
def rvm_task
def rvm_task desc 'Create .rvmrc file' task :rvm do secure_write('.rvmrc') do |output| output.write <<EOT m use #{rvm.use} m gemset create #{rvm.gemset} m gemset use #{rvm.gemset} T end end end
def spec_task
def spec_task defined? ::RSpec::Core::RakeTask or return st = RSpec::Core::RakeTask.new(:run_specs) do |t| t.ruby_opts ||= '' t.ruby_opts << ' -I' << ([ spec_dir ] + require_paths.to_a).uniq * ':' t.pattern = spec_pattern t.verbose = true end task :spec => [ (:compile if extensions.full?), st.name ].compact end
def test_task
def test_task tt = Rake::TestTask.new(:run_tests) do |t| t.libs << test_dir t.libs.concat require_paths.to_a t.test_files = test_files t.verbose = true end desc 'Run the tests' task :test => [ (:compile if extensions.full?), tt.name ].compact end
def version_bump_task
def version_bump_task namespace :version do namespace :bump do desc 'Bump major version' task :major do version = File.read('VERSION').chomp.version version.bump(:major) secure_write('VERSION') { |v| v.puts version } end desc 'Bump minor version' task :minor do version = File.read('VERSION').chomp.version version.bump(:minor) secure_write('VERSION') { |v| v.puts version } end desc 'Bump build version' task :build do version = File.read('VERSION').chomp.version version.bump(:build) secure_write('VERSION') { |v| v.puts version } end end end end
def version_diff_task
def version_diff_task namespace :version do desc "List all versions in order" task :list do system 'git fetch --tags' $?.success? or exit $?.exitstatus puts versions end desc "Displaying the diff from env var VERSION to the next version or HEAD" task :diff do arg_version = ENV.fetch('VERSION', version).dup.prepend(?v) my_versions = versions.map { _1.prepend(?v) } + %w[ HEAD ] start_version, end_version = my_versions[my_versions.index(arg_version), 2] puts color(172) {"Showing diff from version %s to %s:" % [ start_version, end_version ]} puts `git diff --color=always #{start_version}..#{end_version}` end end end
def version_push_task
def version_push_task namespace :version do git_remotes.each do |gr| namespace gr.to_sym do desc "Push version #{version} to git remote #{gr}" task :push do sh "git push #{gr} v#{version}" end end end desc "Push version #{version} to all git remotes: #{git_remotes * ' '}" task :push => git_remotes.map { |gr| :"version:#{gr}:push" } end end
def version_show_task
def version_show_task namespace :version do desc "Displaying the current version" task :show do require path_name dir = File.join('lib', path_name) version_file = File.join(dir, 'version.rb') m = Module.new m.instance_eval File.read(version_file) version_rb = m.const_get( [ path_module, 'VERSION' ] * '::' ) equal = version == version_rb ? '==' : '!=' puts "version.rb=#{version_rb} #{equal} VERSION=#{version}" end end end
def version_tag_task
def version_tag_task namespace :version do desc "Tag this commit as version #{version}" task :tag do force = ENV['FORCE'].to_i == 1 begin sh "git tag -a -m 'Version #{version}' #{'-f' if force} v#{version}" rescue RuntimeError if `git diff v#{version}..HEAD`.empty? puts "Version #{version} is already tagged, but it's no different" else if ask?("Different version tag #{version} already exists. Overwrite with "\ "force? (yes/NO) ", /\Ayes\z/i) force = true retry else exit 1 end end end end end end
def version_task
def version_task desc m = "Writing version information for #{name}-#{version}" task :version do puts m mkdir_p dir = File.join('lib', path_name) secure_write(File.join(dir, 'version.rb')) do |v| v.puts <<EOT module_type} #{path_module} # #{path_module} version VERSION = '#{version}' VERSION_ARRAY = VERSION.split('.').map(&:to_i) # :nodoc: VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc: VERSION_MINOR = VERSION_ARRAY[1] # :nodoc: VERSION_BUILD = VERSION_ARRAY[2] # :nodoc: d T version_epilogue.full? { |ve| v.puts ve } end end end
def versions
def versions `git tag`.lines.grep(/^v?\d+\.\d+\.\d+$/).map(&:chomp).map { _1.sub(/\Av/, '') }.sort_by(&:version) end
def write_gemfile
def write_gemfile default_gemfile =<<EOT vim: set filetype=ruby et sw=2 ts=2: urce 'https://rubygems.org' mspec T current_gemfile = File.exist?('Gemfile') && File.read('Gemfile') case current_gemfile when false secure_write('Gemfile') do |output| output.write default_gemfile end when default_gemfile ;; else warn "INFO: Current Gemfile differs from default Gemfile." end end
def write_ignore_file
def write_ignore_file secure_write('.gitignore') do |output| output.puts(ignore.sort) end end
def yard_task
def yard_task defined? YARD or return namespace :yard do my_yard_dir = Pathname.new(yard_dir) desc 'Create yard documentation (including private)' task :private do sh "yardoc -o #{my_yard_dir}" end desc 'View the yard documentation' task :view do index_file = my_yard_dir + 'index.html' File.exist?(index_file) sh "open #{index_file}" end desc 'Clean the yard documentation' task :clean do rm_rf my_yard_dir end desc 'List all undocumented classes/modules/methods' task :'list-undoc' do sh "yard stats --list-undoc" end end desc 'Create the yard documentation and view it' task :yard => %i[ yard:private yard:view ] end