beet 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +2 -0
- data/LICENSE +20 -0
- data/README.rdoc +13 -0
- data/Rakefile +59 -0
- data/TODO +17 -0
- data/VERSION +1 -0
- data/beet.gemspec +89 -0
- data/bin/beet +112 -0
- data/features/generating.feature +24 -0
- data/features/step_definitions/beet_steps.rb +7 -0
- data/features/step_definitions/common_steps.rb +172 -0
- data/features/support/common.rb +33 -0
- data/features/support/env.rb +25 -0
- data/lib/beet.rb +10 -0
- data/lib/beet/capistrano.rb +14 -0
- data/lib/beet/execution.rb +50 -0
- data/lib/beet/executor.rb +195 -0
- data/lib/beet/file_system.rb +143 -0
- data/lib/beet/gem_location_map.rb +64 -0
- data/lib/beet/interaction.rb +37 -0
- data/lib/beet/logger.rb +44 -0
- data/lib/beet/rails.rb +146 -0
- data/lib/beet/recipes/passenger/vhost.rb +17 -0
- data/lib/beet/recipes/rack/middleware.rb +17 -0
- data/lib/beet/recipes/rails/authlogic.rb +297 -0
- data/lib/beet/recipes/rails/clean_files.rb +4 -0
- data/lib/beet/recipes/rails/clearance.rb +22 -0
- data/lib/beet/recipes/rails/cms/bcms_blog.rb +21 -0
- data/lib/beet/recipes/rails/cms/bcms_event.rb +24 -0
- data/lib/beet/recipes/rails/css/blueprint.rb +5 -0
- data/lib/beet/recipes/rails/css/reset.rb +10 -0
- data/lib/beet/recipes/rails/db/mysql.rb +42 -0
- data/lib/beet/recipes/rails/db/postgres.rb +52 -0
- data/lib/beet/recipes/rails/git.rb +23 -0
- data/lib/beet/recipes/rails/jquery.rb +11 -0
- data/lib/beet/recipes/rails/rspec.rb +3 -0
- data/lib/beet/recipes/rails/shoulda.rb +1 -0
- data/lib/beet/scm.rb +36 -0
- data/lib/beet/scm/git.rb +15 -0
- data/lib/beet/scm/svn.rb +5 -0
- data/lib/beet/template_location_map.rb +13 -0
- data/test/executor_test.rb +24 -0
- data/test/file_system_test.rb +59 -0
- data/test/test_helper.rb +12 -0
- metadata +110 -0
@@ -0,0 +1,33 @@
|
|
1
|
+
module CommonHelpers
|
2
|
+
def in_tmp_folder(&block)
|
3
|
+
FileUtils.chdir(@tmp_root, &block)
|
4
|
+
end
|
5
|
+
|
6
|
+
def in_project_folder(&block)
|
7
|
+
project_folder = @active_project_folder || @tmp_root
|
8
|
+
FileUtils.chdir(project_folder, &block)
|
9
|
+
end
|
10
|
+
|
11
|
+
def in_project_subfolder(folder, &block)
|
12
|
+
FileUtils.chdir(File.join(@active_project_folder, folder), &block)
|
13
|
+
end
|
14
|
+
|
15
|
+
def in_home_folder(&block)
|
16
|
+
FileUtils.chdir(@home_path, &block)
|
17
|
+
end
|
18
|
+
|
19
|
+
def force_local_lib_override(project_name = @project_name)
|
20
|
+
rakefile = File.read(File.join(project_name, 'Rakefile'))
|
21
|
+
File.open(File.join(project_name, 'Rakefile'), "w+") do |f|
|
22
|
+
f << "$:.unshift('#{@lib_path}')\n"
|
23
|
+
f << rakefile
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def setup_active_project_folder project_name
|
28
|
+
@active_project_folder = File.join(@tmp_root, project_name)
|
29
|
+
@project_name = project_name
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
World(CommonHelpers)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
gem 'cucumber'
|
2
|
+
require 'cucumber'
|
3
|
+
gem 'rspec'
|
4
|
+
require 'spec'
|
5
|
+
|
6
|
+
Before do
|
7
|
+
@tmp_root = File.dirname(__FILE__) + "/../../tmp"
|
8
|
+
@home_path = File.expand_path(File.join(@tmp_root, "home"))
|
9
|
+
FileUtils.rm_rf @tmp_root
|
10
|
+
FileUtils.mkdir_p @home_path
|
11
|
+
ENV['HOME'] = @home_path
|
12
|
+
end
|
13
|
+
|
14
|
+
After do
|
15
|
+
unless ENV['LEAVE_CUCUMBER_GENERATED_OUTPUT']
|
16
|
+
FileUtils.rm_rf @active_project_folder
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
gem "fakeweb"
|
21
|
+
require "fakeweb"
|
22
|
+
|
23
|
+
Before do
|
24
|
+
FakeWeb.allow_net_connect = false
|
25
|
+
end
|
data/lib/beet.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# TODO similarly, figure out how to require things as needed by the template
|
2
|
+
require 'beet/execution'
|
3
|
+
require 'beet/file_system'
|
4
|
+
require 'beet/interaction'
|
5
|
+
require 'beet/capistrano'
|
6
|
+
require 'beet/rails'
|
7
|
+
require 'beet/scm'
|
8
|
+
require 'beet/executor'
|
9
|
+
require 'beet/gem_location_map'
|
10
|
+
require 'beet/template_location_map'
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Beet
|
2
|
+
module Execution
|
3
|
+
# Executes a command
|
4
|
+
#
|
5
|
+
# ==== Example
|
6
|
+
#
|
7
|
+
# inside('vendor') do
|
8
|
+
# run('ln -s ~/edge rails)
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
def run(command, log_action = true)
|
12
|
+
log 'executing', "#{command} from #{Dir.pwd}" if log_action
|
13
|
+
system(command)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Executes a command with sudo
|
17
|
+
#
|
18
|
+
# ==== Example
|
19
|
+
#
|
20
|
+
# inside('vendor') do
|
21
|
+
# sudo('mkdir /var/log/something')
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
def sudo(command, log_action = true)
|
25
|
+
command = "#{SUDO}#{command}"
|
26
|
+
run(command,log_action)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Executes a ruby script (taking into account WIN32 platform quirks)
|
30
|
+
def run_ruby_script(command, log_action = true)
|
31
|
+
ruby_command = RUBY_PLATFORM=~ /win32/ ? 'ruby ' : ''
|
32
|
+
run("#{ruby_command}#{command}", log_action)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Runs the supplied rake task
|
36
|
+
#
|
37
|
+
# ==== Example
|
38
|
+
#
|
39
|
+
# rake("db:migrate")
|
40
|
+
# rake("db:migrate", :env => "production")
|
41
|
+
# rake("gems:install", :sudo => true)
|
42
|
+
#
|
43
|
+
def rake(command, options = {})
|
44
|
+
log 'rake', command
|
45
|
+
env = options[:env] || 'development'
|
46
|
+
sudo = options[:sudo] ? 'sudo ' : ''
|
47
|
+
in_root { run("#{sudo}rake #{command} RAILS_ENV=#{env}", false) }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,195 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'beet/logger'
|
3
|
+
|
4
|
+
module Beet
|
5
|
+
class Executor
|
6
|
+
BEET_DATA_FILE = "~/.beet.yml"
|
7
|
+
include Beet::Execution
|
8
|
+
include Beet::FileSystem
|
9
|
+
include Beet::Interaction
|
10
|
+
|
11
|
+
include Beet::Rails
|
12
|
+
include Beet::Capistrano
|
13
|
+
include Beet::SCM
|
14
|
+
|
15
|
+
attr_reader :root, :logger, :options, :template
|
16
|
+
attr_accessor :recipes, :project_name, :gems, :todo_items
|
17
|
+
|
18
|
+
def initialize(project_name, options={}) # :nodoc:
|
19
|
+
@root = calculate_project_root(project_name)
|
20
|
+
@project_name = ((project_name == '.') ? File.basename(Dir.pwd) : project_name)
|
21
|
+
@logger = Beet::Logger.new
|
22
|
+
@gems = []
|
23
|
+
@template = options[:template]
|
24
|
+
@options = options
|
25
|
+
@todo_items = ''
|
26
|
+
@recipes = []
|
27
|
+
@project_type = options[:project_type]
|
28
|
+
@generate = true unless options[:generate] == false
|
29
|
+
@display = options[:display]
|
30
|
+
extract_commands_from_options
|
31
|
+
end
|
32
|
+
|
33
|
+
def start
|
34
|
+
if @options[:use]
|
35
|
+
puts "Loading saved configuration: #{@options[:use]}"
|
36
|
+
data = load_saved_recipe_file
|
37
|
+
if config = data[@options[:use]]
|
38
|
+
@gems.concat(config[:gems]) if config[:gems]
|
39
|
+
@template = config[:template] if config[:template]
|
40
|
+
@recipes.concat(config[:recipes]) if config[:recipes]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
if @display && @template
|
45
|
+
puts open(TEMPLATE_LOCATIONS[@template]).read
|
46
|
+
else
|
47
|
+
case @project_type
|
48
|
+
when :rails
|
49
|
+
if @generate
|
50
|
+
puts "Generating rails project #{project_name}..."
|
51
|
+
if @template
|
52
|
+
system("rails #{project_name} -m #{TEMPLATE_LOCATIONS[@template]}")
|
53
|
+
else
|
54
|
+
system("rails #{project_name}")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
add_gems
|
60
|
+
|
61
|
+
print_todo
|
62
|
+
|
63
|
+
if @options[:save]
|
64
|
+
save_run
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
run_recipes
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
def run_recipes
|
73
|
+
@recipes.each do |recipe|
|
74
|
+
begin
|
75
|
+
code = open(recipe).read
|
76
|
+
if @display
|
77
|
+
puts code
|
78
|
+
else
|
79
|
+
in_root { instance_eval(code)}
|
80
|
+
end
|
81
|
+
rescue LoadError, Errno::ENOENT => e
|
82
|
+
raise "The recipe [#{recipe}] could not be loaded. Error: #{e}"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def log(*args)
|
88
|
+
logger.log(*args)
|
89
|
+
end
|
90
|
+
|
91
|
+
def todo(string=nil, &block)
|
92
|
+
self.todo_items << (string || block.call)
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def print_todo
|
98
|
+
unless todo_items.empty?
|
99
|
+
puts '#' * 30
|
100
|
+
puts "TODO Items:"
|
101
|
+
puts todo_items
|
102
|
+
puts '#' * 30
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def calculate_project_root(project_name)
|
107
|
+
# if the name looks like ~/projects/foobar then thats the root
|
108
|
+
if project_name.include?('/')
|
109
|
+
project_name
|
110
|
+
# if we're running inside the app, then current dir is it
|
111
|
+
elsif File.basename(Dir.pwd) == project_name
|
112
|
+
Dir.pwd
|
113
|
+
# assume the root is ./project_name
|
114
|
+
else
|
115
|
+
File.join(Dir.pwd, project_name)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def add_gems
|
120
|
+
if @gems
|
121
|
+
@gems.each do |gem_data|
|
122
|
+
gem(gem_data[:name], :source => gem_data[:source])
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def extract_commands_from_options
|
128
|
+
if @options[:gems]
|
129
|
+
@options[:gems].split(/[\s,]+/).each do |gem|
|
130
|
+
if location = gem_location(gem)
|
131
|
+
@gems << {:name => gem, :source => location}
|
132
|
+
else
|
133
|
+
puts "gem: #{gem} not found. Did you spell it correctly? If so, submit a patch with its location!"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
if @options[:recipes]
|
138
|
+
@options[:recipes].split(/[\s,]+/).each do |recipe|
|
139
|
+
if file = recipe_location(recipe)
|
140
|
+
@recipes << file
|
141
|
+
else
|
142
|
+
puts "Can't find recipe #{recipe}"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def save_run
|
149
|
+
require 'yaml'
|
150
|
+
name = if options[:save] == true
|
151
|
+
ask("Enter a name for this configuration: ")
|
152
|
+
else
|
153
|
+
options[:save]
|
154
|
+
end
|
155
|
+
data = load_saved_recipe_file
|
156
|
+
data[name] = {:gems => @gems, :recipes => @recipes, :template => @template}
|
157
|
+
write_saved_recipe_file(data)
|
158
|
+
end
|
159
|
+
|
160
|
+
def beet_data_file
|
161
|
+
File.expand_path(BEET_DATA_FILE)
|
162
|
+
end
|
163
|
+
|
164
|
+
def load_saved_recipe_file
|
165
|
+
if File.exists?(beet_data_file)
|
166
|
+
YAML.load_file(beet_data_file)
|
167
|
+
else
|
168
|
+
{}
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def write_saved_recipe_file(data)
|
173
|
+
File.open(beet_data_file, "wb") do |f|
|
174
|
+
f.write(YAML::dump(data))
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def gem_location(gem_name)
|
179
|
+
GEM_LOCATIONS[gem_name]
|
180
|
+
end
|
181
|
+
|
182
|
+
def recipe_location(recipe)
|
183
|
+
return recipe if File.exists?(recipe) or recipe.include?('http://')
|
184
|
+
locations = []
|
185
|
+
locations << File.expand_path(ENV['BEET_RECIPES_DIR']) if ENV['BEET_RECIPES_DIR']
|
186
|
+
locations << File.expand_path(File.join(File.dirname(__FILE__), 'recipes'))
|
187
|
+
locations.each do |location|
|
188
|
+
filename = File.join(location, "#{recipe}.rb")
|
189
|
+
return filename if File.exists?(filename)
|
190
|
+
end
|
191
|
+
nil
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
@@ -0,0 +1,143 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
module Beet
|
3
|
+
module FileSystem
|
4
|
+
# Create a new file in the project folder. Specify the
|
5
|
+
# relative path from the project's root. Data is the return value of a block
|
6
|
+
# or a data string.
|
7
|
+
#
|
8
|
+
# ==== Examples
|
9
|
+
#
|
10
|
+
# file("lib/fun_party.rb") do
|
11
|
+
# hostname = ask("What is the virtual hostname I should use?")
|
12
|
+
# "vhost.name = #{hostname}"
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# file("config/apach.conf", "your apache config")
|
16
|
+
#
|
17
|
+
def file(filename, data = nil, log_action = true, &block)
|
18
|
+
log 'file', filename if log_action
|
19
|
+
dir, file = [File.dirname(filename), File.basename(filename)]
|
20
|
+
|
21
|
+
inside(dir) do
|
22
|
+
File.open(file, "w") do |f|
|
23
|
+
if block_given?
|
24
|
+
f.write(block.call)
|
25
|
+
else
|
26
|
+
f.write(data)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Create a new file in the lib/ directory. Code can be specified
|
33
|
+
# in a block or a data string can be given.
|
34
|
+
#
|
35
|
+
# ==== Examples
|
36
|
+
#
|
37
|
+
# lib("crypto.rb") do
|
38
|
+
# "crypted_special_value = '#{rand}--#{Time.now}--#{rand(1337)}--'"
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# lib("foreign.rb", "# Foreign code is fun")
|
42
|
+
#
|
43
|
+
def lib(filename, data = nil, &block)
|
44
|
+
log 'lib', filename
|
45
|
+
file("lib/#{filename}", data, false, &block)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Create a new Rakefile with the provided code (either in a block or a string).
|
49
|
+
#
|
50
|
+
# ==== Examples
|
51
|
+
#
|
52
|
+
# rakefile("bootstrap.rake") do
|
53
|
+
# project = ask("What is the UNIX name of your project?")
|
54
|
+
#
|
55
|
+
# <<-TASK
|
56
|
+
# namespace :#{project} do
|
57
|
+
# task :bootstrap do
|
58
|
+
# puts "i like boots!"
|
59
|
+
# end
|
60
|
+
# end
|
61
|
+
# TASK
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
# rakefile("seed.rake", "puts 'im plantin ur seedz'")
|
65
|
+
#
|
66
|
+
def rakefile(filename, data = nil, &block)
|
67
|
+
log 'rakefile', filename
|
68
|
+
file("lib/tasks/#{filename}", data, false, &block)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Do something in the root of the project or
|
72
|
+
# a provided subfolder; the full path is yielded to the block you provide.
|
73
|
+
# The path is set back to the previous path when the method exits.
|
74
|
+
def inside(dir = '', &block)
|
75
|
+
folder = File.join(root, dir)
|
76
|
+
FileUtils.mkdir_p(folder) unless File.exist?(folder)
|
77
|
+
FileUtils.cd(folder) { block.arity == 1 ? yield(folder) : yield }
|
78
|
+
end
|
79
|
+
|
80
|
+
def in_root
|
81
|
+
FileUtils.cd(root) { yield }
|
82
|
+
end
|
83
|
+
|
84
|
+
# Run a regular expression replacement on a file
|
85
|
+
#
|
86
|
+
# ==== Example
|
87
|
+
#
|
88
|
+
# gsub_file 'app/controllers/application_controller.rb', /#\s*(filter_parameter_logging :password)/, '\1'
|
89
|
+
#
|
90
|
+
def gsub_file(relative_destination, regexp, *args, &block)
|
91
|
+
#path = destination_path(relative_destination)
|
92
|
+
path = relative_destination
|
93
|
+
content = File.read(path)
|
94
|
+
check_for = args.first || yield('')
|
95
|
+
regex = Regexp.new(regexp.source + Regexp.escape(check_for))
|
96
|
+
return if content =~ regex # if we can match the text and its leadin regex, don't add again
|
97
|
+
content = content.gsub(regexp, *args, &block)
|
98
|
+
File.open(path, 'wb') { |file| file.write(content) }
|
99
|
+
end
|
100
|
+
|
101
|
+
# Append text to a file
|
102
|
+
#
|
103
|
+
# ==== Example
|
104
|
+
#
|
105
|
+
# append_file 'config/environments/test.rb', 'config.gem "rspec"'
|
106
|
+
#
|
107
|
+
def append_file(relative_destination, data)
|
108
|
+
path = destination_path(relative_destination)
|
109
|
+
File.open(path, 'ab') { |file| file.write(data) }
|
110
|
+
end
|
111
|
+
|
112
|
+
# Add text after matching line
|
113
|
+
#
|
114
|
+
# ==== Example
|
115
|
+
#
|
116
|
+
# add_after 'config/environment.rb', '# config.gem "aws-s3", :lib => "aws/s3"'
|
117
|
+
#
|
118
|
+
def add_after(filename, matching_text, data=nil, &block)
|
119
|
+
gsub_file filename, /(\s*#{Regexp.escape(matching_text)})/mi do |match|
|
120
|
+
"#{match}\n#{data || block.call}"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Add text before matching line
|
125
|
+
#
|
126
|
+
# ==== Example
|
127
|
+
#
|
128
|
+
# add_before 'config/environment.rb', '# config.gem "aws-s3", :lib => "aws/s3"'
|
129
|
+
#
|
130
|
+
def add_before(filename, matching_text, data=nil, &block)
|
131
|
+
gsub_file filename, /^(\s*#{Regexp.escape(matching_text)})/mi do |match|
|
132
|
+
"#{data || block.call}#{match}"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
protected
|
137
|
+
|
138
|
+
def destination_path(relative_destination)
|
139
|
+
File.join(root, relative_destination)
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
end
|