2005-10-28 17:21:07 -04:00
# Rails Plugin Manager.
#
# Installing plugins:
#
2010-02-06 11:18:10 -05:00
# $ rails plugin install continuous_builder asset_timestamping
2005-10-28 17:21:07 -04:00
#
2008-07-13 22:24:16 -04:00
# Specifying revisions:
#
# * Subversion revision is a single integer.
#
# * Git revision format:
# - full - 'refs/tags/1.8.0' or 'refs/heads/experimental'
# - short: 'experimental' (equivalent to 'refs/heads/experimental')
# 'tag 1.8.0' (equivalent to 'refs/tags/1.8.0')
#
#
2005-10-30 07:58:41 -05:00
# This is Free Software, copyright 2005 by Ryan Tomayko (rtomayko@gmail.com)
# and is licensed MIT: (http://www.opensource.org/licenses/mit-license.php)
2005-10-28 17:21:07 -04:00
$verbose = false
2005-10-29 19:44:25 -04:00
2005-10-28 17:21:07 -04:00
2005-11-05 20:39:32 -05:00
require 'open-uri'
2005-10-28 17:21:07 -04:00
require 'fileutils'
require 'tempfile'
include FileUtils
class RailsEnvironment
attr_reader :root
def initialize ( dir )
@root = dir
end
2005-11-23 17:24:16 -05:00
2005-10-28 17:21:07 -04:00
def self . find ( dir = nil )
dir || = pwd
while dir . length > 1
return new ( dir ) if File . exist? ( File . join ( dir , 'config' , 'environment.rb' ) )
dir = File . dirname ( dir )
end
end
def self . default
@default || = find
end
def self . default = ( rails_env )
@default = rails_env
end
def install ( name_uri_or_plugin )
if name_uri_or_plugin . is_a? String
if name_uri_or_plugin =~ / : \/ \/ /
plugin = Plugin . new ( name_uri_or_plugin )
else
plugin = Plugins [ name_uri_or_plugin ]
end
else
plugin = name_uri_or_plugin
end
unless plugin . nil?
plugin . install
else
2007-09-30 18:53:27 -04:00
puts " Plugin not found: #{ name_uri_or_plugin } "
2005-10-28 17:21:07 -04:00
end
end
2005-11-05 20:39:32 -05:00
def use_svn?
2005-11-13 18:35:19 -05:00
require 'active_support/core_ext/kernel'
silence_stderr { ` svn --version ` rescue nil }
2005-11-07 04:46:40 -05:00
! $? . nil? && $? . success?
2005-11-05 20:39:32 -05:00
end
2005-10-28 17:21:07 -04:00
def use_externals?
2005-11-13 18:35:19 -05:00
use_svn? && File . directory? ( " #{ root } /vendor/plugins/.svn " )
2005-10-28 17:21:07 -04:00
end
2005-11-23 17:24:16 -05:00
2005-10-28 17:21:07 -04:00
def use_checkout?
# this is a bit of a guess. we assume that if the rails environment
2006-04-06 11:31:41 -04:00
# is under subversion then they probably want the plugin checked out
2005-10-28 17:21:07 -04:00
# instead of exported. This can be overridden on the command line
File . directory? ( " #{ root } /.svn " )
end
2005-11-23 17:24:16 -05:00
2005-10-28 17:21:07 -04:00
def best_install_method
2005-11-05 20:39:32 -05:00
return :http unless use_svn?
2005-10-28 17:21:07 -04:00
case
when use_externals? then :externals
when use_checkout? then :checkout
else :export
end
end
def externals
return [ ] unless use_externals?
2005-11-23 17:24:16 -05:00
ext = ` svn propget svn:externals " #{ root } /vendor/plugins" `
2009-03-05 21:30:26 -05:00
lines = ext . respond_to? ( :lines ) ? ext . lines : ext
lines . reject { | line | line . strip == '' } . map do | line |
2005-10-28 17:21:07 -04:00
line . strip . split ( / \ s+ / , 2 )
end
end
2005-11-23 17:24:16 -05:00
2005-10-28 17:21:07 -04:00
def externals = ( items )
unless items . is_a? String
items = items . map { | name , uri | " #{ name . ljust ( 29 ) } #{ uri . chomp ( '/' ) } " } . join ( " \n " )
end
Tempfile . open ( " svn-set-prop " ) do | file |
file . write ( items )
file . flush
2006-06-11 13:17:32 -04:00
system ( " svn propset -q svn:externals -F \" #{ file . path } \" \" #{ root } /vendor/plugins \" " )
2005-10-28 17:21:07 -04:00
end
end
end
class Plugin
attr_reader :name , :uri
2009-03-18 14:33:44 -04:00
def initialize ( uri , name = nil )
2005-10-28 17:21:07 -04:00
@uri = uri
guess_name ( uri )
end
2006-05-23 02:34:25 -04:00
def self . find ( name )
2009-03-18 14:33:44 -04:00
new ( name )
2006-05-23 02:34:25 -04:00
end
2005-10-28 17:21:07 -04:00
def to_s
" #{ @name . ljust ( 30 ) } #{ @uri } "
end
2006-05-23 02:34:25 -04:00
def svn_url?
2006-05-31 14:06:50 -04:00
@uri =~ / svn(?: \ +ssh)?: \/ \/ * /
2006-05-23 02:34:25 -04:00
end
2008-03-17 17:44:16 -04:00
def git_url?
2008-05-29 13:40:30 -04:00
@uri =~ / ^git: \/ \/ / || @uri =~ / \ .git$ /
2008-03-17 17:44:16 -04:00
end
2005-10-28 17:21:07 -04:00
def installed?
File . directory? ( " #{ rails_env . root } /vendor/plugins/ #{ name } " ) \
or rails_env . externals . detect { | name , repo | self . uri == repo }
end
2005-11-13 18:35:19 -05:00
def install ( method = nil , options = { } )
2005-10-28 17:21:07 -04:00
method || = rails_env . best_install_method?
2008-03-17 17:44:16 -04:00
if :http == method
method = :export if svn_url?
2008-07-13 22:24:16 -04:00
method = :git if git_url?
2008-03-17 17:44:16 -04:00
end
2005-11-13 18:35:19 -05:00
uninstall if installed? and options [ :force ]
2005-10-28 17:21:07 -04:00
unless installed?
2005-11-13 18:35:19 -05:00
send ( " install_using_ #{ method } " , options )
2005-12-03 01:53:14 -05:00
run_install_hook
2005-10-28 17:21:07 -04:00
else
2005-11-13 18:35:19 -05:00
puts " already installed: #{ name } ( #{ uri } ). pass --force to reinstall "
2005-10-28 17:21:07 -04:00
end
end
2005-11-13 18:35:19 -05:00
def uninstall
path = " #{ rails_env . root } /vendor/plugins/ #{ name } "
if File . directory? ( path )
2006-02-25 19:37:11 -05:00
puts " Removing 'vendor/plugins/ #{ name } ' " if $verbose
2006-06-03 18:26:44 -04:00
run_uninstall_hook
2005-11-13 18:35:19 -05:00
rm_r path
else
puts " Plugin doesn't exist: #{ path } "
end
2009-03-18 14:39:31 -04:00
if rails_env . use_externals?
# clean up svn:externals
externals = rails_env . externals
externals . reject! { | n , u | name == n or name == u }
rails_env . externals = externals
end
2005-11-13 18:35:19 -05:00
end
2006-05-23 02:34:25 -04:00
def info
2006-07-17 15:17:50 -04:00
tmp = " #{ rails_env . root } /_tmp_about.yml "
2006-05-23 02:34:25 -04:00
if svn_url?
cmd = " svn export #{ @uri } \" #{ rails_env . root } / #{ tmp } \" "
puts cmd if $verbose
system ( cmd )
end
2006-07-17 15:17:50 -04:00
open ( svn_url? ? tmp : File . join ( @uri , 'about.yml' ) ) do | stream |
2006-05-23 02:34:25 -04:00
stream . read
2006-07-17 15:17:50 -04:00
end rescue " No about.yml found in #{ uri } "
2006-05-23 02:34:25 -04:00
ensure
FileUtils . rm_rf tmp if svn_url?
end
2005-10-28 17:21:07 -04:00
private
2005-12-03 01:53:14 -05:00
def run_install_hook
install_hook_file = " #{ rails_env . root } /vendor/plugins/ #{ name } /install.rb "
2007-12-10 00:54:46 -05:00
load install_hook_file if File . exist? install_hook_file
2005-12-03 01:53:14 -05:00
end
2006-06-03 18:26:44 -04:00
def run_uninstall_hook
uninstall_hook_file = " #{ rails_env . root } /vendor/plugins/ #{ name } /uninstall.rb "
2007-12-10 00:54:46 -05:00
load uninstall_hook_file if File . exist? uninstall_hook_file
2006-06-03 18:26:44 -04:00
end
2005-11-13 18:35:19 -05:00
def install_using_export ( options = { } )
svn_command :export , options
2005-10-28 17:21:07 -04:00
end
2005-11-13 18:35:19 -05:00
def install_using_checkout ( options = { } )
svn_command :checkout , options
2005-10-28 17:21:07 -04:00
end
2005-11-13 18:35:19 -05:00
def install_using_externals ( options = { } )
2005-10-28 17:21:07 -04:00
externals = rails_env . externals
externals . push ( [ @name , uri ] )
rails_env . externals = externals
2005-11-13 18:35:19 -05:00
install_using_checkout ( options )
2005-10-28 17:21:07 -04:00
end
2005-11-05 20:39:32 -05:00
2005-11-13 18:35:19 -05:00
def install_using_http ( options = { } )
2005-11-05 20:39:32 -05:00
root = rails_env . root
2007-09-30 18:53:27 -04:00
mkdir_p " #{ root } /vendor/plugins/ #{ @name } "
Dir . chdir " #{ root } /vendor/plugins/ #{ @name } " do
2007-02-26 22:15:44 -05:00
puts " fetching from ' #{ uri } ' " if $verbose
2007-09-30 18:53:27 -04:00
fetcher = RecursiveHTTPFetcher . new ( uri , - 1 )
2007-02-26 22:15:44 -05:00
fetcher . quiet = true if options [ :quiet ]
fetcher . fetch
end
2005-11-13 18:35:19 -05:00
end
2008-03-17 17:44:16 -04:00
2008-07-13 22:24:16 -04:00
def install_using_git ( options = { } )
root = rails_env . root
2009-02-19 11:27:43 -05:00
mkdir_p ( install_path = " #{ root } /vendor/plugins/ #{ name } " )
2008-07-13 22:24:16 -04:00
Dir . chdir install_path do
init_cmd = " git init "
init_cmd += " -q " if options [ :quiet ] and not $verbose
puts init_cmd if $verbose
system ( init_cmd )
base_cmd = " git pull --depth 1 #{ uri } "
base_cmd += " -q " if options [ :quiet ] and not $verbose
base_cmd += " #{ options [ :revision ] } " if options [ :revision ]
puts base_cmd if $verbose
if system ( base_cmd )
2009-03-06 12:22:52 -05:00
puts " removing: .git .gitignore " if $verbose
rm_rf %w( .git .gitignore )
2008-07-13 22:24:16 -04:00
else
rm_rf install_path
end
end
2008-03-17 17:44:16 -04:00
end
2005-11-13 18:35:19 -05:00
def svn_command ( cmd , options = { } )
root = rails_env . root
mkdir_p " #{ root } /vendor/plugins "
2005-11-23 17:24:16 -05:00
base_cmd = " svn #{ cmd } #{ uri } \" #{ root } /vendor/plugins/ #{ name } \" "
2005-11-13 18:35:19 -05:00
base_cmd += ' -q' if options [ :quiet ] and not $verbose
base_cmd += " -r #{ options [ :revision ] } " if options [ :revision ]
2006-02-25 19:37:11 -05:00
puts base_cmd if $verbose
2005-11-13 18:35:19 -05:00
system ( base_cmd )
2005-11-05 20:39:32 -05:00
end
2005-10-28 17:21:07 -04:00
def guess_name ( url )
@name = File . basename ( url )
if @name == 'trunk' || @name . empty?
@name = File . basename ( File . dirname ( url ) )
end
2008-03-17 17:44:16 -04:00
@name . gsub! ( / \ .git$ / , '' ) if @name =~ / \ .git$ /
2005-10-28 17:21:07 -04:00
end
def rails_env
@rails_env || RailsEnvironment . default
end
end
# load default environment and parse arguments
require 'optparse'
module Commands
class Plugin
attr_reader :environment , :script_name , :sources
def initialize
@environment = RailsEnvironment . default
@rails_root = RailsEnvironment . default . root
@script_name = File . basename ( $0 )
@sources = [ ]
end
def environment = ( value )
@environment = value
RailsEnvironment . default = value
end
def options
OptionParser . new do | o |
o . set_summary_indent ( ' ' )
2010-02-02 19:16:26 -05:00
o . banner = " Usage: plugin [OPTIONS] command "
2005-10-28 17:21:07 -04:00
o . define_head " Rails plugin manager. "
o . separator " "
o . separator " GENERAL OPTIONS "
o . on ( " -r " , " --root=DIR " , String ,
" Set an explicit rails app directory. " ,
2008-09-09 03:26:22 -04:00
" Default: #{ @rails_root } " ) { | rails_root | @rails_root = rails_root ; self . environment = RailsEnvironment . new ( @rails_root ) }
2005-10-28 17:21:07 -04:00
o . on ( " -s " , " --source=URL1,URL2 " , Array ,
2008-09-09 03:26:22 -04:00
" Use the specified plugin repositories instead of the defaults. " ) { | sources | @sources = sources }
2005-10-28 17:21:07 -04:00
2008-09-09 03:26:22 -04:00
o . on ( " -v " , " --verbose " , " Turn on verbose output. " ) { | verbose | $verbose = verbose }
2005-10-28 17:21:07 -04:00
o . on ( " -h " , " --help " , " Show this help message. " ) { puts o ; exit }
o . separator " "
o . separator " COMMANDS "
o . separator " install Install plugin(s) from known repositories or URLs. "
o . separator " remove Uninstall plugins. "
o . separator " "
o . separator " EXAMPLES "
o . separator " Install a plugin: "
2010-02-06 11:18:10 -05:00
o . separator " #{ @script_name } plugin install continuous_builder \n "
2005-10-28 17:21:07 -04:00
o . separator " Install a plugin from a subversion URL: "
2010-02-06 11:18:10 -05:00
o . separator " #{ @script_name } plugin install http://dev.rubyonrails.com/svn/rails/plugins/continuous_builder \n "
2008-03-17 17:44:16 -04:00
o . separator " Install a plugin from a git URL: "
2010-02-06 11:18:10 -05:00
o . separator " #{ @script_name } plugin install git://github.com/SomeGuy/my_awesome_plugin.git \n "
2005-11-06 02:23:58 -05:00
o . separator " Install a plugin and add a svn:externals entry to vendor/plugins "
2010-02-06 11:18:10 -05:00
o . separator " #{ @script_name } plugin install -x continuous_builder \n "
2005-10-28 17:21:07 -04:00
end
end
def parse! ( args = ARGV )
general , sub = split_args ( args )
options . parse! ( general )
command = general . shift
2009-03-18 14:33:44 -04:00
if command =~ / ^(install|remove)$ /
2005-10-28 17:21:07 -04:00
command = Commands . const_get ( command . capitalize ) . new ( self )
command . parse! ( sub )
else
puts " Unknown command: #{ command } "
2006-01-14 04:00:48 -05:00
puts options
2005-10-28 17:21:07 -04:00
exit 1
end
end
def split_args ( args )
left = [ ]
left << args . shift while args [ 0 ] and args [ 0 ] =~ / ^- /
left << args . shift if args [ 0 ]
return [ left , args ]
end
def self . parse! ( args = ARGV )
Plugin . new . parse! ( args )
end
end
class Install
def initialize ( base_command )
@base_command = base_command
2005-11-13 18:35:19 -05:00
@method = :http
@options = { :quiet = > false , :revision = > nil , :force = > false }
2005-10-28 17:21:07 -04:00
end
def options
OptionParser . new do | o |
o . set_summary_indent ( ' ' )
o . banner = " Usage: #{ @base_command . script_name } install PLUGIN [PLUGIN [PLUGIN] ...] "
o . define_head " Install one or more plugins. "
o . separator " "
o . separator " Options: "
2005-11-06 02:23:58 -05:00
o . on ( " -x " , " --externals " ,
" Use svn:externals to grab the plugin. " ,
" Enables plugin updates and plugin versioning. " ) { | v | @method = :externals }
2005-10-28 17:21:07 -04:00
o . on ( " -o " , " --checkout " ,
2005-11-06 02:23:58 -05:00
" Use svn checkout to grab the plugin. " ,
" Enables updating but does not add a svn:externals entry. " ) { | v | @method = :checkout }
2008-02-21 17:44:00 -05:00
o . on ( " -e " , " --export " ,
" Use svn export to grab the plugin. " ,
" Exports the plugin, allowing you to check it into your local repository. Does not enable updates, or add an svn:externals entry. " ) { | v | @method = :export }
2005-11-13 18:35:19 -05:00
o . on ( " -q " , " --quiet " ,
" Suppresses the output from installation. " ,
2010-02-06 11:18:10 -05:00
" Ignored if -v is passed (rails plugin -v install ...) " ) { | v | @options [ :quiet ] = true }
2005-11-13 18:35:19 -05:00
o . on ( " -r REVISION " , " --revision REVISION " ,
2008-07-13 22:24:16 -04:00
" Checks out the given revision from subversion or git. " ,
" Ignored if subversion/git is not used. " ) { | v | @options [ :revision ] = v }
2005-11-13 18:35:19 -05:00
o . on ( " -f " , " --force " ,
" Reinstalls a plugin if it's already installed. " ) { | v | @options [ :force ] = true }
2005-10-28 17:21:07 -04:00
o . separator " "
o . separator " You can specify plugin names as given in 'plugin list' output or absolute URLs to "
o . separator " a plugin repository. "
end
end
def determine_install_method
best = @base_command . environment . best_install_method
2005-11-06 02:23:58 -05:00
@method = :http if best == :http and @method == :export
case
when ( best == :http and @method != :http )
msg = " Cannot install using subversion because `svn' cannot be found in your PATH "
2005-11-13 18:35:19 -05:00
when ( best == :export and ( @method != :export and @method != :http ) )
msg = " Cannot install using #{ @method } because this project is not under subversion. "
2005-11-06 02:23:58 -05:00
when ( best != :externals and @method == :externals )
msg = " Cannot install using externals because vendor/plugins is not under subversion. "
2005-10-28 17:21:07 -04:00
end
2005-11-06 02:23:58 -05:00
if msg
puts msg
2005-10-28 17:21:07 -04:00
exit 1
end
2005-11-06 02:23:58 -05:00
@method
2005-10-28 17:21:07 -04:00
end
def parse! ( args )
options . parse! ( args )
environment = @base_command . environment
install_method = determine_install_method
puts " Plugins will be installed using #{ install_method } " if $verbose
2006-05-23 02:34:25 -04:00
args . each do | name |
:: Plugin . find ( name ) . install ( install_method , @options )
2005-10-28 17:21:07 -04:00
end
2007-09-30 18:53:27 -04:00
rescue StandardError = > e
2006-06-08 22:08:26 -04:00
puts " Plugin not found: #{ args . inspect } "
2007-09-30 18:53:27 -04:00
puts e . inspect if $verbose
2006-05-23 02:34:25 -04:00
exit 1
2005-10-28 17:21:07 -04:00
end
end
2005-11-13 18:35:19 -05:00
class Remove
2005-10-28 17:21:07 -04:00
def initialize ( base_command )
@base_command = base_command
end
def options
OptionParser . new do | o |
o . set_summary_indent ( ' ' )
2005-11-13 18:35:19 -05:00
o . banner = " Usage: #{ @base_command . script_name } remove name [name]... "
o . define_head " Remove plugins. "
2005-10-28 17:21:07 -04:00
end
end
def parse! ( args )
options . parse! ( args )
root = @base_command . environment . root
args . each do | name |
2005-11-13 18:35:19 -05:00
:: Plugin . new ( name ) . uninstall
2005-10-28 17:21:07 -04:00
end
end
end
2006-05-23 02:34:25 -04:00
class Info
def initialize ( base_command )
@base_command = base_command
end
def options
OptionParser . new do | o |
o . set_summary_indent ( ' ' )
o . banner = " Usage: #{ @base_command . script_name } info name [name]... "
2006-07-17 15:17:50 -04:00
o . define_head " Shows plugin info at {url}/about.yml. "
2006-05-23 02:34:25 -04:00
end
end
def parse! ( args )
options . parse! ( args )
args . each do | name |
puts :: Plugin . find ( name ) . info
puts
end
end
end
2005-10-28 17:21:07 -04:00
end
2005-11-05 20:39:32 -05:00
class RecursiveHTTPFetcher
2005-11-13 18:35:19 -05:00
attr_accessor :quiet
2007-09-30 18:53:27 -04:00
def initialize ( urls_to_fetch , level = 1 , cwd = " . " )
@level = level
2005-11-05 20:39:32 -05:00
@cwd = cwd
2008-09-09 04:24:23 -04:00
@urls_to_fetch = RUBY_VERSION > = '1.9' ? urls_to_fetch . lines : urls_to_fetch . to_a
2005-11-13 18:35:19 -05:00
@quiet = false
end
def ls
@urls_to_fetch . collect do | url |
2008-07-19 19:04:35 -04:00
if url =~ / ^svn( \ +ssh)?: \/ \/ .* /
2006-04-06 11:31:41 -04:00
` svn ls #{ url } ` . split ( " \n " ) . map { | entry | " / #{ entry } " } rescue nil
else
open ( url ) do | stream |
links ( " " , stream . read )
end rescue nil
end
2005-11-13 18:35:19 -05:00
end . flatten
2005-11-05 20:39:32 -05:00
end
def push_d ( dir )
@cwd = File . join ( @cwd , dir )
FileUtils . mkdir_p ( @cwd )
end
def pop_d
@cwd = File . dirname ( @cwd )
end
def links ( base_url , contents )
links = [ ]
contents . scan ( / href \ s*= \ s* \ "*[^ \ ">]* /i ) do | link |
link = link . sub ( / href=" /i , " " )
2007-09-22 19:20:48 -04:00
next if link =~ / svnindex.xsl$ /
2007-11-20 20:53:31 -05:00
next if link =~ / ^( \ w*:|) \/ \/ / || link =~ / ^ \ . /
2005-11-05 20:39:32 -05:00
links << File . join ( base_url , link )
end
links
end
def download ( link )
2005-11-13 18:35:19 -05:00
puts " + #{ File . join ( @cwd , File . basename ( link ) ) } " unless @quiet
2005-11-05 20:39:32 -05:00
open ( link ) do | stream |
File . open ( File . join ( @cwd , File . basename ( link ) ) , " wb " ) do | file |
file . write ( stream . read )
end
end
end
def fetch ( links = @urls_to_fetch )
links . each do | l |
( l =~ / \/ $ / || links == @urls_to_fetch ) ? fetch_dir ( l ) : download ( l )
end
end
def fetch_dir ( url )
2007-09-30 18:53:27 -04:00
@level += 1
push_d ( File . basename ( url ) ) if @level > 0
2005-11-05 20:39:32 -05:00
open ( url ) do | stream |
contents = stream . read
fetch ( links ( url , contents ) )
end
2007-09-30 18:53:27 -04:00
pop_d if @level > 0
@level -= 1
2005-11-05 20:39:32 -05:00
end
end
2005-10-28 17:21:07 -04:00
2005-10-29 19:44:25 -04:00
Commands :: Plugin . parse!