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

replace obsolete configuration tests with a failing one. Fix misnamed execution test class. Remove SCM and generator code (which will find its way into a separate package eventually)

git-svn-id: http://svn.rubyonrails.org/rails/tools/capistrano@6273 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
Jamis Buck 2007-03-01 06:39:14 +00:00
parent 4c5597282d
commit 4789f87bf9
22 changed files with 26 additions and 1360 deletions

View file

@ -27,8 +27,8 @@ module Capistrano
# executed tasks.
attr_reader :sessions
def initialize_with_connections #:nodoc:
initialize_without_connections
def initialize_with_connections(*args) #:nodoc:
initialize_without_connections(*args)
@sessions = {}
end

View file

@ -19,8 +19,8 @@ module Capistrano
# A struct for representing a single instance of an invoked task.
TaskCallFrame = Struct.new(:task, :rollback)
def initialize_with_execution #:nodoc:
initialize_without_execution
def initialize_with_execution(*args) #:nodoc:
initialize_without_execution(*args)
@task_call_frames = []
end
private :initialize_with_execution

View file

@ -30,8 +30,8 @@ module Capistrano
# The load paths used for locating recipe files.
attr_reader :load_paths
def initialize_with_loading #:nodoc:
initialize_without_loading
def initialize_with_loading(*args) #:nodoc:
initialize_without_loading(*args)
@load_paths = [".", File.expand_path(File.join(File.dirname(__FILE__), "../recipes"))]
end
private :initialize_with_loading

View file

@ -13,8 +13,8 @@ module Capistrano
# role.
attr_reader :roles
def initialize_with_roles #:nodoc:
initialize_without_roles
def initialize_with_roles(*args) #:nodoc:
initialize_without_roles(*args)
@roles = Hash.new { |h,k| h[k] = [] }
end

View file

@ -85,8 +85,8 @@ module Capistrano
fetch(variable, nil)
end
def initialize_with_variables #:nodoc:
initialize_without_variables
def initialize_with_variables(*args) #:nodoc:
initialize_without_variables(*args)
@variables = {}
@original_procs = {}

View file

@ -1,25 +0,0 @@
class DeploymentGenerator < Rails::Generator::NamedBase
attr_reader :recipe_file
def initialize(runtime_args, runtime_options = {})
super
@recipe_file = @args.shift || "deploy"
end
def manifest
record do |m|
m.directory "config"
m.template "deploy.rb", File.join("config", "#{recipe_file}.rb")
m.directory "lib/tasks"
m.template "capistrano.rake", File.join("lib", "tasks", "capistrano.rake")
end
end
protected
# Override with your own usage banner.
def banner
"Usage: #{$0} deployment ApplicationName [recipe-name]\n" +
" (recipe-name defaults to \"deploy\")"
end
end

View file

@ -1,49 +0,0 @@
# =============================================================================
# A set of rake tasks for invoking the Capistrano automation utility.
# =============================================================================
# Invoke the given actions via Capistrano
def cap(*parameters)
begin
require 'rubygems'
rescue LoadError
# no rubygems to load, so we fail silently
end
require 'capistrano/cli'
STDERR.puts "Capistrano/Rake integration is deprecated."
STDERR.puts "Please invoke the 'cap' command directly: `cap #{parameters.join(" ")}'"
Capistrano::CLI.new(parameters.map { |param| param.to_s }).execute!
end
namespace :remote do
<%- config = Capistrano::Configuration.new
config.load "standard"
options = { :show_tasks => ", '-q'" }
config.actor.each_task do |info| -%>
<%- unless info[:desc].empty? -%>
desc "<%= info[:desc].scan(/.*?(?:\. |$)/).first.strip.gsub(/"/, "\\\"") %>"
<%- end -%>
task(<%= info[:task].inspect %>) { cap <%= info[:task].inspect %><%= options[info[:task]] %> }
<%- end -%>
desc "Execute a specific action using capistrano"
task :exec do
unless ENV['ACTION']
raise "Please specify an action (or comma separated list of actions) via the ACTION environment variable"
end
actions = ENV['ACTION'].split(",")
actions.concat(ENV['PARAMS'].split(" ")) if ENV['PARAMS']
cap(*actions)
end
end
desc "Push the latest revision into production (delegates to remote:deploy)"
task :deploy => "remote:deploy"
desc "Rollback to the release before the current release in production (delegates to remote:rollback)"
task :rollback => "remote:rollback"

