From 2b6eb2f4f39476a383bd5676a69ab753f1a53214 Mon Sep 17 00:00:00 2001 From: Jamis Buck Date: Sat, 21 Jan 2006 05:03:23 +0000 Subject: [PATCH] Add perforce SCM module [Richard McMahon] git-svn-id: http://svn.rubyonrails.org/rails/tools/switchtower@3447 5ecf4fe2-1ee6-0310-87b1-e25e094e27de --- CHANGELOG | 2 + lib/switchtower/scm/perforce.rb | 139 ++++++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+) create mode 100644 lib/switchtower/scm/perforce.rb diff --git a/CHANGELOG b/CHANGELOG index 39f7e6b0..ebf1ba58 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,7 @@ *0.11.0* *SVN* +* Add perforce SCM module [Richard McMahon] + * Add bazaar (v1) SCM module [Edd Dumbill] (#3533) * Fix stftime format string used in CVS module to be Windows-compatible (fixes #3383) diff --git a/lib/switchtower/scm/perforce.rb b/lib/switchtower/scm/perforce.rb new file mode 100644 index 00000000..6d03beab --- /dev/null +++ b/lib/switchtower/scm/perforce.rb @@ -0,0 +1,139 @@ +require 'switchtower/scm/base' + +module SwitchTower + module SCM + + # An SCM module for using perforce as your source control tool. + # This module can explicitly selected by placing the following line + # in your configuration: + # + # set :scm, :perforce + # + # Also, this module accepts a :p4 configuration variable, + # which (if specified) will be used as the full path to the p4 + # executable on the remote machine: + # + # set :p4, "/usr/local/bin/p4" + # + # This module accepts another :p4sync_flags configuration + # variable, which (if specified) can add extra options. This setting + # defaults to the value "-f" which forces resynchronization. + # + # set :p4sync_flags, "-f" + # + # This module accepts another :p4client_root configuration + # variable to handle mapping adjustments. Perforce doesn't have the + # ability to sync to a specific directory (e.g. the release_path); the + # location being synced to is defined by the client-spec. As such, we + # sync the client-spec (defined by p4client and then copy from + # the determined root of the client-spec mappings to the release_path. + # In the unlikely event that your client-spec mappings introduces + # directory structure above the rails structure, you can override the + # may need to specify the directory + # + # set :p4client_root, "/user/rmcmahon/project1/code" + # + # Finally, the module accepts a p4diff2_options configuration + # variable. This can be used to manipulate the output when running a + # diff between what is deployed, and another revision. This option + # defaults to "-u". Run 'p4 help diff2' for other options. + # + class Perforce < Base + + def latest_revision + configuration.logger.debug "querying latest revision..." unless @latest_revision + @latest_revision = `#{local_p4} counter change`.strip + @latest_revision + end + + # Return the number of the revision currently deployed. + def current_revision(actor) + latest = actor.releases.last + grep = %(grep " #{latest}$" #{configuration.deploy_to}/revisions.log) + result = "" + actor.run(grep, :once => true) do |ch, str, out| + result << out if str == :out + raise "could not determine current revision" if str == :err + end + + date, time, user, rev, dir = result.split + raise "current revision not found in revisions.log" unless dir == latest + rev.to_i + end + + # Return a string containing the diff between the two revisions. +from+ + # and +to+ may be in any format that p4 recognizes as a valid revision + # identifiers. If +from+ is +nil+, it defaults to the last deployed + # revision. If +to+ is +nil+, it defaults to #head. + def diff(actor, from=nil, to=nil) + from ||= "@#{current_revision(actor)}" + to ||= "#head" + p4client = configuration[:p4client] + p4diff2_options = configuration[:p4diff2_options]||"-u -db" + `#{local_p4} diff2 #{p4diff2_options} //#{p4client}/...#{from} //#{p4client}/...#{to}` + end + + # Syncronizes (on all servers associated with the current task) the head + # revision of the code. Uses the given actor instance to execute the command. + # + def checkout(actor) + p4sync_flags = configuration[:p4sync_flags] || "-f" + p4client_root = configuration[:p4client_root] || "`#{remote_p4} client -o | grep ^Root | cut -f2`" + command = "#{remote_p4} sync #{p4sync_flags} && cp -rf #{p4client_root} #{actor.release_path};" + run_checkout(actor, command, &p4_stream_handler(actor)) + end + + def update(actor) + raise "#{self.class} doesn't support update(actor)" + end + + private + + def local_p4 + add_standard_p4_options('p4') + end + + def remote_p4 + p4_cmd = configuration[:p4] || 'p4' + add_standard_p4_options(p4_cmd) + end + + def add_standard_p4_options(p4_location) + check_settings + p4_cmd = p4_location + p4_cmd = "#{p4_cmd} -p #{configuration[:p4port]}" if configuration[:p4port] + p4_cmd = "#{p4_cmd} -u #{configuration[:p4user]}" if configuration[:p4user] + p4_cmd = "#{p4_cmd} -P #{configuration[:p4passwd]}" if configuration[:p4passwd] + p4_cmd = "#{p4_cmd} -c #{configuration[:p4client]}" if configuration[:p4client] + p4_cmd + end + + def check_settings + check_setting(:p4port, "Add set :p4port, to deploy.rb") + check_setting(:p4user, "Add set :p4user, to deploy.rb") + check_setting(:p4passwd, "Add set :p4passwd, to deploy.rb") + check_setting(:p4client, "Add set :p4client, to deploy.rb") + end + + def check_setting(p4setting, message) + raise "#{p4setting} is not configured. #{message}" unless configuration[p4setting] + end + + def p4_stream_handler(actor) + Proc.new do |ch, stream, out| + prefix = "#{stream} :: #{ch[:host]}" + actor.logger.info out, prefix + if out =~ /\(P4PASSWD\) invalid or unset\./i + raise "p4passwd is incorrect or unset" + elsif out =~ /Can.t create a new user.*/i + raise "p4user is incorrect or unset" + elsif out =~ /Perforce client error\:/i + raise "p4port is incorrect or unset" + elsif out =~ /Client \'[\w\-\_\.]+\' unknown.*/i + raise "p4client is incorrect or unset" + end + end + end + end + end +end