1
0
Fork 0
mirror of https://github.com/capistrano/capistrano synced 2023-03-27 23:21:18 -04:00

Add Accurev SCM support (closes #9238)

git-svn-id: http://svn.rubyonrails.org/rails/tools/capistrano@7375 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
Jamis Buck 2007-08-30 14:17:10 +00:00
parent db1a850915
commit 03522bf7c1
3 changed files with 194 additions and 0 deletions

View file

@ -1,5 +1,7 @@
*SVN*
* Add Accurev SCM support [Doug Barth]
* Use the :runner variable to determine who to sudo as for deploy:restart [Graham Ashton]
* Add Namespaces#top to always return a reference to the topmost namespace [Jamis Buck]

View file

@ -0,0 +1,169 @@
require 'capistrano/recipes/deploy/scm/base'
require 'rexml/xpath'
require 'rexml/document'
module Capistrano
module Deploy
module SCM
# Accurev bridge for use by Capistrano. This implementation does not
# implement all features of a Capistrano SCM module. The ones that are
# left out are either exceedingly difficult to implement with Accurev
# or are considered bad form.
#
# When using this module in a project, the following variables are used:
# * :repository - This should match the depot that code lives in. If your code
# exists in a subdirectory, you can append the path depot.
# eg. foo-depot/bar_dir
# * :stream - The stream in the depot that code should be pulled from. If
# left blank, the depot stream will be used
# * :revision - Should be in the form 'stream/transaction'.
class Accurev < Base
include REXML
default_command 'accurev'
# Defines pseudo-revision value for the most recent changes to be deployed.
def head
"#{stream}/highest"
end
# Given an Accurev revision identifier, this method returns an identifier that
# can be used for later SCM calls. This returned identifier will not
# change as a result of further SCM activity.
def query_revision(revision)
internal_revision = InternalRevision.parse(revision)
return revision unless internal_revision.psuedo_revision?
logger.debug("Querying for real revision for #{internal_revision}")
rev_stream = internal_revision.stream
logger.debug("Determining what type of stream #{rev_stream} is...")
stream_xml = yield show_streams_for(rev_stream)
stream_doc = Document.new(stream_xml)
type = XPath.first(stream_doc, '//streams/stream/@type').value
case type
when 'snapshot'
InternalRevision.new(rev_stream, 'highest').to_s
else
logger.debug("Getting latest transaction id in #{rev_stream}")
# Doing another yield for a second Accurev call. Hopefully this is ok.
hist_xml = yield scm(:hist, '-ftx', '-s', rev_stream, '-t', 'now.1')
hist_doc = Document.new(hist_xml)
transaction_id = XPath.first(hist_doc, '//AcResponse/transaction/@id').value
InternalRevision.new(stream, transaction_id).to_s
end
end
# Pops a copy of the code for the specified Accurev revision identifier.
# The revision identifier is represented as a stream & transaction ID combo.
# Accurev can only pop a particular transaction if a stream is created on the server
# with a time basis of that transaction id. Therefore, we will create a stream with
# the required criteria and pop that.
def export(revision_id, destination)
revision = InternalRevision.parse(revision_id)
logger.debug("Exporting #{revision.stream}/#{revision.transaction_id} to #{destination}")
commands = [
change_or_create_stream("#{revision.stream}-capistrano-deploy", revision),
"mkdir -p #{destination}",
scm_quiet(:pop, "-Rv #{stream}", "-L #{destination}", "'/./#{subdir}'")
]
if subdir
commands.push(
"mv #{destination}/#{subdir}/* #{destination}",
"rm -rf #{File.join(destination, subdir)}"
)
end
commands.join(' && ')
end
# Returns the command needed to show the changes that exist between the two revisions.
def log(from, to=head)
logger.info("Getting transactions between #{from} and #{to}")
from_rev = InternalRevision.parse(from)
to_rev = InternalRevision.parse(to)
[
scm(:hist, '-s', from_rev.stream, '-t', "#{to_rev.transaction_id}-#{from_rev.transaction_id}"),
"sed -e '/transaction #{from_rev.transaction_id}/ { Q }'"
].join(' | ')
end
# Returns the command needed to show the diff between what is deployed and what is
# pending. Because Accurev can not do this task without creating some streams,
# two time basis streams will be created for the purposes of doing the diff.
def diff(from, to=head)
from = InternalRevision.parse(from)
to = InternalRevision.parse(to)
from_stream = "#{from.stream}-capistrano-diff-from"
to_stream = "#{to.stream}-capistrano-diff-to"
[
change_or_create_stream(from_stream, from),
change_or_create_stream(to_stream, to),
scm(:diff, '-v', from_stream, '-V', to_stream, '-a')
].join(' && ')
end
private
def depot
repository.split('/')[0]
end
def stream
variable(:stream) || depot
end
def subdir
repository.split('/')[1..-1].join('/') unless repository.index('/').nil?
end
def change_or_create_stream(name, revision)
[
scm_quiet(:mkstream, '-b', revision.stream, '-s', name, '-t', revision.transaction_id),
scm_quiet(:chstream, '-b', revision.stream, '-s', name, '-t', revision.transaction_id)
].join('; ')
end
def show_streams_for(stream)
scm :show, '-fx', '-s', stream, :streams
end
def scm_quiet(*args)
scm(*args) + (variable(:scm_verbose) ? '' : '&> /dev/null')
end
class InternalRevision
attr_reader :stream, :transaction_id
def self.parse(string)
match = /([^\/]+)(\/(.+)){0,1}/.match(string)
raise "Unrecognized revision identifier: #{string}" unless match
stream = match[1]
transaction_id = match[3] || 'highest'
InternalRevision.new(stream, transaction_id)
end
def initialize(stream, transaction_id)
@stream = stream
@transaction_id = transaction_id
end
def psuedo_revision?
@transaction_id == 'highest'
end
def to_s
"#{stream}/#{transaction_id}"
end
def ==(other)
(stream == other.stream) && (transaction_id == other.transaction_id)
end
end
end
end
end
end

View file

@ -0,0 +1,23 @@
require "#{File.dirname(__FILE__)}/../../utils"
require 'capistrano/recipes/deploy/scm/accurev'
class AccurevTest < Test::Unit::TestCase
include Capistrano::Deploy::SCM
def test_internal_revision_to_s
assert_equal 'foo/1', Accurev::InternalRevision.new('foo', 1).to_s
assert_equal 'foo/highest', Accurev::InternalRevision.new('foo', 'highest').to_s
end
def test_internal_revision_parse
revision = Accurev::InternalRevision.parse('foo')
assert_equal 'foo', revision.stream
assert_equal 'highest', revision.transaction_id
assert_equal 'foo/highest', revision.to_s
revision = Accurev::InternalRevision.parse('foo/1')
assert_equal 'foo', revision.stream
assert_equal '1', revision.transaction_id
assert_equal 'foo/1', revision.to_s
end
end