Skip to content

Commit

Permalink
Added CVS module for SwitchTower (very, very experimental!)
Browse files Browse the repository at this point in the history
git-svn-id: http://svn.rubyonrails.org/rails/trunk/switchtower@2027 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information
jamis committed Aug 17, 2005
1 parent 5246654 commit d89f08f
Show file tree
Hide file tree
Showing 3 changed files with 243 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG
@@ -1,5 +1,7 @@
*SVN*

* Added CVS module (very very experimental!)

* Works with public keys now, for passwordless deployment

* Subversion module recognizes the password prompt for HTTP authentication
Expand Down
82 changes: 82 additions & 0 deletions lib/switchtower/scm/cvs.rb
@@ -0,0 +1,82 @@
require 'time'

module SwitchTower
module SCM

# An SCM module for using CVS as your source control tool. You can
# specify it by placing the following line in your configuration:
#
# set :scm, :cvs
#
# Also, this module accepts a <tt>:cvs</tt> configuration variable,
# which (if specified) will be used as the full path to the cvs
# executable on the remote machine:
#
# set :cvs, "/opt/local/bin/cvs"
#
# You can specify the location of your local copy (used to query
# the revisions, etc.) via the <tt>:local</tt> variable, which defaults to
# ".".
#
# Also, you can specify the CVS_RSH variable to use on the remote machine(s)
# via the <tt>:cvs_rsh</tt> variable. This defaults to the value of the
# CVS_RSH environment variable locally, or if it is not set, to "ssh".
class Cvs
attr_reader :configuration

def initialize(configuration) #:nodoc:
@configuration = configuration
end

# Return a string representing the date of the last revision (CVS is
# seriously retarded, in that it does not give you a way to query when
# the last revision was made to the repository, so this is a fairly
# expensive operation...)
def latest_revision
return @latest_revision if @latest_revision
configuration.logger.debug "querying latest revision..."
@latest_revision = cvs_log(configuration.local).
split(/\r?\n/).
grep(/^date: (.*?);/) { Time.parse($1).strftime("%FT%T") }.
sort.
last
end

# Check out (on all servers associated with the current task) the latest
# revision. Uses the given actor instance to execute the command.
def checkout(actor)
cvs = configuration[:cvs] || "cvs"
cvs_rsh = configuration[:cvs_rsh] || ENV['CVS_RSH'] || "ssh"

command = <<-CMD
if [[ -d #{actor.release_path} ]]; then
cd #{actor.release_path};
CVS_RSH="#{cvs_rsh}" #{cvs} -q up -d#{latest_revision};
else
cd #{configuration.releases_path};
CVS_RSH="#{cvs_rsh}" #{cvs} -d #{configuration.repository} -q co -D #{latest_revision} -d #{latest_revision} #{actor.application};
fi
CMD
actor.run(command) do |ch, stream, out|
prefix = "#{stream} :: #{ch[:host]}"
actor.logger.info out, prefix
if out =~ %r{password:}
actor.logger.info "CVS is asking for a password", prefix
ch.send_data "#{actor.password}\n"
elsif out =~ %r{^Enter passphrase}
message = "CVS needs your key's passphrase and cannot proceed"
actor.logger.info message, prefix
raise message
end
end
end

private

def cvs_log(path)
`cd #{path || "."} && cvs -q log -N -rHEAD`
end
end

end
end
159 changes: 159 additions & 0 deletions test/scm/cvs_test.rb
@@ -0,0 +1,159 @@
$:.unshift File.dirname(__FILE__) + "/../../lib"

require File.dirname(__FILE__) + "/../utils"
require 'test/unit'
require 'switchtower/scm/cvs'

class ScmCvsTest < Test::Unit::TestCase
class CvsTest < SwitchTower::SCM::Cvs
attr_accessor :story
attr_reader :last_path

def cvs_log(path)
@last_path = path
story.shift
end
end

class MockChannel
attr_reader :sent_data

def send_data(data)
@sent_data ||= []
@sent_data << data
end

def [](name)
"value"
end
end

class MockActor
attr_reader :command
attr_reader :channels
attr_accessor :story

def initialize(config)
@config = config
end

def run(command)
@command = command
@channels ||= []
@channels << MockChannel.new
story.each { |stream, line| yield @channels.last, stream, line }
end

def method_missing(sym, *args)
@config.send(sym, *args)
end
end

def setup
@config = MockConfiguration.new
@config[:repository] = ":ext:joetester@rubyforge.org:/hello/world"
@config[:local] = "/hello/world"
@config[:cvs] = "/path/to/cvs"
@config[:password] = "chocolatebrownies"
@scm = CvsTest.new(@config)
@actor = MockActor.new(@config)
@log_msg = <<MSG.strip
RCS file: /var/cvs/copland/copland/LICENSE,v
Working file: LICENSE
head: 1.1
branch:
locks: strict
access list:
keyword substitution: kv
total revisions: 1; selected revisions: 1
description:
----------------------------
revision 1.1
date: 2004/08/29 04:23:36; author: minam; state: Exp;
New implementation.
=============================================================================
RCS file: /var/cvs/copland/copland/Rakefile,v
Working file: Rakefile
head: 1.7
branch:
locks: strict
access list:
keyword substitution: kv
total revisions: 7; selected revisions: 1
description:
----------------------------
revision 1.7
date: 2004/09/15 16:35:01; author: minam; state: Exp; lines: +2 -1
Rakefile now publishes package documentation from doc/packages instead of
doc/packrat. Updated "latest updates" in manual.
=============================================================================
RCS file: /var/cvs/copland/copland/TODO,v
Working file: TODO
head: 1.18
branch:
locks: strict
access list:
keyword substitution: kv
total revisions: 18; selected revisions: 1
description:
----------------------------
revision 1.18
date: 2004/10/12 02:21:02; author: minam; state: Exp; lines: +4 -1
Added RubyConf 2004 presentation.
=============================================================================
RCS file: /var/cvs/copland/copland/Attic/build-gemspec.rb,v
Working file: build-gemspec.rb
head: 1.5
branch:
locks: strict
access list:
keyword substitution: kv
total revisions: 5; selected revisions: 1
description:
----------------------------
revision 1.5
date: 2004/08/29 04:10:17; author: minam; state: dead; lines: +0 -0
Here we go -- point of no return. Deleting existing implementation to make
way for new implementation.
=============================================================================
RCS file: /var/cvs/copland/copland/copland.gemspec,v
Working file: copland.gemspec
head: 1.12
branch:
locks: strict
access list:
keyword substitution: kv
total revisions: 13; selected revisions: 1
description:
----------------------------
revision 1.12
date: 2004/09/11 21:45:58; author: minam; state: Exp; lines: +4 -4
Minor change in how version is communicated to gemspec.
=============================================================================
MSG
@scm.story = [ @log_msg ]
end

def test_latest_revision
@scm.story = [ @log_msg ]
assert_equal "2004-10-12T02:21:02", @scm.latest_revision
assert_equal "/hello/world", @scm.last_path
end

def test_checkout
@actor.story = []
assert_nothing_raised { @scm.checkout(@actor) }
assert_nil @actor.channels.last.sent_data
assert_match %r{/path/to/cvs}, @actor.command
end

def test_checkout_needs_ssh_password
@actor.story = [[:out, "joetester@rubyforge.org's password: "]]
assert_nothing_raised { @scm.checkout(@actor) }
assert_equal ["chocolatebrownies\n"], @actor.channels.last.sent_data
end
end

0 comments on commit d89f08f

Please sign in to comment.