diff options
author | Jamis Buck <jamis@37signals.com> | 2005-08-17 10:13:25 +0000 |
---|---|---|
committer | Jamis Buck <jamis@37signals.com> | 2005-08-17 10:13:25 +0000 |
commit | 72f50e65ce95a27cb08373256ddd316ba621064e (patch) | |
tree | 6d0b9d0b2e69d9e88e3ea594058f1d39efba75d5 | |
parent | fbba128f73e9a297ca6252c85d3c42b34241ff2f (diff) | |
download | rails-72f50e65ce95a27cb08373256ddd316ba621064e.tar.gz rails-72f50e65ce95a27cb08373256ddd316ba621064e.tar.bz2 rails-72f50e65ce95a27cb08373256ddd316ba621064e.zip |
Added CVS module for SwitchTower (very, very experimental!)
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@2027 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
-rw-r--r-- | switchtower/CHANGELOG | 2 | ||||
-rw-r--r-- | switchtower/lib/switchtower/scm/cvs.rb | 82 | ||||
-rw-r--r-- | switchtower/test/scm/cvs_test.rb | 159 |
3 files changed, 243 insertions, 0 deletions
diff --git a/switchtower/CHANGELOG b/switchtower/CHANGELOG index f40d897780..7b3a24a42d 100644 --- a/switchtower/CHANGELOG +++ b/switchtower/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 diff --git a/switchtower/lib/switchtower/scm/cvs.rb b/switchtower/lib/switchtower/scm/cvs.rb new file mode 100644 index 0000000000..a4c588e611 --- /dev/null +++ b/switchtower/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 diff --git a/switchtower/test/scm/cvs_test.rb b/switchtower/test/scm/cvs_test.rb new file mode 100644 index 0000000000..3df64cce0e --- /dev/null +++ b/switchtower/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 |