mirror of
https://github.com/capistrano/capistrano
synced 2023-03-27 23:21:18 -04:00
Added CVS module for SwitchTower (very, very experimental!)
git-svn-id: http://svn.rubyonrails.org/rails/trunk/switchtower@2027 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
parent
5246654ab7
commit
d89f08f780
3 changed files with 243 additions and 0 deletions
|
@ -1,5 +1,7 @@
|
||||||
*SVN*
|
*SVN*
|
||||||
|
|
||||||
|
* Added CVS module (very very experimental!)
|
||||||
|
|
||||||
* Works with public keys now, for passwordless deployment
|
* Works with public keys now, for passwordless deployment
|
||||||
|
|
||||||
* Subversion module recognizes the password prompt for HTTP authentication
|
* Subversion module recognizes the password prompt for HTTP authentication
|
||||||
|
|
82
lib/switchtower/scm/cvs.rb
Normal file
82
lib/switchtower/scm/cvs.rb
Normal file
|
@ -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
test/scm/cvs_test.rb
Normal file
159
test/scm/cvs_test.rb
Normal file
|
@ -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
|
Loading…
Reference in a new issue