visionbundles 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +8 -0
- data/README.md +24 -229
- data/Rakefile +12 -0
- data/lib/generators/install_generator.rb +42 -0
- data/lib/generators/security_generator.rb +44 -0
- data/lib/generators/templates/Capfile +3 -0
- data/lib/generators/templates/database.yml +26 -0
- data/lib/generators/templates/deploy_multiple.rb +59 -0
- data/lib/generators/templates/deploy_single.rb +49 -0
- data/lib/visionbundles.rb +2 -1
- data/lib/visionbundles/asset_sync.rb +4 -0
- data/lib/visionbundles/helpers.rb +2 -0
- data/lib/visionbundles/helpers/configuration.rb +41 -0
- data/lib/visionbundles/helpers/git.rb +10 -0
- data/lib/visionbundles/helpers/logger.rb +27 -0
- data/lib/visionbundles/helpers/recipes.rb +17 -0
- data/lib/visionbundles/helpers/servers.rb +32 -0
- data/lib/visionbundles/helpers/templates.rb +19 -0
- data/lib/visionbundles/rails.rb +7 -0
- data/lib/visionbundles/recipes/db.rb +20 -34
- data/lib/visionbundles/recipes/dev.rb +0 -2
- data/lib/visionbundles/recipes/fast_assets.rb +25 -3
- data/lib/visionbundles/recipes/nginx.rb +3 -1
- data/lib/visionbundles/recipes/preconfig.rb +69 -0
- data/lib/visionbundles/recipes/preconfig/helpers.rb +28 -0
- data/lib/visionbundles/recipes/puma.rb +7 -8
- data/lib/visionbundles/recipes/secret.rb +14 -37
- data/lib/visionbundles/recipes/valid.rb +52 -0
- data/lib/visionbundles/templates/puma/config.erb +9 -7
- data/lib/visionbundles/version.rb +1 -1
- data/todo.md +15 -0
- data/visionbundles.gemspec +3 -2
- metadata +38 -5
- data/lib/visionbundles/base.rb +0 -100
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'bundler/capistrano'
|
2
|
+
require 'rvm/capistrano'
|
3
|
+
require 'visionbundles'
|
4
|
+
|
5
|
+
# RVM Settings
|
6
|
+
set :rvm_ruby_string, '2.1.0'
|
7
|
+
set :rvm_type, :user
|
8
|
+
$:.unshift(File.expand_path('./lib', ENV['rvm_path']))
|
9
|
+
|
10
|
+
# Recipes Included
|
11
|
+
# Source: https://github.com/afunction/visionbundles/blob/master/lib/visionbundles/recipes
|
12
|
+
include_recipes :nginx, :puma, :db, :dev
|
13
|
+
|
14
|
+
# Nginx
|
15
|
+
# Source: https://github.com/afunction/visionbundles/blob/master/lib/visionbundles/recipes/nginx.rb
|
16
|
+
set :nginx_vhost_domain, 'www.domain.com' # your domain that to nginx.
|
17
|
+
set :nginx_upstream_via_sock_file, false
|
18
|
+
set :nginx_app_servers, ['127.0.0.1:9290'] # upstream will point to app server.
|
19
|
+
|
20
|
+
# Puma
|
21
|
+
# Source: https://github.com/afunction/visionbundles/blob/master/lib/visionbundles/recipes/puma.rb
|
22
|
+
set :puma_bind_for, :tcp
|
23
|
+
set :puma_bind_to, '127.0.0.1'
|
24
|
+
set :puma_bind_port, '9290'
|
25
|
+
set :puma_thread_min, 32
|
26
|
+
set :puma_thread_max, 32
|
27
|
+
set :puma_workers, 3
|
28
|
+
|
29
|
+
# Role Settings
|
30
|
+
server '11.222.33.44', :web, :app, :db, primary: true
|
31
|
+
|
32
|
+
# Capistrano Base Setting
|
33
|
+
set :application, 'your_project_name'
|
34
|
+
set :user, 'rails'
|
35
|
+
set :deploy_to, "/home/#{user}/apps/#{application}"
|
36
|
+
set :deploy_via, :remote_cache
|
37
|
+
set :use_sudo, false
|
38
|
+
set :rails_env, :production
|
39
|
+
|
40
|
+
# Git Settings
|
41
|
+
set :scm, :git
|
42
|
+
set :repository, "git@github.com:username/#{application}.git" # your git source, and make sure your server have premission to access your git server
|
43
|
+
set :branch, :master # the branch you want to deploy
|
44
|
+
|
45
|
+
# Extra settings
|
46
|
+
default_run_options[:pty] = true
|
47
|
+
ssh_options[:forward_agent] = true
|
48
|
+
|
49
|
+
after 'deploy', 'deploy:cleanup' # keep only the last 5 releases
|
data/lib/visionbundles.rb
CHANGED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
module Visionbundles
|
3
|
+
module Helpers
|
4
|
+
module Configuration
|
5
|
+
def load_config_from(file_path, env)
|
6
|
+
config = YAML::load_file("./#{file_path}.yml")[env.to_s]
|
7
|
+
setup_configs(config['config'] || {})
|
8
|
+
setup_servers(config['servers'] || [])
|
9
|
+
setup_preconfig(config['preconfig' || {}])
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def setup_configs(configs)
|
15
|
+
configs.each do |key, value|
|
16
|
+
send(:set, key, value)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def setup_servers(servers)
|
21
|
+
servers.each do |server|
|
22
|
+
roles = server['roles'].is_a?(Array) ? server['roles'].map(&:to_sym) : [ server['roles'].to_sym ]
|
23
|
+
if server['opts'].is_a?(Hash)
|
24
|
+
options = Hash[server['opts'].map { |k, y| [k.to_sym, y] }]
|
25
|
+
roles.push(options)
|
26
|
+
end
|
27
|
+
send(:server, server['host'], *roles)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def setup_preconfig(preconfig)
|
32
|
+
set :preconfig_dir, preconfig['root'] if preconfig['root'].present?
|
33
|
+
preconfig_mapper = Hash[(preconfig['list'] || []).map { |list|
|
34
|
+
[list['src'], list['dest']]
|
35
|
+
}]
|
36
|
+
preconfig_files Hash[preconfig_mapper] if preconfig_mapper.present?
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
include Visionbundles::Helpers::Configuration
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'colorize'
|
2
|
+
module Visionbundles
|
3
|
+
module Helpers
|
4
|
+
module Logger
|
5
|
+
def info(message)
|
6
|
+
puts "#{current_server} -> #{message}".colorize(:light_cyan)
|
7
|
+
end
|
8
|
+
|
9
|
+
def warn(message)
|
10
|
+
puts "#{current_server} -> #{message}".colorize(:red)
|
11
|
+
end
|
12
|
+
|
13
|
+
def valid_pass(topic)
|
14
|
+
puts "\t[Pass] #{topic}".colorize(color: :green, background: :light_white).underline
|
15
|
+
end
|
16
|
+
|
17
|
+
def valid_faild(topic)
|
18
|
+
puts "\t[Faild] #{topic}".colorize(color: :red, background: :light_white).underline
|
19
|
+
end
|
20
|
+
|
21
|
+
def valid_skip(topic)
|
22
|
+
puts "\t[Skip] #{topic}".colorize(color: :yellow, background: :light_white).underline
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
include Visionbundles::Helpers::Logger
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Visionbundles
|
2
|
+
module Helpers
|
3
|
+
module Recipes
|
4
|
+
def include_recipes(*recipes)
|
5
|
+
recipes.each do |recipe|
|
6
|
+
require "visionbundles/recipes/#{recipe}.rb"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def set_default(name, *args, &block)
|
11
|
+
set(name, *args, &block) unless exists?(name)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
include Visionbundles::Helpers::Recipes
|
17
|
+
include_recipes 'preconfig'
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Visionbundles
|
2
|
+
module Helpers
|
3
|
+
module Servers
|
4
|
+
def remote_file_exists?(path)
|
5
|
+
results = []
|
6
|
+
|
7
|
+
invoke_command("if [ -f '#{path}' ]; then echo -n 'true'; fi") do |ch, stream, out|
|
8
|
+
results << (out == 'true')
|
9
|
+
end
|
10
|
+
|
11
|
+
!results.empty?
|
12
|
+
end
|
13
|
+
|
14
|
+
def run_if_file_exists?(path, command)
|
15
|
+
run "if [ -f '#{path}' ]; then #{command}; fi"
|
16
|
+
end
|
17
|
+
|
18
|
+
def run_if_file_not_exists?(path, command)
|
19
|
+
run "if [ ! -f '#{path}' ]; then #{command}; fi"
|
20
|
+
end
|
21
|
+
|
22
|
+
def mkdir(remote_path)
|
23
|
+
run "mkdir -p #{remote_path}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def current_server
|
27
|
+
capture("echo $CAPISTRANO:HOST$").strip
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
include Visionbundles::Helpers::Servers
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'erb'
|
2
|
+
module Visionbundles
|
3
|
+
module Helpers
|
4
|
+
module Templates
|
5
|
+
def from_template(file)
|
6
|
+
abs_path = File.join(File.dirname(__FILE__), file)
|
7
|
+
template = File.read(abs_path)
|
8
|
+
ERB.new(template).result(binding)
|
9
|
+
end
|
10
|
+
|
11
|
+
def template(erb_source, to_dir, filename)
|
12
|
+
mkdir(to_dir)
|
13
|
+
compiled_file = "#{to_dir}/#{filename}"
|
14
|
+
put from_template(erb_source), compiled_file
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
include Visionbundles::Helpers::Templates
|
@@ -1,40 +1,26 @@
|
|
1
1
|
Capistrano::Configuration.instance(:must_exist).load do
|
2
|
-
|
3
|
-
set_default(:database_template, 'database.production.yml')
|
2
|
+
require 'active_record'
|
4
3
|
|
5
|
-
|
6
|
-
desc 'copy production database config from local'
|
7
|
-
task :copy_production_config, roles: [:app, :web] do
|
8
|
-
info '[Template] Copy Database Template'
|
9
|
-
copy_production_from_local(database_template, 'database.yml')
|
10
|
-
end
|
4
|
+
preconfig_files('database.yml' => 'config/database.yml')
|
11
5
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
database_setting_path = "#{shared_path}/config/database.yml"
|
17
|
-
template_database_path = production_config('database.yml')
|
18
|
-
run_if_file_not_exists?(database_setting_path, "cp #{template_database_path} #{database_setting_path}")
|
19
|
-
end
|
20
|
-
after 'deploy:setup', 'db:copy_production_config', 'db:setup'
|
21
|
-
|
22
|
-
desc "setup database symlink for every time deploy"
|
23
|
-
task :symlink_config, roles: [:app, :web] do
|
24
|
-
run "ln -nfs #{shared_path}/config/database.yml #{release_path}/config/database.yml"
|
25
|
-
end
|
26
|
-
|
27
|
-
after "deploy:finalize_update", "db:symlink_config"
|
28
|
-
|
29
|
-
desc "remove database config file"
|
30
|
-
task :remove_config, roles: [:app, :web] do
|
31
|
-
run "rm #{shared_path}/config/database.yml"
|
32
|
-
end
|
6
|
+
namespace :db do
|
7
|
+
desc 'check if each servers can authenticate and connect to database'
|
8
|
+
task :valid do
|
9
|
+
db = YAML::load_file("./#{preconfig_dir}/database.yml")[rails_env.to_s]
|
33
10
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
11
|
+
find_servers.map.with_index { |server, num|
|
12
|
+
Thread.new {
|
13
|
+
host = server.to_s
|
14
|
+
port = Net::SSH::Gateway.new(host, user).open('127.0.0.1', 3306, 3307 + num)
|
15
|
+
begin
|
16
|
+
ActiveRecord::Base.establish_connection(db.merge(host: '127.0.0.1', port: port)).connection
|
17
|
+
valid_pass "Server: #{host} database authenticate and connection successful."
|
18
|
+
rescue Exception => e
|
19
|
+
valid_faild "Server: #{host} cannot connect to database: #{db['host']}, please check the database.yml or database firewall."
|
20
|
+
end
|
21
|
+
}
|
22
|
+
}.each { |task| task.join }
|
38
23
|
end
|
39
24
|
end
|
40
|
-
|
25
|
+
after 'deploy:valid', 'db:valid'
|
26
|
+
end
|
@@ -14,8 +14,6 @@ Capistrano::Configuration.instance(:must_exist).load do
|
|
14
14
|
|
15
15
|
desc "dev:build task"
|
16
16
|
task :build, roles: :db, primary: true do
|
17
|
-
return warn "Not Found Primary Database" unless have_primary_database?
|
18
|
-
|
19
17
|
if dev_sure_danger_command == 'Y'
|
20
18
|
["tmp:clear", "log:clear", "db:drop", "db:create", "db:migrate", "db:seed"].each do |rake_command|
|
21
19
|
run "cd #{current_path}; bundle exec rake #{rake_command} RAILS_ENV=#{rails_env}"
|
@@ -55,7 +55,7 @@ Capistrano::Configuration.instance(:must_exist).load do
|
|
55
55
|
config.manifest_path = run_locally("ls #{assets_dir}/manifest*").strip!
|
56
56
|
config.assets_prefix = 'assets'
|
57
57
|
config.public_path = './public'
|
58
|
-
|
58
|
+
|
59
59
|
cdn.each do |option, value|
|
60
60
|
config.send("#{option}=", value)
|
61
61
|
end
|
@@ -65,9 +65,31 @@ Capistrano::Configuration.instance(:must_exist).load do
|
|
65
65
|
end
|
66
66
|
end
|
67
67
|
end
|
68
|
-
|
69
68
|
before "deploy:assets:symlink", "deploy:assets:remove_manifest"
|
70
69
|
after "deploy:assets:precompile", "deploy:assets:sync_manifest_to_app_servers",
|
71
70
|
"deploy:assets:sync_assets_files", "deploy:assets:cleanup"
|
72
71
|
end
|
73
|
-
|
72
|
+
|
73
|
+
namespace :fast_assets do
|
74
|
+
desc "check is local git commit same as the branch that deploying."
|
75
|
+
task :valid do
|
76
|
+
local_current_commit = `git rev-parse HEAD`.strip.split(' ').last[0..7]
|
77
|
+
# if setup commit id on branch variable
|
78
|
+
if local_current_commit.include?(branch.to_s)
|
79
|
+
valid_pass "fast_assets need same commit id between local (#{local_current_commit}) and remote (#{branch})"
|
80
|
+
else
|
81
|
+
remote_branch_commit = `git ls-remote #{repository} #{branch}`.strip
|
82
|
+
if remote_branch_commit.blank?
|
83
|
+
valid_faild "Not found remote branch: #{branch}"
|
84
|
+
exit
|
85
|
+
elsif (deploying_commit = (remote_branch_commit.split(' ').first || '')[0..7]) == local_current_commit
|
86
|
+
valid_pass "fast_assets need same commit id between local (#{local_current_commit}) and remote (#{deploying_commit})"
|
87
|
+
else
|
88
|
+
valid_faild "Local HEAD commit is (#{local_current_commit}), please checkout local branch to \"#{branch} (#{deploying_commit})\" same as you deployed."
|
89
|
+
exit
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
before 'deploy:update', 'fast_assets:valid'
|
94
|
+
end
|
95
|
+
end
|
@@ -6,11 +6,13 @@ Capistrano::Configuration.instance(:must_exist).load do
|
|
6
6
|
nginx_upstream_via_sock_file ? "/tmp/#{application}.sock" : "127.0.0.1:9292"
|
7
7
|
}
|
8
8
|
|
9
|
+
set_default(:nginx_config_template) { "../templates/nginx/nginx.conf.erb" }
|
10
|
+
|
9
11
|
namespace :nginx do
|
10
12
|
desc "setup nginx vhost config"
|
11
13
|
task :setup, roles: :web do
|
12
14
|
info '[Nginx] Setup vhost configuration files ...'
|
13
|
-
template
|
15
|
+
template nginx_config_template, "#{shared_path}/nginx", "vhost.conf"
|
14
16
|
sudo "ln -nfs #{shared_path}/nginx/vhost.conf /etc/nginx/sites-enabled/#{application}"
|
15
17
|
end
|
16
18
|
after 'deploy:setup', 'nginx:setup'
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'visionbundles/recipes/preconfig/helpers'
|
2
|
+
|
3
|
+
Capistrano::Configuration.instance(:must_exist).load do
|
4
|
+
set_default(:preconfig_dir) { "./preconfig" }
|
5
|
+
set_default(:preconfig_roles) { [:app, :web, :worker] }
|
6
|
+
|
7
|
+
namespace :preconfig do
|
8
|
+
desc "upload configuration file to shared path"
|
9
|
+
task :upload_config, roles: lambda { preconfig_roles} do
|
10
|
+
preconfig_files_list.each do |source, destination|
|
11
|
+
info "[Preconfig] uploading preconfig/#{source}"
|
12
|
+
upload_preconfig_file(source)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
after 'deploy:setup', 'preconfig:upload_config'
|
16
|
+
|
17
|
+
task :symlink, roles: lambda { preconfig_roles} do
|
18
|
+
preconfig_files_list.each do |source, destination|
|
19
|
+
config_file_path = remote_preconfig_souece(source)
|
20
|
+
info "[Preconfig] symlinking => #{destination}"
|
21
|
+
run "ln -nfs #{config_file_path} #{release_path}/#{destination}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
after "deploy:finalize_update", "preconfig:symlink"
|
25
|
+
|
26
|
+
desc <<-DESC
|
27
|
+
This task will check preconfig source file exists in local, and destination not in source control.
|
28
|
+
deploy flow will use `ln` command to make a symlink those config file to release folder,
|
29
|
+
however this command will not replace when file exists. so we should make sure those file exists in local
|
30
|
+
and destination not in source control.
|
31
|
+
DESC
|
32
|
+
task :valid do
|
33
|
+
unless File.exists?(preconfig_dir)
|
34
|
+
valid_faild "Not found preconfig folder: #{preconfig_dir}"
|
35
|
+
end
|
36
|
+
|
37
|
+
local_preconfigurations = []
|
38
|
+
destination_preconfigurations = []
|
39
|
+
|
40
|
+
preconfig_files_list.each do |source, destination|
|
41
|
+
local_file_path = "#{preconfig_dir}/#{source}"
|
42
|
+
|
43
|
+
# Make sure precofig source file is exists in local.
|
44
|
+
unless File.exists?(local_file_path)
|
45
|
+
local_preconfigurations.push(local_file_path)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Make sure preconfig destination is out of source control.
|
49
|
+
if file_in_source_control?(destination)
|
50
|
+
destination_preconfigurations.push(destination)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
if local_preconfigurations.empty?
|
55
|
+
valid_pass "Preconfig source: #{preconfig_files_list.map(&:first).join(', ')}"
|
56
|
+
else
|
57
|
+
valid_faild "Not found preconfig files: #{local_preconfigurations.join(', ')}"
|
58
|
+
end
|
59
|
+
|
60
|
+
if destination_preconfigurations.empty?
|
61
|
+
valid_pass "Preconfig destination: #{preconfig_files_list.map(&:last).join(', ')}"
|
62
|
+
else
|
63
|
+
valid_faild "Have to remove from source control files: #{destination_preconfigurations.join(', ')}"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
after 'deploy:valid', 'preconfig:valid'
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Visionbundles::PreconfigFiles
|
2
|
+
@@preconfig_files = {}
|
3
|
+
|
4
|
+
def self.preconfig_files
|
5
|
+
@@preconfig_files
|
6
|
+
end
|
7
|
+
|
8
|
+
def preconfig_files(src_to_dests)
|
9
|
+
src_to_dests.each do |file, desc|
|
10
|
+
Visionbundles::PreconfigFiles.preconfig_files[file] = desc
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def remote_preconfig_souece(file)
|
15
|
+
"#{shared_path}/preconfig/#{file}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def upload_preconfig_file(source)
|
19
|
+
preconfig_path = "#{shared_path}/preconfig"
|
20
|
+
mkdir(preconfig_path)
|
21
|
+
put File.read("#{preconfig_dir}/#{source}"), "#{preconfig_path}/#{source}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def preconfig_files_list
|
25
|
+
Visionbundles::PreconfigFiles.preconfig_files
|
26
|
+
end
|
27
|
+
end
|
28
|
+
include Visionbundles::PreconfigFiles
|