Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #9238: accurev_patch.patch

File accurev_patch.patch, 7.8 kB (added by dbarth, 1 year ago)

Patch to add support for Accurev

  • test/deploy/scm/accurev_test.rb

    old new  
     1require "#{File.dirname(__FILE__)}/../../utils" 
     2require 'capistrano/recipes/deploy/scm/accurev' 
     3 
     4class AccurevTest < Test::Unit::TestCase 
     5  include Capistrano::Deploy::SCM 
     6 
     7  def test_internal_revision_to_s 
     8    assert_equal 'foo/1', Accurev::InternalRevision.new('foo', 1).to_s 
     9    assert_equal 'foo/highest', Accurev::InternalRevision.new('foo', 'highest').to_s 
     10  end 
     11 
     12  def test_internal_revision_parse 
     13    revision = Accurev::InternalRevision.parse('foo') 
     14    assert_equal 'foo', revision.stream 
     15    assert_equal 'highest', revision.transaction_id 
     16    assert_equal 'foo/highest', revision.to_s 
     17 
     18    revision = Accurev::InternalRevision.parse('foo/1') 
     19    assert_equal 'foo', revision.stream 
     20    assert_equal '1', revision.transaction_id 
     21    assert_equal 'foo/1', revision.to_s 
     22  end 
     23end 
  • lib/capistrano/recipes/deploy/scm/accurev.rb

    old new  
     1require 'capistrano/recipes/deploy/scm/base' 
     2require 'rexml/xpath' 
     3require 'rexml/document' 
     4 
     5module Capistrano 
     6  module Deploy 
     7    module SCM 
     8      # Accurev bridge for use by Capistrano. This implementation does not 
     9      # implement all features of a Capistrano SCM module. The ones that are 
     10      # left out are either exceedingly difficult to implement with Accurev 
     11      # or are considered bad form. 
     12      # 
     13      # When using this module in a project, the following variables are used: 
     14      #  * :repository - This should match the depot that code lives in. If your code 
     15      #                  exists in a subdirectory, you can append the path depot. 
     16      #                  eg. foo-depot/bar_dir 
     17      #  * :stream - The stream in the depot that code should be pulled from. If  
     18      #              left blank, the depot stream will be used 
     19      #  * :revision - Should be in the form 'stream/transaction'.  
     20      class Accurev < Base 
     21        include REXML 
     22        default_command 'accurev' 
     23 
     24        # Defines pseudo-revision value for the most recent changes to be deployed. 
     25        def head 
     26          "#{stream}/highest" 
     27        end 
     28 
     29        # Given an Accurev revision identifier, this method returns an identifier that 
     30        # can be used for later SCM calls. This returned identifier will not 
     31        # change as a result of further SCM activity. 
     32        def query_revision(revision) 
     33          internal_revision = InternalRevision.parse(revision) 
     34          return revision unless internal_revision.psuedo_revision? 
     35 
     36          logger.debug("Querying for real revision for #{internal_revision}") 
     37          rev_stream = internal_revision.stream 
     38 
     39          logger.debug("Determining what type of stream #{rev_stream} is...") 
     40          stream_xml = yield show_streams_for(rev_stream) 
     41          stream_doc = Document.new(stream_xml) 
     42          type = XPath.first(stream_doc, '//streams/stream/@type').value 
     43 
     44          case type 
     45          when 'snapshot' 
     46            InternalRevision.new(rev_stream, 'highest').to_s 
     47          else 
     48            logger.debug("Getting latest transaction id in #{rev_stream}") 
     49            # Doing another yield for a second Accurev call. Hopefully this is ok. 
     50            hist_xml = yield scm(:hist, '-ftx', '-s', rev_stream, '-t', 'now.1') 
     51            hist_doc = Document.new(hist_xml) 
     52            transaction_id = XPath.first(hist_doc, '//AcResponse/transaction/@id').value 
     53            InternalRevision.new(stream, transaction_id).to_s 
     54          end 
     55        end 
     56 
     57        # Pops a copy of the code for the specified Accurev revision identifier.  
     58        # The revision identifier is represented as a stream & transaction ID combo. 
     59        # Accurev can only pop a particular transaction if a stream is created on the server 
     60        # with a time basis of that transaction id. Therefore, we will create a stream with  
     61        # the required criteria and pop that. 
     62        def export(revision_id, destination) 
     63          revision = InternalRevision.parse(revision_id) 
     64          logger.debug("Exporting #{revision.stream}/#{revision.transaction_id} to #{destination}") 
     65 
     66          commands = [ 
     67            change_or_create_stream("#{revision.stream}-capistrano-deploy", revision), 
     68            "mkdir -p #{destination}", 
     69            scm_quiet(:pop, "-Rv #{stream}", "-L #{destination}", "'/./#{subdir}'") 
     70          ] 
     71          if subdir 
     72            commands.push( 
     73              "mv #{destination}/#{subdir}/* #{destination}", 
     74              "rm -rf #{File.join(destination, subdir)}" 
     75            ) 
     76          end 
     77          commands.join(' && ') 
     78        end 
     79 
     80        # Returns the command needed to show the changes that exist between the two revisions. 
     81        def log(from, to=head) 
     82          logger.info("Getting transactions between #{from} and #{to}") 
     83          from_rev = InternalRevision.parse(from) 
     84          to_rev = InternalRevision.parse(to) 
     85 
     86          [ 
     87            scm(:hist, '-s', from_rev.stream, '-t', "#{to_rev.transaction_id}-#{from_rev.transaction_id}"), 
     88            "sed -e '/transaction #{from_rev.transaction_id}/ { Q }'" 
     89          ].join(' | ') 
     90        end 
     91 
     92        # Returns the command needed to show the diff between what is deployed and what is  
     93        # pending. Because Accurev can not do this task without creating some streams, 
     94        # two time basis streams will be created for the purposes of doing the diff. 
     95        def diff(from, to=head) 
     96          from = InternalRevision.parse(from) 
     97          to = InternalRevision.parse(to) 
     98 
     99          from_stream = "#{from.stream}-capistrano-diff-from" 
     100          to_stream = "#{to.stream}-capistrano-diff-to" 
     101 
     102          [ 
     103            change_or_create_stream(from_stream, from), 
     104            change_or_create_stream(to_stream, to), 
     105            scm(:diff, '-v', from_stream, '-V', to_stream, '-a') 
     106          ].join(' && ') 
     107        end 
     108 
     109        private 
     110        def depot 
     111          repository.split('/')[0] 
     112        end 
     113 
     114        def stream 
     115          variable(:stream) || depot 
     116        end 
     117 
     118        def subdir 
     119          repository.split('/')[1..-1].join('/') unless repository.index('/').nil? 
     120        end 
     121 
     122        def change_or_create_stream(name, revision) 
     123          [ 
     124            scm_quiet(:mkstream, '-b', revision.stream, '-s', name, '-t', revision.transaction_id), 
     125            scm_quiet(:chstream, '-b', revision.stream, '-s', name, '-t', revision.transaction_id) 
     126          ].join('; ') 
     127        end 
     128 
     129        def show_streams_for(stream) 
     130          scm :show, '-fx', '-s', stream, :streams 
     131        end 
     132 
     133        def scm_quiet(*args) 
     134          scm(*args) + (variable(:scm_verbose) ? '' : '&> /dev/null') 
     135        end 
     136 
     137        class InternalRevision 
     138          attr_reader :stream, :transaction_id 
     139 
     140          def self.parse(string) 
     141            match = /([^\/]+)(\/(.+)){0,1}/.match(string) 
     142            raise "Unrecognized revision identifier: #{string}" unless match 
     143 
     144            stream = match[1] 
     145            transaction_id = match[3] || 'highest' 
     146            InternalRevision.new(stream, transaction_id) 
     147          end 
     148 
     149          def initialize(stream, transaction_id) 
     150            @stream = stream 
     151            @transaction_id = transaction_id 
     152          end 
     153 
     154          def psuedo_revision? 
     155            @transaction_id == 'highest' 
     156          end 
     157 
     158          def to_s 
     159            "#{stream}/#{transaction_id}"  
     160          end 
     161 
     162          def ==(other) 
     163            (stream == other.stream) && (transaction_id == other.transaction_id) 
     164          end 
     165        end 
     166      end 
     167    end 
     168  end 
     169end