Commits

Alessio Caiazza committed a7aa126

Added mercurial support

Comments (0)

Files changed (5)

lib/bundler/cli.rb

 
     desc "gem GEM", "Creates a skeleton for creating a rubygem"
     method_option :bin, :type => :boolean, :default => false, :aliases => '-b', :banner => "Generate a binary for your library."
+    method_option :hg, :type => :boolean, :default => false, :aliases => '-H', :banner => "Use mercurial instead of git"
     def gem(name)
       target = File.join(Dir.pwd, name)
       constant_name = name.split('_').map{|p| p.capitalize}.join
       constant_name = constant_name.split('-').map{|q| q.capitalize}.join('::') if constant_name =~ /-/
       constant_array = constant_name.split('::')
       FileUtils.mkdir_p(File.join(target, 'lib', name))
-      opts = {:name => name, :constant_name => constant_name, :constant_array => constant_array}
+      opts = {:name => name, :constant_name => constant_name, :constant_array => constant_array, :hg => options[:hg]}
       template(File.join("newgem/Gemfile.tt"),               File.join(target, "Gemfile"),                opts)
       template(File.join("newgem/Rakefile.tt"),              File.join(target, "Rakefile"),               opts)
-      template(File.join("newgem/gitignore.tt"),             File.join(target, ".gitignore"),             opts)
       template(File.join("newgem/newgem.gemspec.tt"),        File.join(target, "#{name}.gemspec"),        opts)
       template(File.join("newgem/lib/newgem.rb.tt"),         File.join(target, "lib/#{name}.rb"),         opts)
       template(File.join("newgem/lib/newgem/version.rb.tt"), File.join(target, "lib/#{name}/version.rb"), opts)
       if options[:bin]
         template(File.join("newgem/bin/newgem.tt"),          File.join(target, 'bin', name),              opts)
       end
-      Bundler.ui.info "Initializating git repo in #{target}"
-      Dir.chdir(target) { `git init`; `git add .` }
+
+      if options[:hg]
+        template(File.join("newgem/hgignore.tt"), File.join(target, ".hgignore"), opts)
+        init_blk = Proc.new { `hg init .`; `hg addremove`}
+      else
+        template(File.join("newgem/gitignore.tt"), File.join(target, ".gitignore"), opts)
+        init_blk = Proc.new { `git init`; `git add .` }
+      end
+      Bundler.ui.info "Initializating #{options[:hg] ? 'hg' : 'git'} repo in #{target}"
+      Dir.chdir(target, &init_blk)
     end
 
     def self.source_root

lib/bundler/gem_helper_mercurial.rb

+require 'bundler/gem_helper'
+
+module Bundler
+  class GemHelperMercurial < GemHelper
+
+    protected
+    def hg_push
+      perform_hg_push ' --rev .'  #mimic the git only-current-branch-push
+      # the above command should push tags also
+      Bundler.ui.confirm "Pushed hg commits and tags for the current branch"
+    end
+    alias :git_push :hg_push
+
+    def perform_hg_push(options = '')
+      cmd = "hg push #{options}"
+      out, code = sh_with_code(cmd)
+      raise "Couldn't hg push. `#{cmd}' failed with the following output:\n\n#{out}\n" unless code == 0
+    end
+    alias :perform_git_push :perform_hg_push
+
+
+    def guard_already_tagged
+      # mercurial tags are outputted in the form:
+      # tagname1                local_commit_number:commit_hash
+      # tagname2                local_commit_number:commit_hash
+      sh('hg tags').split(/\n/).each do |t|
+        raise("This tag has already been committed to the repo.") if t.start_with?(version_tag)
+      end
+    end
+
+    def guard_clean
+      clean? or raise("There are files that need to be committed first.")
+    end
+
+    def clean?
+      sh("hg status -mard").empty?
+    end
+
+    def tag_version
+      sh "hg tag -m \"Version #{version}\" #{version_tag}"
+      Bundler.ui.confirm "Tagged #{version_tag}"
+      yield if block_given?
+    rescue
+      Bundler.ui.error "Untagged #{version_tag} due to error"
+      sh_with_code "hg tag --remove #{version_tag}"
+      raise
+    end
+  end
+end

lib/bundler/templates/newgem/Rakefile.tt

 require 'bundler'
+<% if config[:hg]%>
+Bundler::GemHelperMercurial.install_tasks
+<% else %>
 Bundler::GemHelper.install_tasks
+<% end %>

lib/bundler/templates/newgem/hgignore.tt

+glob: *.gem
+relre: ^\.bundle/
+glob: Gemfile.lock
+relre: ^pkg/

spec/other/gem_helper_mercurial_spec.rb

