crafterm-sprinkle 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/CREDITS +11 -0
  2. data/History.txt +4 -0
  3. data/MIT-LICENSE +20 -0
  4. data/Manifest.txt +55 -0
  5. data/README.txt +218 -0
  6. data/Rakefile +4 -0
  7. data/bin/sprinkle +79 -0
  8. data/config/hoe.rb +70 -0
  9. data/config/requirements.rb +17 -0
  10. data/examples/merb/deploy.rb +5 -0
  11. data/examples/rails/README +15 -0
  12. data/examples/rails/deploy.rb +2 -0
  13. data/examples/rails/packages/database.rb +9 -0
  14. data/examples/rails/packages/essential.rb +6 -0
  15. data/examples/rails/packages/rails.rb +28 -0
  16. data/examples/rails/packages/search.rb +11 -0
  17. data/examples/rails/packages/server.rb +28 -0
  18. data/examples/rails/rails.rb +71 -0
  19. data/lib/sprinkle/actors/capistrano.rb +80 -0
  20. data/lib/sprinkle/deployment.rb +33 -0
  21. data/lib/sprinkle/extensions/arbitrary_options.rb +10 -0
  22. data/lib/sprinkle/extensions/array.rb +7 -0
  23. data/lib/sprinkle/extensions/blank_slate.rb +5 -0
  24. data/lib/sprinkle/extensions/dsl_accessor.rb +15 -0
  25. data/lib/sprinkle/extensions/string.rb +10 -0
  26. data/lib/sprinkle/extensions/symbol.rb +7 -0
  27. data/lib/sprinkle/installers/apt.rb +20 -0
  28. data/lib/sprinkle/installers/gem.rb +31 -0
  29. data/lib/sprinkle/installers/installer.rb +47 -0
  30. data/lib/sprinkle/installers/rake.rb +16 -0
  31. data/lib/sprinkle/installers/source.rb +137 -0
  32. data/lib/sprinkle/package.rb +76 -0
  33. data/lib/sprinkle/policy.rb +84 -0
  34. data/lib/sprinkle/script.rb +13 -0
  35. data/lib/sprinkle/version.rb +9 -0
  36. data/lib/sprinkle.rb +29 -0
  37. data/script/destroy +14 -0
  38. data/script/generate +14 -0
  39. data/spec/spec.opts +1 -0
  40. data/spec/spec_helper.rb +17 -0
  41. data/spec/sprinkle/actors/capistrano_spec.rb +150 -0
  42. data/spec/sprinkle/deployment_spec.rb +80 -0
  43. data/spec/sprinkle/extensions/array_spec.rb +19 -0
  44. data/spec/sprinkle/extensions/string_spec.rb +21 -0
  45. data/spec/sprinkle/installers/apt_spec.rb +46 -0
  46. data/spec/sprinkle/installers/gem_spec.rb +64 -0
  47. data/spec/sprinkle/installers/installer_spec.rb +125 -0
  48. data/spec/sprinkle/installers/source_spec.rb +315 -0
  49. data/spec/sprinkle/package_spec.rb +203 -0
  50. data/spec/sprinkle/policy_spec.rb +110 -0
  51. data/spec/sprinkle/script_spec.rb +51 -0
  52. data/spec/sprinkle/sprinkle_spec.rb +25 -0
  53. data/tasks/deployment.rake +34 -0
  54. data/tasks/environment.rake +7 -0
  55. data/tasks/rspec.rake +21 -0
  56. metadata +137 -0
