diff --git a/docs/documentation/advanced-features/custom-scm/index.markdown b/docs/documentation/advanced-features/custom-scm/index.markdown index 57579ffd..b423143c 100644 --- a/docs/documentation/advanced-features/custom-scm/index.markdown +++ b/docs/documentation/advanced-features/custom-scm/index.markdown @@ -20,20 +20,20 @@ Capistrano checks out your application's source code. SCM plugins can be packaged as Ruby gems and distributed to other users. This document is a short guide to writing your own plugin. *It applies to -Capistrano 3.5.0 and newer.* +Capistrano 3.7.0 and newer.* -### 1. Write a Ruby class that extends Capistrano::Plugin +### 1. Write a Ruby class that extends Capistrano::SCM::Plugin Let's say you want to create a "Foo" SCM. You'll need to write a plugin class, like this: ```ruby -require "capistrano/plugin" +require "capistrano/scm/plugin" # By convention, Capistrano plugins are placed in the # Capistrano namespace. This is completely optional. module Capistrano - class FooPlugin < ::Capistrano::Plugin + class FooPlugin < ::Capistrano::SCM::Plugin def set_defaults # Define any variables needed to configure the plugin. # set_if_empty :myvar, "my-default-value" @@ -42,11 +42,13 @@ module Capistrano end ``` -### 2. Implement the create_release task +### 2. Implement a create_release task When the user runs `cap deploy`, your SCM is responsible for creating the release directory and copying the application source code into it. You need to -do this using a `create_release` task that is namespaced to your plugin. +do this using a task that is registered to run after `deploy:new_release_path`. + +By convention (not a requirement), this task is called `create_release`. Inside your plugin class, use the `define_tasks` and `register_hooks` methods like this: @@ -56,7 +58,6 @@ def define_tasks # The namespace can be whatever you want, but its best # to choose a name that matches your plugin name. namespace :foo do - # The task *must* be named `create_release` task :create_release do # Your code to create the release directory and copy # the source code into it goes here. diff --git a/lib/capistrano/configuration.rb b/lib/capistrano/configuration.rb index 2fc08ea5..e7f093ac 100644 --- a/lib/capistrano/configuration.rb +++ b/lib/capistrano/configuration.rb @@ -140,6 +140,10 @@ module Capistrano load_immediately: load_immediately) end + def scm_plugin_installed? + installer.scm_installed? + end + def servers @servers ||= Servers.new end diff --git a/lib/capistrano/configuration/plugin_installer.rb b/lib/capistrano/configuration/plugin_installer.rb index 82f4f4e4..c4ce176d 100644 --- a/lib/capistrano/configuration/plugin_installer.rb +++ b/lib/capistrano/configuration/plugin_installer.rb @@ -26,6 +26,7 @@ module Capistrano plugin.define_tasks plugin.register_hooks if load_hooks + @scm_installed ||= provides_scm?(plugin) if load_immediately plugin.set_defaults @@ -35,6 +36,16 @@ module Capistrano end end end + + def scm_installed? + @scm_installed + end + + private + + def provides_scm?(plugin) + plugin.respond_to?(:scm?) && plugin.scm? + end end end end diff --git a/lib/capistrano/configuration/scm_resolver.rb b/lib/capistrano/configuration/scm_resolver.rb index d0351f61..c4d37593 100644 --- a/lib/capistrano/configuration/scm_resolver.rb +++ b/lib/capistrano/configuration/scm_resolver.rb @@ -29,7 +29,8 @@ module Capistrano set(:scm, :git) if using_default_scm? print_deprecation_warnings_if_applicable - return if scm_plugin_loaded? + # Note that `scm_plugin_installed?` comes from Capistrano::DSL + return if scm_plugin_installed? if built_in_scm_name? load_built_in_scm @@ -47,13 +48,6 @@ module Capistrano @using_default_scm = (fetch(:scm) == DEFAULT_GIT) end - # This is somewhat of a hack, because there is no guarantee that a third- - # party SCM will necessarily implement the typical SCM tasks. But it works - # well enough for the built-in SCMs. - def scm_plugin_loaded? - Rake::Task.tasks.any? { |t| t.name =~ /^[^:]+:create_release/ } - end - def scm_name fetch(:scm) end @@ -102,7 +96,7 @@ module Capistrano def print_deprecation_warnings_if_applicable if using_default_scm? - warn_add_git_to_capfile unless scm_plugin_loaded? + warn_add_git_to_capfile unless scm_plugin_installed? elsif built_in_scm_name? warn_set_scm_is_deprecated elsif third_party_scm_name? diff --git a/lib/capistrano/dsl/env.rb b/lib/capistrano/dsl/env.rb index 422aebbb..c5c0dc5b 100644 --- a/lib/capistrano/dsl/env.rb +++ b/lib/capistrano/dsl/env.rb @@ -8,7 +8,7 @@ module Capistrano :configure_backend, :fetch, :set, :set_if_empty, :delete, :ask, :role, :server, :primary, :validate, :append, :remove, :dry_run?, :install_plugin, :any?, :is_question?, - :configure_scm + :configure_scm, :scm_plugin_installed? def roles(*names) env.roles_for(names.flatten) diff --git a/lib/capistrano/scm/git.rb b/lib/capistrano/scm/git.rb index e79f447b..43e8ded1 100644 --- a/lib/capistrano/scm/git.rb +++ b/lib/capistrano/scm/git.rb @@ -1,7 +1,6 @@ -require "capistrano/plugin" -require "capistrano/scm" +require "capistrano/scm/plugin" -class Capistrano::SCM::Git < Capistrano::Plugin +class Capistrano::SCM::Git < Capistrano::SCM::Plugin def set_defaults set_if_empty :git_shallow_clone, false set_if_empty :git_wrapper_path, lambda { diff --git a/lib/capistrano/scm/hg.rb b/lib/capistrano/scm/hg.rb index 7d10210e..3754e427 100644 --- a/lib/capistrano/scm/hg.rb +++ b/lib/capistrano/scm/hg.rb @@ -1,7 +1,6 @@ -require "capistrano/plugin" -require "capistrano/scm" +require "capistrano/scm/plugin" -class Capistrano::SCM::Hg < Capistrano::Plugin +class Capistrano::SCM::Hg < Capistrano::SCM::Plugin def register_hooks after "deploy:new_release_path", "hg:create_release" before "deploy:check", "hg:check" diff --git a/lib/capistrano/scm/plugin.rb b/lib/capistrano/scm/plugin.rb new file mode 100644 index 00000000..9b862797 --- /dev/null +++ b/lib/capistrano/scm/plugin.rb @@ -0,0 +1,13 @@ +require "capistrano/plugin" +require "capistrano/scm" + +# Base class for all built-in and third-party SCM plugins. Notice that this +# class doesn't really do anything other than provide an `scm?` predicate. This +# tells Capistrano that the plugin provides SCM functionality. All other plugin +# features are inherited from Capistrano::Plugin. +# +class Capistrano::SCM::Plugin < Capistrano::Plugin + def scm? + true + end +end diff --git a/lib/capistrano/scm/svn.rb b/lib/capistrano/scm/svn.rb index 0b9f4e1e..269b6a8e 100644 --- a/lib/capistrano/scm/svn.rb +++ b/lib/capistrano/scm/svn.rb @@ -1,7 +1,6 @@ -require "capistrano/plugin" -require "capistrano/scm" +require "capistrano/scm/plugin" -class Capistrano::SCM::Svn < Capistrano::Plugin +class Capistrano::SCM::Svn < Capistrano::SCM::Plugin def register_hooks after "deploy:new_release_path", "svn:create_release" before "deploy:check", "svn:check"