mirror of
https://github.com/capistrano/capistrano
synced 2023-03-27 23:21:18 -04:00
Merge pull request #828 from coffeeaddict/decapsulated-scm
Decapsulated scm
This commit is contained in:
commit
f4d2bfe358
9 changed files with 446 additions and 10 deletions
|
@ -5,6 +5,7 @@ Reverse Chronological Order:
|
||||||
## master
|
## master
|
||||||
|
|
||||||
* Add a command line option to control role filter (`--roles`) (@andytinycat)
|
* Add a command line option to control role filter (`--roles`) (@andytinycat)
|
||||||
|
* Use an SCM object with a pluggable strategy
|
||||||
|
|
||||||
## `3.1.0` (not released)
|
## `3.1.0` (not released)
|
||||||
|
|
||||||
|
|
|
@ -1 +1,36 @@
|
||||||
load File.expand_path("../tasks/git.rake", __FILE__)
|
load File.expand_path("../tasks/git.rake", __FILE__)
|
||||||
|
|
||||||
|
require 'capistrano/scm'
|
||||||
|
|
||||||
|
class Capistrano::Git < Capistrano::SCM
|
||||||
|
|
||||||
|
# execute git with argument in the context
|
||||||
|
#
|
||||||
|
def git(*args)
|
||||||
|
args.unshift :git
|
||||||
|
context.execute *args
|
||||||
|
end
|
||||||
|
|
||||||
|
# The Capistrano default strategy for git. You should want to use this.
|
||||||
|
module DefaultStrategy
|
||||||
|
def test
|
||||||
|
test! " [ -f #{repo_path}/HEAD ] "
|
||||||
|
end
|
||||||
|
|
||||||
|
def check
|
||||||
|
test! :git, :'ls-remote', repo_url
|
||||||
|
end
|
||||||
|
|
||||||
|
def clone
|
||||||
|
git :clone, '--mirror', repo_url, repo_path
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
git :remote, :update
|
||||||
|
end
|
||||||
|
|
||||||
|
def release
|
||||||
|
git :archive, fetch(:branch), '| tar -x -C', release_path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -1 +1,33 @@
|
||||||
load File.expand_path("../tasks/hg.rake", __FILE__)
|
load File.expand_path("../tasks/hg.rake", __FILE__)
|
||||||
|
|
||||||
|
require 'capistrano/scm'
|
||||||
|
|
||||||
|
class Capistrano::Hg < Capistrano::SCM
|
||||||
|
# execute hg in context with arguments
|
||||||
|
def hg(*args)
|
||||||
|
args.unshift(:hg)
|
||||||
|
context.execute *args
|
||||||
|
end
|
||||||
|
|
||||||
|
module DefaultStrategy
|
||||||
|
def test
|
||||||
|
test! " [ -d #{repo_path}/.hg ] "
|
||||||
|
end
|
||||||
|
|
||||||
|
def check
|
||||||
|
hg "id", repo_url
|
||||||
|
end
|
||||||
|
|
||||||
|
def clone
|
||||||
|
hg "clone", "--noupdate", repo_url, repo_path
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
hg "pull"
|
||||||
|
end
|
||||||
|
|
||||||
|
def release
|
||||||
|
hg "archive", release_path, "--rev", fetch(:branch)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
116
lib/capistrano/scm.rb
Normal file
116
lib/capistrano/scm.rb
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
module Capistrano
|
||||||
|
|
||||||
|
# Base class for SCM strategy providers.
|
||||||
|
#
|
||||||
|
# @abstract
|
||||||
|
#
|
||||||
|
# @attr_reader [Rake] context
|
||||||
|
#
|
||||||
|
# @author Hartog de Mik
|
||||||
|
#
|
||||||
|
class SCM
|
||||||
|
attr_reader :context
|
||||||
|
|
||||||
|
# Provide a wrapper for the SCM that loads a strategy for the user.
|
||||||
|
#
|
||||||
|
# @param [Rake] context The context in which the strategy should run
|
||||||
|
# @param [Module] strategy A module to include into the SCM instance. The
|
||||||
|
# module should provide the abstract methods of Capistrano::SCM
|
||||||
|
#
|
||||||
|
def initialize(context, strategy)
|
||||||
|
@context = context
|
||||||
|
singleton = class << self; self; end
|
||||||
|
singleton.send(:include, strategy)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Call test in context
|
||||||
|
def test!(*args)
|
||||||
|
context.test *args
|
||||||
|
end
|
||||||
|
|
||||||
|
# The repository URL accoriding to the context
|
||||||
|
def repo_url
|
||||||
|
context.repo_url
|
||||||
|
end
|
||||||
|
|
||||||
|
# The repository path accoriding to the context
|
||||||
|
def repo_path
|
||||||
|
context.repo_path
|
||||||
|
end
|
||||||
|
|
||||||
|
# The release path accoriding to the context
|
||||||
|
def release_path
|
||||||
|
context.release_path
|
||||||
|
end
|
||||||
|
|
||||||
|
# Fetch a var from the context
|
||||||
|
# @param [Symbol] variable The variable to fetch
|
||||||
|
# @param [Object] default The default value if not found
|
||||||
|
#
|
||||||
|
def fetch(*args)
|
||||||
|
context.fetch(*args)
|
||||||
|
end
|
||||||
|
|
||||||
|
# @abstract
|
||||||
|
#
|
||||||
|
# Your implementation should check the existance of a cache repository on
|
||||||
|
# the deployment target
|
||||||
|
#
|
||||||
|
# @return [Boolean]
|
||||||
|
#
|
||||||
|
def test
|
||||||
|
raise NotImplementedError.new(
|
||||||
|
"Your SCM strategy module should provide a #test method"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
# @abstract
|
||||||
|
#
|
||||||
|
# Your implementation should check if the specified remote-repository is
|
||||||
|
# available.
|
||||||
|
#
|
||||||
|
# @return [Boolean]
|
||||||
|
#
|
||||||
|
def check
|
||||||
|
raise NotImplementedError.new(
|
||||||
|
"Your SCM strategy module should provide a #check method"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
# @abstract
|
||||||
|
#
|
||||||
|
# Create a (new) clone of the remote-repository on the deployment target
|
||||||
|
#
|
||||||
|
# @return void
|
||||||
|
#
|
||||||
|
def clone
|
||||||
|
raise NotImplementedError.new(
|
||||||
|
"Your SCM strategy module should provide a #clone method"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
# @abstract
|
||||||
|
#
|
||||||
|
# Update the clone on the deployment target
|
||||||
|
#
|
||||||
|
# @return void
|
||||||
|
#
|
||||||
|
def update
|
||||||
|
raise NotImplementedError.new(
|
||||||
|
"Your SCM strategy module should provide a #update method"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
# @abstract
|
||||||
|
#
|
||||||
|
# Copy the contents of the cache-repository onto the release path
|
||||||
|
#
|
||||||
|
# @return void
|
||||||
|
#
|
||||||
|
def release
|
||||||
|
raise NotImplementedError.new(
|
||||||
|
"Your SCM strategy module should provide a #release method"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,5 +1,9 @@
|
||||||
namespace :git do
|
namespace :git do
|
||||||
|
|
||||||
|
def strategy
|
||||||
|
@strategy ||= Capistrano::Git.new(self, fetch(:git_strategy, Capistrano::Git::DefaultStrategy))
|
||||||
|
end
|
||||||
|
|
||||||
set :git_environmental_variables, ->() {
|
set :git_environmental_variables, ->() {
|
||||||
{
|
{
|
||||||
git_askpass: "/bin/echo",
|
git_askpass: "/bin/echo",
|
||||||
|
@ -21,7 +25,7 @@ namespace :git do
|
||||||
fetch(:branch)
|
fetch(:branch)
|
||||||
on release_roles :all do
|
on release_roles :all do
|
||||||
with fetch(:git_environmental_variables) do
|
with fetch(:git_environmental_variables) do
|
||||||
exit 1 unless test :git, :'ls-remote', repo_url
|
exit 1 unless strategy.check
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -29,12 +33,12 @@ namespace :git do
|
||||||
desc 'Clone the repo to the cache'
|
desc 'Clone the repo to the cache'
|
||||||
task clone: :'git:wrapper' do
|
task clone: :'git:wrapper' do
|
||||||
on release_roles :all do
|
on release_roles :all do
|
||||||
if test " [ -f #{repo_path}/HEAD ] "
|
if strategy.test
|
||||||
info t(:mirror_exists, at: repo_path)
|
info t(:mirror_exists, at: repo_path)
|
||||||
else
|
else
|
||||||
within deploy_path do
|
within deploy_path do
|
||||||
with fetch(:git_environmental_variables) do
|
with fetch(:git_environmental_variables) do
|
||||||
execute :git, :clone, '--mirror', repo_url, repo_path
|
strategy.clone
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -46,7 +50,7 @@ namespace :git do
|
||||||
on release_roles :all do
|
on release_roles :all do
|
||||||
within repo_path do
|
within repo_path do
|
||||||
capturing_revisions do
|
capturing_revisions do
|
||||||
execute :git, :remote, :update
|
strategy.update
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -58,7 +62,7 @@ namespace :git do
|
||||||
with fetch(:git_environmental_variables) do
|
with fetch(:git_environmental_variables) do
|
||||||
within repo_path do
|
within repo_path do
|
||||||
execute :mkdir, '-p', release_path
|
execute :mkdir, '-p', release_path
|
||||||
execute :git, :archive, fetch(:branch), '| tar -x -C', release_path
|
strategy.release
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,19 +1,23 @@
|
||||||
namespace :hg do
|
namespace :hg do
|
||||||
|
def strategy
|
||||||
|
@strategy ||= Capistrano::Hg.new(self, fetch(:hg_strategy, Capistrano::Hg::DefaultStrategy))
|
||||||
|
end
|
||||||
|
|
||||||
desc 'Check that the repo is reachable'
|
desc 'Check that the repo is reachable'
|
||||||
task :check do
|
task :check do
|
||||||
on release_roles :all do
|
on release_roles :all do
|
||||||
execute "hg", "id", repo_url
|
strategy.check
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
desc 'Clone the repo to the cache'
|
desc 'Clone the repo to the cache'
|
||||||
task :clone do
|
task :clone do
|
||||||
on release_roles :all do
|
on release_roles :all do
|
||||||
if test " [ -d #{repo_path}/.hg ] "
|
if strategy.test
|
||||||
info t(:mirror_exists, at: repo_path)
|
info t(:mirror_exists, at: repo_path)
|
||||||
else
|
else
|
||||||
within deploy_path do
|
within deploy_path do
|
||||||
execute "hg", "clone", "--noupdate", repo_url, repo_path
|
strategy.clone
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -23,7 +27,7 @@ namespace :hg do
|
||||||
task :update => :'hg:clone' do
|
task :update => :'hg:clone' do
|
||||||
on release_roles :all do
|
on release_roles :all do
|
||||||
within repo_path do
|
within repo_path do
|
||||||
execute "hg", "pull"
|
strategy.update
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -32,7 +36,7 @@ namespace :hg do
|
||||||
task :create_release => :'hg:update' do
|
task :create_release => :'hg:update' do
|
||||||
on release_roles :all do
|
on release_roles :all do
|
||||||
within repo_path do
|
within repo_path do
|
||||||
execute "hg", "archive", release_path, "--rev", fetch(:branch)
|
strategy.release
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
70
spec/lib/capistrano/git_spec.rb
Normal file
70
spec/lib/capistrano/git_spec.rb
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
require 'capistrano/git'
|
||||||
|
|
||||||
|
module Capistrano
|
||||||
|
describe Git do
|
||||||
|
let(:context) { Class.new.new }
|
||||||
|
subject { Capistrano::Git.new(context, Capistrano::Git::DefaultStrategy) }
|
||||||
|
|
||||||
|
describe "#git" do
|
||||||
|
it "should call execute git in the context, with arguments" do
|
||||||
|
context.expects(:execute).with(:git, :init)
|
||||||
|
subject.git(:init)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe Git::DefaultStrategy do
|
||||||
|
let(:context) { Class.new.new }
|
||||||
|
subject { Capistrano::Git.new(context, Capistrano::Git::DefaultStrategy) }
|
||||||
|
|
||||||
|
describe "#test" do
|
||||||
|
it "should call test for repo HEAD" do
|
||||||
|
context.expects(:repo_path).returns("/path/to/repo")
|
||||||
|
context.expects(:test).with " [ -f /path/to/repo/HEAD ] "
|
||||||
|
|
||||||
|
subject.test
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#check" do
|
||||||
|
it "should test the repo url" do
|
||||||
|
context.expects(:repo_url).returns(:url)
|
||||||
|
context.expects(:test).with(:git, :'ls-remote', :url).returns(true)
|
||||||
|
|
||||||
|
subject.check
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#clone" do
|
||||||
|
it "should run git clone" do
|
||||||
|
context.expects(:repo_url).returns(:url)
|
||||||
|
context.expects(:repo_path).returns(:path)
|
||||||
|
|
||||||
|
context.expects(:execute).with(:git, :clone, '--mirror', :url, :path)
|
||||||
|
|
||||||
|
subject.clone
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#update" do
|
||||||
|
it "should run git update" do
|
||||||
|
context.expects(:execute).with(:git, :remote, :update)
|
||||||
|
|
||||||
|
subject.update
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#release" do
|
||||||
|
it "should run git archive" do
|
||||||
|
context.expects(:fetch).returns(:branch)
|
||||||
|
context.expects(:release_path).returns(:path)
|
||||||
|
|
||||||
|
context.expects(:execute).with(:git, :archive, :branch, '| tar -x -C', :path)
|
||||||
|
|
||||||
|
subject.release
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
70
spec/lib/capistrano/hg_spec.rb
Normal file
70
spec/lib/capistrano/hg_spec.rb
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
require 'capistrano/hg'
|
||||||
|
|
||||||
|
module Capistrano
|
||||||
|
describe Hg do
|
||||||
|
let(:context) { Class.new.new }
|
||||||
|
subject { Capistrano::Hg.new(context, Capistrano::Hg::DefaultStrategy) }
|
||||||
|
|
||||||
|
describe "#hg" do
|
||||||
|
it "should call execute hg in the context, with arguments" do
|
||||||
|
context.expects(:execute).with(:hg, :init)
|
||||||
|
subject.hg(:init)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe Hg::DefaultStrategy do
|
||||||
|
let(:context) { Class.new.new }
|
||||||
|
subject { Capistrano::Hg.new(context, Capistrano::Hg::DefaultStrategy) }
|
||||||
|
|
||||||
|
describe "#test" do
|
||||||
|
it "should call test for repo HEAD" do
|
||||||
|
context.expects(:repo_path).returns("/path/to/repo")
|
||||||
|
context.expects(:test).with " [ -d /path/to/repo/.hg ] "
|
||||||
|
|
||||||
|
subject.test
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#check" do
|
||||||
|
it "should test the repo url" do
|
||||||
|
context.expects(:repo_url).returns(:url)
|
||||||
|
context.expects(:execute).with(:hg, "id", :url)
|
||||||
|
|
||||||
|
subject.check
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#clone" do
|
||||||
|
it "should run hg clone" do
|
||||||
|
context.expects(:repo_url).returns(:url)
|
||||||
|
context.expects(:repo_path).returns(:path)
|
||||||
|
|
||||||
|
context.expects(:execute).with(:hg, "clone", '--noupdate', :url, :path)
|
||||||
|
|
||||||
|
subject.clone
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#update" do
|
||||||
|
it "should run hg update" do
|
||||||
|
context.expects(:execute).with(:hg, "pull")
|
||||||
|
|
||||||
|
subject.update
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#release" do
|
||||||
|
it "should run hg archive" do
|
||||||
|
context.expects(:fetch).returns(:branch)
|
||||||
|
context.expects(:release_path).returns(:path)
|
||||||
|
|
||||||
|
context.expects(:execute).with(:hg, "archive", :path, "--rev", :branch)
|
||||||
|
|
||||||
|
subject.release
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
104
spec/lib/capistrano/scm_spec.rb
Normal file
104
spec/lib/capistrano/scm_spec.rb
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
require 'capistrano/scm'
|
||||||
|
|
||||||
|
module RaiseNotImplementedMacro
|
||||||
|
def raise_not_implemented_on(method)
|
||||||
|
it "should raise NotImplemented on #{method}" do
|
||||||
|
expect {
|
||||||
|
subject.send(method)
|
||||||
|
}.to raise_error(NotImplementedError)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
RSpec.configure do
|
||||||
|
include RaiseNotImplementedMacro
|
||||||
|
end
|
||||||
|
|
||||||
|
module DummyStrategy
|
||||||
|
def test
|
||||||
|
test!("you dummy!")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module BlindStrategy; end
|
||||||
|
|
||||||
|
module Capistrano
|
||||||
|
describe SCM do
|
||||||
|
let(:context) { Class.new.new }
|
||||||
|
|
||||||
|
describe "#initialize" do
|
||||||
|
subject { Capistrano::SCM.new(context, DummyStrategy) }
|
||||||
|
|
||||||
|
it "should load the provided strategy" do
|
||||||
|
context.expects(:test).with("you dummy!")
|
||||||
|
subject.test
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "Convenience methods" do
|
||||||
|
subject { Capistrano::SCM.new(context, BlindStrategy) }
|
||||||
|
|
||||||
|
describe "#test!" do
|
||||||
|
it "should return call test on the context" do
|
||||||
|
context.expects(:test).with(:x)
|
||||||
|
subject.test!(:x)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#repo_url" do
|
||||||
|
it "should return the repo url according to the context" do
|
||||||
|
context.expects(:repo_url).returns(:url)
|
||||||
|
subject.repo_url.should == :url
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#repo_path" do
|
||||||
|
it "should return the repo path according to the context" do
|
||||||
|
context.expects(:repo_path).returns(:path)
|
||||||
|
subject.repo_path.should == :path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#release_path" do
|
||||||
|
it "should return the release path according to the context" do
|
||||||
|
context.expects(:release_path).returns('/path/to/nowhere')
|
||||||
|
subject.release_path.should == '/path/to/nowhere'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#fetch" do
|
||||||
|
it "should call fetch on the context" do
|
||||||
|
context.expects(:fetch)
|
||||||
|
subject.fetch(:branch)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "With a 'blind' strategy" do
|
||||||
|
subject { Capistrano::SCM.new(context, BlindStrategy) }
|
||||||
|
|
||||||
|
describe "#test" do
|
||||||
|
raise_not_implemented_on(:test)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#check" do
|
||||||
|
raise_not_implemented_on(:check)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#clone" do
|
||||||
|
raise_not_implemented_on(:clone)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#update" do
|
||||||
|
raise_not_implemented_on(:update)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#release" do
|
||||||
|
raise_not_implemented_on(:release)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
Loading…
Reference in a new issue