@@ -0,0 +1,80 @@
1
+ require 'capistrano/cli'
2
+
3
+ module Sprinkle
4
+ module Actors
5
+ class Capistrano
6
+ attr_accessor :config, :loaded_recipes
7
+
8
+ def initialize(&block)
9
+ @config = ::Capistrano::Configuration.new
10
+ @config.logger.level = Sprinkle::OPTIONS[:verbose] ? ::Capistrano::Logger::INFO : ::Capistrano::Logger::IMPORTANT
11
+ @config.set(:password) { ::Capistrano::CLI.password_prompt }
12
+ if block
13
+ self.instance_eval &block
14
+ else
15
+ @config.load 'deploy' # normally in the config directory for rails
16
+ end
17
+ end
18
+
19
+ def recipes(script)
20
+ @loaded_recipes ||= []
21
+ @config.load script
22
+ @loaded_recipes << script
23
+ end
24
+
25
+ def process(name, commands, roles)
26
+ define_task(name, roles) do
27
+ via = fetch(:run_method, :sudo)
28
+ commands.each do |command|
29
+ invoke_command command, :via => via
30
+ end
31
+ end
32
+ run(name)
33
+ end
34
+
35
+ private
36
+
37
+ # REVISIT: can we set the description somehow?
38
+ def define_task(name, roles, &block)
39
+ @config.task task_sym(name), :roles => roles, &block
40
+ end
41
+
42
+ def run(task)
43
+ @config.send task_sym(task)
44
+ end
45
+
46
+ def task_sym(name)
47
+ "install_#{name.to_task_name}".to_sym
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+
54
+ =begin
55
+
56
+ # channel: the SSH channel object used for this response
57
+ # stream: either :err or :out, for stderr or stdout responses
58
+ # output: the text that the server is sending, might be in chunks
59
+ run "apt-get update" do |channel, stream, output|
60
+ if output =~ /Are you sure?/
61
+ answer = Capistrano::CLI.ui.ask("Are you sure: ")
62
+ channel.send_data(answer + "\n")
63
+ else
64
+ # allow the default callback to be processed
65
+ Capistrano::Configuration.default_io_proc.call[channel, stream, output]
66
+ end
67
+ end
68
+
69
+
70
+
71
+ You can tell subversion to use a different username+password by
72
+ setting a couple variables:
73
+ set :svn_username, "my svn username"
74
+ set :svn_password, "my svn password"
75
+ If you don't want to set the password explicitly in your recipe like
76
+ that, you can make capistrano prompt you for it like this:
77
+ set(:svn_password) { Capistrano::CLI.password_prompt("Subversion
78
+ password: ") }
79
+ - Jamis
80
+ =end
@@ -0,0 +1,33 @@
1
+ module Sprinkle
2
+ module Deployment
3
+ def deployment(&block)
4
+ @deployment = Deployment.new(&block)
5
+ end
6
+
7
+ class Deployment
8
+ attr_accessor :style, :defaults
9
+
10
+ def initialize(&block)
11
+ @defaults = {}
12
+ self.instance_eval(&block)
13
+ raise 'No delivery mechanism defined' unless @style
14
+ end
15
+
16
+ def delivery(type, &block)
17
+ @style = Actors.const_get(type.to_s.titleize).new &block
18
+ end
19
+
20
+ def method_missing(sym, *args, &block)
21
+ @defaults[sym] = block
22
+ end
23
+
24
+ def respond_to?(sym); !!@defaults[sym]; end
25
+
26
+ def process
27
+ POLICIES.each do |policy|
28
+ policy.process(self)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,10 @@
1
+ module ArbitraryOptions
2
+ def self.included(base)
3
+ base.alias_method_chain :method_missing, :arbitrary_options
4
+ end
5
+
6
+ def method_missing_with_arbitrary_options(sym, *args, &block)
7
+ self.class.dsl_accessor sym
8
+ send(sym, *args, &block)
9
+ end
10
+ end
@@ -0,0 +1,7 @@
1
+ class Array
2
+
3
+ def to_task_name
4
+ collect(&:to_task_name).join('_')
5
+ end
6
+
7
+ end
@@ -0,0 +1,5 @@
1
+ class BlankSlate
2
+ instance_methods.each do |m|
3
+ undef_method(m) unless %w( __send__ __id__ send class inspect instance_eval instance_variables ).include?(m)
4
+ end
5
+ end
@@ -0,0 +1,15 @@
1
+ class Module
2
+ def dsl_accessor(*symbols)
3
+ symbols.each do |sym|
4
+ class_eval %{
5
+ def #{sym}(*val)
6
+ if val.empty?
7
+ @#{sym}
8
+ else
9
+ @#{sym} = val.size == 1 ? val[0] : val
10
+ end
11
+ end
12
+ }
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,10 @@
1
+ class String
2
+
3
+ # REVISIT: what chars shall we allow in task names?
4
+ def to_task_name
5
+ s = downcase
6
+ s.gsub!(/-/, '_') # all - to _ chars
7
+ s
8
+ end
9
+
10
+ end
@@ -0,0 +1,7 @@
1
+ class Symbol
2
+
3
+ def to_task_name
4
+ to_s.to_task_name
5
+ end
6
+
7
+ end
@@ -0,0 +1,20 @@
1
+ module Sprinkle
2
+ module Installers
3
+ class Apt < Installer
4
+ attr_accessor :packages
5
+
6
+ def initialize(parent, packages, &block)
7
+ super parent, &block
8
+ packages = [packages] unless packages.is_a? Array
9
+ @packages = packages
10
+ end
11
+
12
+ protected
13
+
14
+ def install_sequence
15
+ "DEBCONF_TERSE='yes' DEBIAN_PRIORITY='critical' DEBIAN_FRONTEND=noninteractive apt-get -qyu install #{@packages.join(' ')}"
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,31 @@
1
+ module Sprinkle
2
+ module Installers
3
+ class Gem < Installer
4
+ attr_accessor :gem
5
+
6
+ def initialize(parent, gem, options = {}, &block)
7
+ super parent, options, &block
8
+ @gem = gem
9
+ end
10
+
11
+ def source(location = nil)
12
+ # package defines an installer called source so here we specify a method directly
13
+ # rather than rely on the automatic options processing since packages' method missing
14
+ # won't be run
15
+ return @options[:source] unless location
16
+ @options[:source] = location
17
+ end
18
+
19
+ protected
20
+
21
+ # rubygems 0.9.5+ installs dependencies by default, and does platform selection
22
+
23
+ def install_sequence
24
+ cmd = "gem install #{gem}"
25
+ cmd << " --version '#{version}'" if version
26
+ cmd << " --source #{source}" if source
27
+ cmd
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,47 @@
1
+ module Sprinkle
2
+ module Installers
3
+ class Installer
4
+ attr_accessor :delivery, :package, :options
5
+
6
+ def initialize(package, options = {}, &block)
7
+ @package = package
8
+ @options = options
9
+ self.instance_eval(&block) if block
10
+ end
11
+
12
+ def defaults(deployment)
13
+ defaults = deployment.defaults[self.class.name.split(/::/).last.downcase.to_sym]
14
+ self.instance_eval(&defaults) if defaults
15
+ @delivery = deployment.style
16
+ end
17
+
18
+ def process(roles)
19
+ raise 'Unknown command delivery target' unless @delivery
20
+
21
+ if logger.debug?
22
+ sequence = install_sequence; sequence = sequence.join('; ') if sequence.is_a? Array
23
+ logger.debug "#{@package.name} install sequence: #{sequence} for roles: #{roles}\n"
24
+ end
25
+
26
+ unless Sprinkle::OPTIONS[:testing]
27
+ logger.info "--> Installing #{package.name} for roles: #{roles}"
28
+ @delivery.process(@package.name, install_sequence, roles)
29
+ end
30
+ end
31
+
32
+ def method_missing(sym, *args, &block)
33
+ unless args.empty? # mutate if not set
34
+ @options[sym] = *args unless @options[sym]
35
+ end
36
+
37
+ @options[sym] || @package.send(sym, *args, &block) # try the parents options if unknown
38
+ end
39
+
40
+ protected
41
+
42
+ def install_sequence
43
+ raise 'Concrete installers implement this to specify commands to run to install their respective packages'
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,16 @@
1
+ module Sprinkle
2
+ module Installers
3
+ class Rake < Installer
4
+ def initialize(parent, commands = [], &block)
5
+ super parent, &block
6
+ @commands = commands
7
+ end
8
+
9
+ protected
10
+
11
+ def install_sequence
12
+ "rake #{@commands.join(' ')}"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,137 @@
1
+ module Sprinkle
2
+ module Installers
3
+ class Source < Installer
4
+ attr_accessor :source, :pre, :post
5
+
6
+ def initialize(parent, source, options = {}, &block)
7
+ @pre = {}; @post = {}
8
+ @source = source
9
+ super parent, options, &block
10
+ end
11
+
12
+ def pre(stage, *commands)
13
+ @pre[stage] ||= []
14
+ @pre[stage] += commands
15
+ end
16
+
17
+ def post(stage, *commands)
18
+ @post[stage] ||= []
19
+ @post[stage] += commands
20
+ end
21
+
22
+ protected
23
+
24
+ def install_sequence
25
+ prepare + download + extract + configure + build + install
26
+ end
27
+
28
+ %w( prepare download extract configure build install ).each do |stage|
29
+ define_method stage do
30
+ pre_commands(stage.to_sym) + self.send("#{stage}_commands") + post_commands(stage.to_sym)
31
+ end
32
+ end
33
+
34
+ def prepare_commands
35
+ raise 'No installation area defined' unless @options[:prefix]
36
+ raise 'No build area defined' unless @options[:builds]
37
+ raise 'No source download area defined' unless @options[:archives]
38
+
39
+ [ "mkdir -p #{@options[:prefix]}",
40
+ "mkdir -p #{@options[:builds]}",
41
+ "mkdir -p #{@options[:archives]}" ]
42
+ end
43
+
44
+ def download_commands
45
+ [ "wget -cq --directory-prefix='#{@options[:archives]}' #{@source}" ]
46
+ end
47
+
48
+ def extract_commands
49
+ [ "bash -c 'cd #{@options[:builds]} && #{extract_command} #{@options[:archives]}/#{archive_name}'" ]
50
+ end
51
+
52
+ def configure_commands
53
+ return [] if custom_install?
54
+
55
+ command = "bash -c 'cd #{build_dir} && ./configure --silent --prefix #{@options[:prefix]} "
56
+
57
+ extras = {
58
+ :enable => '--enable', :disable => '--disable',
59
+ :with => '--with', :without => '--without'
60
+ }
61
+
62
+ extras.inject(command) { |m, (k, v)| m << create_options(k, v) if options[k]; m }
63
+
64
+ [ command << " > #{@package.name}-configure.log 2>&1'" ]
65
+ end
66
+
67
+ def build_commands
68
+ return [] if custom_install?
69
+ [ "bash -c 'cd #{build_dir} && make > #{@package.name}-build.log 2>&1'" ]
70
+ end
71
+
72
+ def install_commands
73
+ return custom_install_commands if custom_install?
74
+ [ "bash -c 'cd #{build_dir} && make install > #{@package.name}-install.log 2>&1'" ]
75
+ end
76
+
77
+ def custom_install?
78
+ !! @options[:custom_install]
79
+ end
80
+
81
+ # REVISIT: must be better processing of custom install commands somehow? use splat operator?
82
+ def custom_install_commands
83
+ dress @options[:custom_install], :install
84
+ end
85
+
86
+ private
87
+
88
+ def pre_commands(stage)
89
+ dress @pre[stage] || [], :pre
90
+ end
91
+
92
+ def post_commands(stage)
93
+ dress @post[stage] || [], :post
94
+ end
95
+
96
+ def dress(commands, stage)
97
+ commands.collect { |command| "bash -c 'cd #{build_dir} && #{command} >> #{@package.name}-#{stage}.log 2>&1'" }
98
+ end
99
+
100
+ def create_options(key, prefix)
101
+ @options[key].inject(' ') { |m, option| m << "#{prefix}-#{option} "; m }
102
+ end
103
+
104
+ def extract_command
105
+ case @source
106
+ when /(tar.gz)|(tgz)$/
107
+ 'tar xzf'
108
+ when /(tar.bz2)|(tb2)$/
109
+ 'tar xjf'
110
+ when /tar$/
111
+ 'tar xf'
112
+ when /zip$/
113
+ 'unzip'
114
+ else
115
+ raise "Unknown source archive format: #{archive_name}"
116
+ end
117
+ end
118
+
119
+ def archive_name
120
+ name = @source.split('/').last
121
+ raise "Unable to determine archive name for source: #{source}, please update code knowledge" unless name
122
+ name
123
+ end
124
+
125
+ def build_dir
126
+ "#{@options[:builds]}/#{base_dir}"
127
+ end
128
+
129
+ def base_dir
130
+ if @source.split('/').last =~ /(.*)\.(tar\.gz|tgz|tar\.bz2|tb2)/
131
+ return $1
132
+ end
133
+ raise "Unknown base path for source archive: #{@source}, please update code knowledge"
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,76 @@
1
+ module Sprinkle
2
+ module Package
3
+ PACKAGES = {}
4
+
5
+ def package(name, metadata = {}, &block)
6
+ package = Package.new(name, metadata, &block)
7
+ PACKAGES[name] = package
8
+
9
+ if package.provides
10
+ (PACKAGES[package.provides] ||= []) << package
11
+ end
12
+
13
+ package
14
+ end
15
+
16
+ class Package
17
+ include ArbitraryOptions
18
+ attr_accessor :name, :provides, :installer, :dependencies
19
+
20
+ def initialize(name, metadata = {}, &block)
21
+ raise 'No package name supplied' unless name
22
+
23
+ @name = name
24
+ @provides = metadata[:provides]
25
+ @dependencies = []
26
+ self.instance_eval &block
27
+ end
28
+
29
+ def apt(*names)
30
+ @installer = Sprinkle::Installers::Apt.new(self, *names)
31
+ end
32
+
33
+ def gem(name)
34
+ @dependencies << :rubygems
35
+ @installer = Sprinkle::Installers::Gem.new(self, name)
36
+ end
37
+
38
+ def source(source, &block)
39
+ @dependencies << :build_essential # REVISIT: should only be for Ubuntu/Debian, need platform specific bits here
40
+ @installer = Sprinkle::Installers::Source.new(self, source, &block)
41
+ end
42
+
43
+ def process(deployment, roles)
44
+ return if meta_package?
45
+
46
+ @installer.defaults(deployment)
47
+ @installer.process(roles)
48
+ end
49
+
50
+ def requires(*packages)
51
+ @dependencies << packages
52
+ @dependencies.flatten!
53
+ end
54
+
55
+ def tree(depth = 1, &block)
56
+ packages = []
57
+
58
+ @dependencies.each do |dep|
59
+ package = PACKAGES[dep]
60
+ block.call(self, package, depth) if block
61
+ packages << package.tree(depth + 1, &block)
62
+ end
63
+
64
+ packages << self
65
+ end
66
+
67
+ def to_s; @name; end
68
+
69
+ private
70
+
71
+ def meta_package?
72
+ @installer == nil
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,84 @@
1
+ require 'highline/import'
2
+
3
+ module Sprinkle
4
+ module Policy
5
+ POLICIES = []
6
+
7
+ def policy(name, options = {}, &block)
8
+ p = Policy.new(name, options, &block)
9
+ POLICIES << p
10
+ p
11
+ end
12
+
13
+ class Policy
14
+ attr_reader :name, :packages
15
+
16
+ def initialize(name, metadata = {}, &block)
17
+ raise 'No name provided' unless name
18
+ raise 'No roles provided' unless metadata[:roles]
19
+
20
+ @name = name
21
+ @roles = metadata[:roles]
22
+ @packages = []
23
+ self.instance_eval(&block)
24
+ end
25
+
26
+ def requires(package, options = {})
27
+ @packages << package
28
+ end
29
+
30
+ def to_s; name; end
31
+
32
+ def process(deployment)
33
+ all = []
34
+
35
+ cloud_info "--> Cloud hierarchy for policy #{@name}"
36
+
37
+ @packages.each do |p|
38
+ cloud_info "\nPolicy #{@name} requires package #{p}"
39
+
40
+ package = Sprinkle::Package::PACKAGES[p]
41
+ package = select_package(p, package) if package.is_a? Array # handle virtual package selection
42
+
43
+ tree = package.tree do |parent, child, depth|
44
+ indent = "\t" * depth; cloud_info "#{indent}Package #{parent.name} requires #{child.name}"
45
+ end
46
+
47
+ all << tree
48
+ end
49
+
50
+ normalize(all) do |package|
51
+ package.process(deployment, @roles)
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ def cloud_info(message)
58
+ logger.info(message) if Sprinkle::OPTIONS[:cloud] or logger.debug?
59
+ end
60
+
61
+ def select_package(name, packages)
62
+ if packages.size <= 1
63
+ package = packages.first
64
+ else
65
+ package = choose do |menu|
66
+ menu.prompt = "Multiple choices exist for virtual package #{name}"
67
+ menu.choices *packages.collect(&:to_s)
68
+ end
69
+ package = Sprinkle::Package::PACKAGES[package]
70
+ end
71
+
72
+ cloud_info "Selecting #{package.to_s} for virtual package #{name}"
73
+
74
+ package
75
+ end
76
+
77
+ def normalize(all, &block)
78
+ all = all.flatten.uniq
79
+ cloud_info "\n--> Normalized installation order for all packages: #{all.collect(&:name).join(', ')}"
80
+ all.each &block
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,13 @@
1
+ module Sprinkle
2
+ class Script
3
+ def self.sprinkle(script, filename = '__SCRIPT__')
4
+ powder = new
5
+ powder.instance_eval script, filename
6
+ powder.sprinkle
7
+ end
8
+
9
+ def sprinkle
10
+ @deployment.process if @deployment
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ module Sprinkle #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 1
5
+ TINY = 0
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
data/lib/sprinkle.rb ADDED
@@ -0,0 +1,29 @@
1
+ require 'rubygems'
2
+ require 'active_support'
3
+
4
+ # Use active supports auto load mechanism
5
+ Dependencies.load_paths << File.dirname(__FILE__)
6
+
7
+ # Configure active support to log auto-loading of dependencies
8
+ #Dependencies::RAILS_DEFAULT_LOGGER = Logger.new($stdout)
9
+ #Dependencies.log_activity = true
10
+
11
+ # Load up extensions to existing classes
12
+ Dir[File.dirname(__FILE__) + '/sprinkle/extensions/*.rb'].each { |e| require e }
13
+
14
+ # Configuration options
15
+ module Sprinkle
16
+ OPTIONS = { :testing => false, :verbose => false }
17
+ end
18
+
19
+ # Understand packages, policies and deployment DSL
20
+ class Object
21
+ include Sprinkle::Package, Sprinkle::Policy, Sprinkle::Deployment
22
+ end
23
+
24
+ # Define a logging target
25
+ class Object
26
+ def logger
27
+ @@__log__ ||= ActiveSupport::BufferedLogger.new($stdout, ActiveSupport::BufferedLogger::Severity::INFO)
28
+ end
29
+ end
data/script/destroy ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
data/script/generate ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --colour