View file

@ -1,122 +0,0 @@
# This defines a deployment "recipe" that you can feed to capistrano
# (http://manuals.rubyonrails.com/read/book/17). It allows you to automate
# (among other things) the deployment of your application.
# =============================================================================
# REQUIRED VARIABLES
# =============================================================================
# You must always specify the application and repository for every recipe. The
# repository must be the URL of the repository you want this recipe to
# correspond to. The deploy_to path must be the path on each machine that will
# form the root of the application path.
set :application, "<%= singular_name %>"
set :repository, "http://svn.yourhost.com/#{application}/trunk"
# =============================================================================
# ROLES
# =============================================================================
# You can define any number of roles, each of which contains any number of
# machines. Roles might include such things as :web, or :app, or :db, defining
# what the purpose of each machine is. You can also specify options that can
# be used to single out a specific subset of boxes in a particular role, like
# :primary => true.
role :web, "www01.example.com", "www02.example.com"
role :app, "app01.example.com", "app02.example.com", "app03.example.com"
role :db, "db01.example.com", :primary => true
role :db, "db02.example.com", "db03.example.com"
# =============================================================================
# OPTIONAL VARIABLES
# =============================================================================
# set :deploy_to, "/path/to/app" # defaults to "/u/apps/#{application}"
# set :user, "flippy" # defaults to the currently logged in user
# set :scm, :darcs # defaults to :subversion
# set :svn, "/path/to/svn" # defaults to searching the PATH
# set :darcs, "/path/to/darcs" # defaults to searching the PATH
# set :cvs, "/path/to/cvs" # defaults to searching the PATH
# set :gateway, "gate.host.com" # default to no gateway
# =============================================================================
# SSH OPTIONS
# =============================================================================
# ssh_options[:keys] = %w(/path/to/my/key /path/to/another/key)
# ssh_options[:port] = 25
# =============================================================================
# TASKS
# =============================================================================
# Define tasks that run on all (or only some) of the machines. You can specify
# a role (or set of roles) that each task should be executed on. You can also
# narrow the set of servers to a subset of a role by specifying options, which
# must match the options given for the servers to select (like :primary => true)
desc <<DESC
An imaginary backup task. (Execute the 'show_tasks' task to display all
available tasks.)
DESC
task :backup, :roles => :db, :only => { :primary => true } do
# the on_rollback handler is only executed if this task is executed within
# a transaction (see below), AND it or a subsequent task fails.
on_rollback { delete "/tmp/dump.sql" }
run "mysqldump -u theuser -p thedatabase > /tmp/dump.sql" do |ch, stream, out|
ch.send_data "thepassword\n" if out =~ /^Enter password:/
end
end
# Tasks may take advantage of several different helper methods to interact
# with the remote server(s). These are:
#
# * run(command, options={}, &block): execute the given command on all servers
# associated with the current task, in parallel. The block, if given, should
# accept three parameters: the communication channel, a symbol identifying the
# type of stream (:err or :out), and the data. The block is invoked for all
# output from the command, allowing you to inspect output and act
# accordingly.
# * sudo(command, options={}, &block): same as run, but it executes the command
# via sudo.
# * delete(path, options={}): deletes the given file or directory from all
# associated servers. If :recursive => true is given in the options, the
# delete uses "rm -rf" instead of "rm -f".
# * put(buffer, path, options={}): creates or overwrites a file at "path" on
# all associated servers, populating it with the contents of "buffer". You
# can specify :mode as an integer value, which will be used to set the mode
# on the file.
# * render(template, options={}) or render(options={}): renders the given
# template and returns a string. Alternatively, if the :template key is given,
# it will be treated as the contents of the template to render. Any other keys
# are treated as local variables, which are made available to the (ERb)
# template.
desc "Demonstrates the various helper methods available to recipes."
task :helper_demo do
# "setup" is a standard task which sets up the directory structure on the
# remote servers. It is a good idea to run the "setup" task at least once
# at the beginning of your app's lifetime (it is non-destructive).
setup
buffer = render("maintenance.rhtml", :deadline => ENV['UNTIL'])
put buffer, "#{shared_path}/system/maintenance.html", :mode => 0644
sudo "killall -USR1 dispatch.fcgi"
run "#{release_path}/script/spin"
delete "#{shared_path}/system/maintenance.html"
end
# You can use "transaction" to indicate that if any of the tasks within it fail,
# all should be rolled back (for each task that specifies an on_rollback
# handler).
desc "A task demonstrating the use of transactions."
task :long_deploy do
transaction do
update_code
disable_web
symlink
migrate
end
restart
enable_web
end

