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

Add a Plugin API

The Plugin class is a simple set of conventions for defining Capistrano plugins.
This is to support the eventual migration of SCM logic out of Capistrano itself
and into separate gems as plugins.
This commit is contained in:
Matt Brictson 2016-01-17 16:46:53 -08:00
parent 3b9c90327b
commit dc59c248a7
3 changed files with 194 additions and 0 deletions

106
lib/capistrano/plugin.rb Normal file
View file

@ -0,0 +1,106 @@
require "capistrano/all"
require "capistrano/ext/sshkit/backend/thread_local"
require "rake/tasklib"
# Base class for Capistrano plugins. Makes building a Capistrano plugin as easy
# as writing a `Capistrano::Plugin` subclass and overriding any or all of its
# three template methods:
#
# * set_defaults
# * register_hooks
# * define_tasks
#
# Within the plugin you can use any methods of the Rake or Capistrano DSLs, like
# `fetch`, `invoke`, etc. In cases when you need to use SSHKit's backend outside
# of an `on` block, use the `backend` convenience method. E.g. `backend.test`,
# `backend.execute`, or `backend.capture`.
#
# Package up and distribute your plugin class as a gem and you're good to go!
#
# To use a plugin, all a user has to do is instantiate it in the Capfile, like
# this:
#
# # Capfile
# require "capistrano/superfancy"
# Capistrano::Superfancy.new
#
# Or, to install the plugin without its hooks:
#
# # Capfile
# require "capistrano/superfancy"
# Capistrano::Superfancy.new(hooks: false)
#
class Capistrano::Plugin < Rake::TaskLib
include Capistrano::DSL
# Constructing a plugin "installs" it into Capistrano by loading its tasks,
# hooks, and defaults at the appropriate time. The hooks in particular can be
# skipped, if you want full control over when and how the plugin's tasks are
# executed. Simply pass `hooks:false` to opt out.
#
def initialize(hooks:true)
define_tasks
register_hooks if hooks
task "load:defaults" do
set_defaults
end
end
private
# Implemented by subclasses to provide default values for settings needed by
# this plugin. Typically done using the `set_if_empty` Capistrano DSL method.
#
# Example:
#
# def set_defaults
# set_if_empty :my_plugin_option, true
# end
#
def set_defaults; end
# Implemented by subclasses to hook into Capistrano's deployment flow using
# using the `before` and `after` DSL methods. Note that `register_hooks` will
# not be called if the user has opted-out of hooks when installing the plugin.
#
# Example:
#
# def register_hooks
# after "deploy:updated", "my_plugin:do_something"
# end
#
def register_hooks; end
# Implemented by subclasses to define Rake tasks. Typically a plugin will call
# `eval_rakefile` to load Rake tasks from a separate .rake file.
#
# Example:
#
# def define_tasks
# eval_rakefile File.expand_path("../tasks.rake", __FILE__)
# end
#
# For simple tasks, you can define them inline. No need for a separate file.
#
# def define_tasks
# desc "Do something fantastic."
# task "my_plugin:fantastic" do
# ...
# end
# end
#
def define_tasks; end
# Read and eval a .rake file in such a way that `self` within the .rake file
# refers to this plugin instance. This gives the tasks in the file access to
# helper methods defined by the plugin.
def eval_rakefile(path)
contents = IO.read(path)
instance_eval(contents, path, 1)
end
# Convenience to access the current SSHKit backend outside of an `on` block.
def backend
SSHKit::Backend.current
end
end

View file

@ -0,0 +1,82 @@
require "spec_helper"
require "capistrano/plugin"
module Capistrano
describe Plugin do
include Rake::DSL
include Capistrano::DSL
class DummyPlugin < Capistrano::Plugin
def define_tasks
task :hello do
end
end
def register_hooks
before "deploy:published", "hello"
end
end
class ExternalTasksPlugin < Capistrano::Plugin
def define_tasks
eval_rakefile(
File.expand_path("../../../support/tasks/plugin.rake", __FILE__)
)
end
# Called from plugin.rake to demonstrate that helper methods work
def hello
set :plugin_result, "hello"
end
end
before do
# Define an example task to allow testing hooks
task "deploy:published"
end
after do
# Clean up any tasks or variables we created during the tests
Rake::Task.clear
Capistrano::Configuration.reset!
end
it "defines tasks when constructed" do
DummyPlugin.new
expect(Rake::Task["hello"]).not_to be_nil
end
it "registers hooks when constructed" do
DummyPlugin.new
expect(Rake::Task["deploy:published"].prerequisites).to include("hello")
end
it "skips registering hooks if :hooks => false" do
DummyPlugin.new(:hooks => false)
expect(Rake::Task["deploy:published"].prerequisites).to be_empty
end
it "doesn't call set_defaults immediately" do
dummy = DummyPlugin.new
dummy.expects(:set_defaults).never
end
it "calls set_defaults during load:defaults" do
dummy = DummyPlugin.new
dummy.expects(:set_defaults).once
Rake::Task["load:defaults"].invoke
end
it "is able to load tasks from a .rake file" do
ExternalTasksPlugin.new
Rake::Task["plugin_test"].invoke
expect(fetch(:plugin_result)).to eq("hello")
end
it "exposes the SSHKit backend to subclasses" do
SSHKit::Backend.expects(:current).returns(:backend)
plugin = DummyPlugin.new
expect(plugin.send(:backend)).to eq(:backend)
end
end
end

View file

@ -0,0 +1,6 @@
# This rake file is used by plugin_spec.rb.
task :plugin_test do
# Example of invoking a helper method provided by the plugin
hello
end