+require "spec_helper"
+require 'bundler/gem_helper_mercurial'
+
+describe "Bundler::GemHelperMercurial tasks" do
+  context "gem cli command" do
+    it "should generate .hgignore instead of .gitignore with -H flag" do
+      bundle 'gem test -H'
+      app = bundled_app("test")
+      helper = Bundler::GemHelperMercurial.new(app.to_s)
+      helper.gemspec.name.should == 'test'
+      File.exist?("#{app.to_s}/.hgignore").should be_true
+      File.exist?("#{app.to_s}/.gitignore").should be_false
+    end
+
+    it "should work as expected without -H flag" do
+      bundle 'gem test'
+      app = bundled_app("test")
+      helper = Bundler::GemHelperMercurial.new(app.to_s)
+      helper.gemspec.name.should == 'test'
+      File.exist?("#{app.to_s}/.hgignore").should be_false
+      File.exist?("#{app.to_s}/.gitignore").should be_true
+      rakefile = File.open("#{app.to_s}/Rakefile", 'r') {|f| f.readlines.map() {|line| line.strip}}
+      rakefile.should include("Bundler::GemHelper.install_tasks")
+      rakefile.should_not include("Bundler::GemHelperMercurial.install_tasks")
+    end
+  end
+
+  context "gem management" do
+    def mock_confirm_message(message)
+      Bundler.ui.should_receive(:confirm).with(message)
+    end
+
+    def mock_build_message
+      mock_confirm_message "test 0.0.1 built to pkg/test-0.0.1.gem"
+    end
+
+    before(:each) do
+      bundle 'gem test -H'
+      @app = bundled_app("test")
+      @gemspec = File.read("#{@app.to_s}/test.gemspec")
+      File.open("#{@app.to_s}/test.gemspec", 'w'){|f| f << @gemspec.gsub('TODO: ', '') }
+      File.open("#{@app.to_s}/.hg/hgrc", 'w') { |f| f << "[ui]\nusername = Me The Tester<me@tester.com>\n"}
+      @helper = Bundler::GemHelperMercurial.new(@app.to_s)
+    end
+
+    it "uses a shell UI for output" do
+      Bundler.ui.should be_a(Bundler::UI::Shell)
+    end
+
+    describe 'build' do
+      it "builds" do
+        mock_build_message
+        @helper.build_gem
+        bundled_app('test/pkg/test-0.0.1.gem').should exist
+      end
+
+      it "raises an appropriate error when the build fails" do
+        # break the gemspec by adding back the TODOs...
+        File.open("#{@app.to_s}/test.gemspec", 'w'){|f| f << @gemspec }
+        proc { @helper.build_gem }.should raise_error(/TODO/)
+      end
+    end
+
+    describe 'install' do
+      it "installs" do
+        mock_build_message
+        mock_confirm_message "test (0.0.1) installed"
+        @helper.install_gem
+        bundled_app('test/pkg/test-0.0.1.gem').should exist
+        %x{gem list}.should include("test (0.0.1)")
+      end
+
+      it "raises an appropriate error when the install fails" do
+        @helper.should_receive(:build_gem) do
+          # write an invalid gem file, so we can simulate install failure...
+          FileUtils.mkdir_p(File.join(@app.to_s, 'pkg'))
+          path = "#{@app.to_s}/pkg/test-0.0.1.gem"
+          File.open(path, 'w'){|f| f << "not actually a gem"}
+          path
+        end
+        proc { @helper.install_gem }.should raise_error
+      end
+    end
+
+    describe 'release' do
+      it "shouldn't push if there are uncommitted files" do
+        proc { @helper.release_gem }.should raise_error(/files that need to be committed/)
+      end
+
+      it 'raises an appropriate error if there is no git remote' do
+        Bundler.ui.stub(:confirm => nil, :error => nil) # silence messages
+
+
+        Dir.chdir(@app) {
+          #`hg init .`
+          `hg commit -m "initial commit"`
+        }
+
+        proc { @helper.release_gem }.should raise_error
+      end
+
+      it "releases" do
+        mock_build_message
+        mock_confirm_message(/Tagged v0.0.1/)
+        mock_confirm_message("Pushed hg commits and tags for the current branch")
+
+        @helper.should_receive(:rubygem_push).with(bundled_app('test/pkg/test-0.0.1.gem').to_s)
+
+        Dir.chdir(gem_repo1) {
+          `hg init .`
+        }
+        Dir.chdir(@app) {
+          #`hg init .`
+          open('.hg/hgrc', 'a') { |f| f << "[paths]\ndefault = #{gem_repo1}\n"}
+          `hg commit -m "initial commit"`
+          Open3.popen3("hg push") # use popen3 to silence output...
+          `hg commit -m "another commit"`
+        }
+        @helper.release_gem
+      end
+    end
+  end
+end