visionbundles 0.2.0 → 0.3.0
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.
- 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
|