View file

@ -1,20 +0,0 @@
module Capistrano
module Generators
class RailsLoader
def self.load!(options)
require "#{options[:apply_to]}/config/environment"
require "rails_generator"
require "rails_generator/scripts/generate"
Rails::Generator::Base.sources << Rails::Generator::PathSource.new(
:capistrano, File.dirname(__FILE__))
args = ["deployment"]
args << (options[:application] || "Application")
args << (options[:recipe_file] || "deploy")
Rails::Generator::Scripts::Generate.new.run(args)
end
end
end
end

View file

@ -1,61 +0,0 @@
module Capistrano
module SCM
# The ancestor class of the various SCM module implementations.
class Base
attr_reader :configuration
def initialize(configuration) #:nodoc:
@configuration = configuration
end
def latest_revision
nil
end
def current_revision(actor)
raise "#{self.class} doesn't support querying the deployed revision"
end
def diff(actor, from=nil, to=nil)
raise "#{self.class} doesn't support diff(from, to)"
end
def update(actor)
raise "#{self.class} doesn't support update(actor)"
end
private
def run_checkout(actor, guts, &block)
directory = File.basename(configuration.release_path)
command = <<-STR
if [[ ! -d #{configuration.release_path} ]]; then
#{guts}
#{logging_commands(directory)}
fi
STR
actor.run(command, &block)
end
def run_update(actor, guts, &block)
command = <<-STR
#{guts}
#{logging_commands}
STR
actor.run(command, &block)
end
def logging_commands(directory = nil)
log = "#{configuration.deploy_to}/revisions.log"
"(test -e #{log} || (touch #{log} && chmod 666 #{log})) && " +
"echo `date +\"%Y-%m-%d %H:%M:%S\"` $USER #{configuration.revision} #{directory} >> #{log};"
end
end
end
end

View file

@ -1,118 +0,0 @@
require 'capistrano/scm/base'
module Capistrano
module SCM
# An SCM module for using Bazaar as your source control tool. This
# module is used by default, but you can explicitly specify it by
# placing the following line in your configuration:
#
# set :scm, :baz
#
# Also, this module accepts a <tt>:baz</tt> configuration variable,
# which (if specified) will be used as the full path to the svn
# executable on the remote machine:
#
# set :baz, "/opt/local/bin/baz"
#
# Set the version you wish to deploy as the repository variable,
# for example:
#
# set :repository, "you@example.com--dev/yourstuff--trunk--1.0"
#
# Ensure that you have already registered the archive on the target
# machines.
#
# As bazaar keeps a great deal of extra information on a checkout,
# you will probably want to use export instead:
#
# set :checkout, "export"
#
# TODO: provide setup recipe to register archive
class Baz < Base
# Return an integer identifying the last known revision in the baz
# repository. (This integer is currently the revision number.)
def latest_revision
`#{baz} revisions #{configuration.repository}`.split.last =~ /\-(\d+)$/
$1
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 bzr recognizes as a valid revision
# identifier. If +from+ is +nil+, it defaults to the last deployed
# revision. If +to+ is +nil+, it defaults to the last developed revision.
def diff(actor, from=nil, to=nil)
from ||= current_revision(actor)
to ||= latest_revision
from = baz_revision_name(from)
to = baz_revision_name(to)
`#{baz} delta --diffs -A #{baz_archive} #{baz_version}--#{from} #{baz_version}--#{to}`
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)
op = configuration[:checkout] || "get"
from = baz_revision_name(configuration.revision)
command = "#{baz} #{op} #{configuration.repository}--#{from} #{actor.release_path} &&"
run_checkout(actor, command, &baz_stream_handler(actor))
end
def update(actor)
command = "cd #{actor.current_path} && #{baz} update &&"
run_update(actor, command, &baz_stream_handler(actor))
end
private
def baz
configuration[:baz] || "baz"
end
def baz_revision_name(number)
if number.to_i == 0 then
"base-0"
else
"patch-#{number}"
end
end
def baz_archive
configuration[:repository][/(.*)\//, 1]
end
def baz_version
configuration[:repository][/\/(.*)$/, 1]
end
def baz_stream_handler(actor)
Proc.new do |ch, stream, out|
prefix = "#{stream} :: #{ch[:host]}"
actor.logger.info out, prefix
if out =~ /\bpassword.*:/i
actor.logger.info "baz is asking for a password", prefix
ch.send_data "#{actor.password}\n"
elsif out =~ %r{passphrase}
message = "baz needs your key's passphrase, sending empty string"
actor.logger.info message, prefix
ch.send_data "\n"
end
end
end
end
end
end

View file

@ -1,70 +0,0 @@
require 'capistrano/scm/base'
module Capistrano
module SCM
# An SCM module for using Bazaar-NG (bzr) as your source control tool.
# You can use it by placing the following line in your configuration:
#
# set :scm, :bzr
#
# Also, this module accepts a <tt>:bzr</tt> configuration variable,
# which (if specified) will be used as the full path to the bzr
# executable on the remote machine:
#
# set :bzr, "/opt/local/bin/bzr"
class Bzr < Base
# Return an integer identifying the last known revision in the bzr
# repository. (This integer is currently the revision number.)
def latest_revision
`#{bzr} revno #{configuration.repository}`.to_i
end
# Return the number of the revision currently deployed.
def current_revision(actor)
command = "#{bzr} revno #{actor.release_path} &&"
run_update(actor, command, &bzr_stream_handler(actor))
end
# Return a string containing the diff between the two revisions. +from+
# and +to+ may be in any format that bzr recognizes as a valid revision
# identifier. If +from+ is +nil+, it defaults to the last deployed
# revision. If +to+ is +nil+, it defaults to the last developed revision.
# Pay attention to the fact that as of now bzr does NOT support
# diff on remote locations.
def diff(actor, from=nil, to=nil)
from ||= current_revision(actor)
to ||= ""
`#{bzr} diff -r #{from}..#{to} #{configuration.repository}`
end
# Check out (on all servers associated with the current task) the latest
# revision. Uses the given actor instance to execute the command. If
# bzr asks for a password this will automatically provide it (assuming
# the requested password is the same as the password for logging into the
# remote server.)
def checkout(actor)
op = configuration[:checkout] || "branch"
command = "#{bzr} #{op} -r#{configuration.revision} #{configuration.repository} #{actor.release_path} &&"
run_checkout(actor, command, &bzr_stream_handler(actor))
end
def update(actor)
command = "cd #{actor.current_path} && #{bzr} pull -q &&"
run_update(actor, command, &bzr_stream_handler(actor))
end
private
def bzr
configuration[:bzr] || "bzr"
end
def bzr_stream_handler(actor)
Proc.new do |ch, stream, out|
prefix = "#{stream} :: #{ch[:host]}"
actor.logger.info out, prefix
end
end
end
end
end

View file

@ -1,129 +0,0 @@
require 'time'
require 'capistrano/scm/base'
module Capistrano
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
# ".".
#
# You may also specify a <tt>:branch</tt> configuration variable,
# which (if specified) will be used in the '-r' option to the cvs
# check out command. If it is not set, the module will determine if a
# branch is being used in the CVS sandbox relative to
# <tt>:local</tt> and act accordingly.
#
# set :branch, "prod-20060124"
#
# 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 < Base
def initialize(configuration)
super(configuration)
if not @configuration.respond_to?(:local) then
@configuration.set(:local,".")
end
if not configuration.respond_to?(:branch) then
configuration.set(:branch) { self.current_branch }
else
@current_branch = configuration[:branch]
end
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(cvs_local, configuration.branch).
split(/\r?\n/).
grep(/^date: (.*?);/) { Time.parse($1).strftime("%Y-%m-%d %H:%M:%S") }.
sort.
last
end
# Return a string representing the branch that the sandbox
# relative to <tt>:local</tt> contains.
def current_branch
return @current_branch if @current_branch
configuration.logger.debug "determining current_branch..."
@current_branch = cvs_branch(cvs_local)
end
# Check out (on all servers associated with the current task) the latest
# revision, using a branch if necessary. 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"
if "HEAD" == configuration.branch then
branch_option = ""
else
branch_option = "-r #{configuration.branch}"
end
command = <<-CMD
cd #{configuration.releases_path};
CVS_RSH="#{cvs_rsh}" #{cvs} -d #{configuration.repository} -Q co -D "#{configuration.revision}" #{branch_option} -d #{File.basename(actor.release_path)} #{actor.application};
CMD
run_checkout(actor, 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
# Look for a 'CVS/Tag' file in the path. If this file exists
# and contains a Line starting with 'T' then this CVS sandbox is
# 'tagged' with a branch. In the default case return 'HEAD'
def cvs_branch(path)
branch = "HEAD"
branch_file = File.join(path || ".", "CVS", "Tag")
if File.exists?(branch_file) then
File.open(branch_file) do |f|
possible_branch = f.find { |l| l =~ %r{^T} }
branch = possible_branch.strip[1..-1] if possible_branch
end
end
branch
end
def cvs_log(path,branch)
`cd #{path || "."} && cvs -d #{configuration.repository} -q log -N -r#{branch}`
end
def cvs_local
configuration.local || "."
end
end
end
end

View file

@ -1,27 +0,0 @@
require 'capistrano/scm/base'
module Capistrano
module SCM
# An SCM module for using darcs as your source control tool. Use it by
# specifying the following line in your configuration:
#
# set :scm, :darcs
#
# Also, this module accepts a <tt>:darcs</tt> configuration variable,
# which (if specified) will be used as the full path to the darcs
# executable on the remote machine:
#
# set :darcs, "/opt/local/bin/darcs"
class Darcs < Base
# 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)
darcs = configuration[:darcs] ? configuration[:darcs] : "darcs"
revision = configuration[:revision] ? %(--to-match "#{configuration.revision}") : ""
run_checkout(actor, "#{darcs} get -q --set-scripts-executable #{revision} #{configuration.repository} #{actor.release_path};")
end
end
end
end

View file

@ -1,83 +0,0 @@
require 'capistrano/scm/base'
module Capistrano
module SCM
# An SCM module for using Mercurial as your source control tool.
# You can use it by placing the following line in your configuration:
#
# set :scm, :mercurial
#
# Also, this module accepts a <tt>:mercurial</tt> configuration variable,
# which (if specified) will be used as the full path to the hg
# executable on the remote machine:
#
# set :mercurial, "/usr/local/bin/hg"
class Mercurial < Base
# Return a string identifying the tip changeset in the mercurial
# repository. Note that this fetches the tip changeset from the
# local repository, but capistrano will deploy from your _remote_
# repository. So just make sure your local repository is synchronized
# with your remote one.
def latest_revision
`#{mercurial} tip --template '{node|short}'`
end
# Return the changeset currently deployed.
def current_revision(actor)
# NOTE:
# copied almost verbatim from svn except its not cast into an int
# this should be the same for almost _every_ scm can we take it out
# of SCM-specific code?
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 changeset" if str == :err
end
date, time, user, changeset, dir = result.split
raise "current changeset not found in revisions.log" unless dir == latest
changeset
end
# Return a string containing the diff between the two changesets. +from+
# and +to+ may be in any format that mercurial recognizes as a valid
# changeset. If +from+ is +nil+, it defaults to the last deployed
# changeset. If +to+ is +nil+, it defaults to the current working
# directory.
def diff(actor, from = current_revision(actor), to = nil)
cmd = "#{mercurial} diff -r #{from}"
cmd << " -r #{to}" if to
`#{cmd}`
end
# Check out (on all servers associated with the current task) the latest
# revision. Uses the given actor instance to execute the command. If
# mercurial asks for a password this will automatically provide it
# (assuming the requested password is the same as the password for
# logging into the remote server.) If ssh repository method is used,
# authorized keys must be setup.
def checkout(actor)
command = "#{mercurial} clone -U #{configuration.repository} " +
"#{actor.release_path} && " +
"#{mercurial} -R #{actor.release_path} update " +
"-C #{configuration.revision} &&"
run_checkout(actor, command, &hg_stream_handler(actor))
end
private
def mercurial
configuration[:mercurial] || "hg"
end
def hg_stream_handler(actor)
Proc.new do |ch, stream, out|
prefix = "#{stream} :: #{ch[:host]}"
actor.logger.info out, prefix
end
end
end
end
end

View file

@ -1,139 +0,0 @@
require 'capistrano/scm/base'
module Capistrano
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 <tt>:p4</tt> 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 <tt>:p4sync_flags</tt> 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 <tt>:p4client_root</tt> 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 <tt>p4client</tt> 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 <tt>p4diff2_options</tt> 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, <your perforce server details e.g. my.p4.server:1666> to deploy.rb")
check_setting(:p4user, "Add set :p4user, <your production build username> to deploy.rb")
check_setting(:p4passwd, "Add set :p4passwd, <your build user password> to deploy.rb")
check_setting(:p4client, "Add set :p4client, <your client-spec name> 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

View file

@ -1,127 +0,0 @@
require 'capistrano/scm/base'
module Capistrano
module SCM
# An SCM module for using subversion as your source control tool. This
# module is used by default, but you can explicitly specify it by
# placing the following line in your configuration:
#
# set :scm, :subversion
#
# Also, this module accepts a <tt>:svn</tt> configuration variable,
# which (if specified) will be used as the full path to the svn
# executable on the remote machine:
#
# set :svn, "/opt/local/bin/svn"
class Subversion < Base
# Return an integer identifying the last known revision in the svn
# repository. (This integer is currently the revision number.)
def latest_revision
@latest_revision ||= begin
configuration.logger.debug "querying latest revision..."
match = svn_log(configuration.repository).scan(/r(\d+)/).first or
raise "Could not determine latest revision"
match.first
end
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 svn recognizes as a valid revision
# identifier. 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"
`svn diff #{authorization} #{configuration.repository}@#{from} #{configuration.repository}@#{to}`
end
# Check out (on all servers associated with the current task) the latest
# revision. Uses the given actor instance to execute the command. If
# svn asks for a password this will automatically provide it (assuming
# the requested password is the same as the password for logging into the
# remote server.)
def checkout(actor)
op = configuration[:checkout] || "co"
command = "#{svn} #{op} #{authorization} -q -r#{configuration.revision} #{configuration.repository} #{actor.release_path} &&"
run_checkout(actor, command, &svn_stream_handler(actor))
end
# Update the current release in-place. This assumes that the original
# deployment was made using checkout, and not something like export.
def update(actor)
command = "cd #{actor.current_path} && #{svn} up -q &&"
run_update(actor, command, &svn_stream_handler(actor))
end
private
def svn
configuration[:svn] || "svn"
end
def authorization
username = configuration[:svn_username] ? "--username #{configuration[:svn_username]}" : ""
password = configuration[:svn_password] ? "--password #{configuration[:svn_password]}" : ""
"--no-auth-cache #{username} #{password}"
end
def svn_log(path)
`svn log #{authorization} -q --limit 1 #{path}`
end
def svn_password
configuration[:svn_password] || configuration[:password]
end
def svn_passphrase
configuration[:svn_passphrase] || svn_password
end
def svn_stream_handler(actor)
Proc.new do |ch, stream, out|
prefix = "#{stream} :: #{ch[:host]}"
actor.logger.info out, prefix
if out =~ /\bpassword.*:/i
actor.logger.info "subversion is asking for a password", prefix
ch.send_data "#{svn_password}\n"
elsif out =~ %r{\(yes/no\)}
actor.logger.info "subversion is asking whether to connect or not",
prefix
ch.send_data "yes\n"
elsif out =~ %r{passphrase}i
message = "subversion needs your key's passphrase"
actor.logger.info message, prefix
ch.send_data "#{svn_passphrase}\n"
elsif out =~ %r{The entry \'(\w+)\' is no longer a directory}
message = "subversion can't update because directory '#{$1}' was replaced. Please add it to svn:ignore."
actor.logger.info message, prefix
raise message
elsif out =~ %r{accept \(t\)emporarily}
message = "accepting certificate temporarily"
actor.logger.info message, prefix
ch.send_data "t\n"
end
end
end
end
end
end

View file

@ -1,20 +1,15 @@
begin
require 'capistrano/version'
require 'net/sftp'
require 'net/sftp/version'
sftp_version = [Net::SFTP::Version::MAJOR, Net::SFTP::Version::MINOR, Net::SFTP::Version::TINY]
required_version = [1,1,0]
if !Capistrano::Version.check(required_version, sftp_version)
warn "You have Net::SFTP #{sftp_version.join(".")}, but you need at least #{required_version.join(".")}. Net::SFTP will not be used."
Capistrano::SFTP = false
else
Capistrano::SFTP = true
end
rescue LoadError
Capistrano::SFTP = false
end
require 'net/sftp'
module Capistrano
unless ENV['SKIP_VERSION_CHECK']
require 'capistrano/version'
require 'net/sftp/version'
sftp_version = [Net::SFTP::Version::MAJOR, Net::SFTP::Version::MINOR, Net::SFTP::Version::TINY]
required_version = [1,1,0]
if !Capistrano::Version.check(required_version, sftp_version)
raise "You have Net::SFTP #{sftp_version.join(".")}, but you need at least #{required_version.join(".")}. Net::SFTP will not be used."
end
end
# This class encapsulates a single file transfer to be performed in parallel
# across multiple machines, using the SFTP protocol.

View file

@ -2,10 +2,8 @@ require "#{File.dirname(__FILE__)}/../utils"
require 'capistrano/configuration/execution'
require 'capistrano/task_definition'
class ConfigurationNamespacesDSLTest < Test::Unit::TestCase
class ConfigurationExecutionTest < Test::Unit::TestCase
class MockConfig
include Mocha::AutoVerify
attr_reader :tasks, :namespaces, :fully_qualified_name, :parent
attr_reader :state, :original_initialize_called
attr_accessor :logger
@ -17,14 +15,14 @@ class ConfigurationNamespacesDSLTest < Test::Unit::TestCase
@state = {}
@fully_qualified_name = options[:fqn]
@parent = options[:parent]
@logger = stub(:debug => nil, :info => nil, :important => nil)
@logger = options.delete(:logger)
end
include Capistrano::Configuration::Execution
end
def setup
@config = MockConfig.new
@config = MockConfig.new(:logger => stub(:debug => nil, :info => nil, :important => nil))
end
def test_initialize_should_initialize_collections

View file

@ -1,35 +1,11 @@
$:.unshift File.dirname(__FILE__) + "/../lib"
require 'test/unit'
require 'capistrano/configuration'
require "#{File.dirname(__FILE__)}/utils"
class ConfigurationTest < Test::Unit::TestCase
def setup
@config = Capistrano::Configuration.new
end
def test_task_without_options
block = Proc.new { }
@config.task :hello, &block
assert_equal 1, @config.actor.tasks.length
assert_equal :hello, @config.actor.tasks[0][0]
assert_equal({}, @config.actor.tasks[0][1])
assert_equal block, @config.actor.tasks[0][2]
end
def test_task_with_options
block = Proc.new { }
@config.task :hello, :roles => :app, &block
assert_equal 1, @config.actor.tasks.length
assert_equal :hello, @config.actor.tasks[0][0]
assert_equal({:roles => :app}, @config.actor.tasks[0][1])
assert_equal block, @config.actor.tasks[0][2]
end
def test_task_description
block = Proc.new { }
@config.desc "A sample task"
@config.task :hello, &block
assert_equal "A sample task", @config.actor.tasks[0][1][:desc]
def test_flunk
flunk
end
end

View file

@ -1,196 +0,0 @@
$:.unshift File.dirname(__FILE__) + "/../../lib"
require File.dirname(__FILE__) + "/../utils"
require 'test/unit'
require 'capistrano/scm/cvs'
class ScmCvsTest < Test::Unit::TestCase
class CvsTest < Capistrano::SCM::Cvs
attr_accessor :story
attr_reader :last_path
def cvs_log(path,branch)
@last_path = path
story.shift
end
def cvs_branch(path)
"deploy-me"
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 release_path
(@config[:now] || Time.now.utc).strftime("%Y%m%d%H%M%S")
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[:cvs] = "/path/to/cvs"
@config[:password] = "chocolatebrownies"
@config[:now] = Time.utc(2005,8,24,12,0,0)
@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
@config[:local] = "/hello/world"
@scm.story = [ @log_msg ]
assert_equal "2004-10-12 02:21:02", @scm.latest_revision
assert_equal "/hello/world", @scm.last_path
end
def test_latest_with_default_local
@config[:local] = nil
@scm.story = [ @log_msg ]
assert_equal "2004-10-12 02:21:02", @scm.latest_revision
assert_equal ".", @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
def test_current_branch
assert_equal "deploy-me", @scm.current_branch
end
def test_default_current_branch
@config[:branch] = "default-branch"
@scm = CvsTest.new(@config)
assert_equal "default-branch", @scm.current_branch
end
def test_default_local
@config = MockConfiguration.new
@config[:repository] = ":ext:joetester@rubyforge.org:/hello/world"
@config[:cvs] = "/path/to/cvs"
@config[:password] = "chocolatebrownies"
@config[:now] = Time.utc(2005,8,24,12,0,0)
@scm = CvsTest.new(@config)
assert_equal ".", @scm.configuration.local
end
end

View file

@ -1,137 +0,0 @@
$:.unshift File.dirname(__FILE__) + "/../../lib"
require File.dirname(__FILE__) + "/../utils"
require 'test/unit'
require 'capistrano/scm/subversion'
class ScmSubversionTest < Test::Unit::TestCase
class SubversionTest < Capistrano::SCM::Subversion
attr_accessor :story
attr_reader :last_path
def svn_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[:current_path] = "/mwa/ha/ha/current"
@config[:repository] = "/hello/world"
@config[:svn] = "/path/to/svn"
@config[:password] = "chocolatebrownies"
@scm = SubversionTest.new(@config)
@actor = MockActor.new(@config)
@log_msg = <<MSG.strip
------------------------------------------------------------------------
r1967 | minam | 2005-08-03 06:59:03 -0600 (Wed, 03 Aug 2005) | 2 lines
Initial commit of the new capistrano utility
------------------------------------------------------------------------
MSG
@scm.story = [ @log_msg ]
end
def test_latest_revision
@scm.story = [ @log_msg ]
assert_equal "1967", @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/svn\b.*\bco\b.* -q}, @actor.command
end
def test_checkout_via_export
@actor.story = []
@config[:checkout] = "export"
assert_nothing_raised { @scm.checkout(@actor) }
assert_nil @actor.channels.last.sent_data
assert_match %r{/path/to/svn\b.*\bexport\b.* -q}, @actor.command
end
def test_update
@actor.story = []
assert_nothing_raised { @scm.update(@actor) }
assert_nil @actor.channels.last.sent_data
assert_match %r{/path/to/svn\b.*\bup\b}, @actor.command
end
def test_checkout_needs_ssh_password
@actor.story = [[:out, "Password: "]]
assert_nothing_raised { @scm.checkout(@actor) }
assert_equal ["chocolatebrownies\n"], @actor.channels.last.sent_data
end
def test_checkout_needs_http_password
@actor.story = [[:out, "Password for (something): "]]
assert_nothing_raised { @scm.checkout(@actor) }
assert_equal ["chocolatebrownies\n"], @actor.channels.last.sent_data
end
def test_checkout_needs_https_certificate
@actor.story = [[:out, "(R)eject, accept (t)emporarily or accept (p)ermanently? "]]
assert_nothing_raised { @scm.checkout(@actor) }
assert_equal ["t\n"], @actor.channels.last.sent_data
end
def test_checkout_needs_alternative_ssh_password
@actor.story = [[:out, "someone's password: "]]
assert_nothing_raised { @scm.checkout(@actor) }
assert_equal ["chocolatebrownies\n"], @actor.channels.last.sent_data
end
def test_svn_password
@config[:svn_password] = "butterscotchcandies"
@actor.story = [[:out, "Password: "]]
assert_nothing_raised { @scm.checkout(@actor) }
assert_equal ["butterscotchcandies\n"], @actor.channels.last.sent_data
end
def test_svn_username
@actor.story = []
@config[:svn_username] = "turtledove"
assert_nothing_raised { @scm.checkout(@actor) }
assert_nil @actor.channels.last.sent_data
assert_match %r{/path/to/svn\b.*\bco\b.* --username turtledove}, @actor.command
end
end