visionbundles 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +8 -0
  3. data/README.md +24 -229
  4. data/Rakefile +12 -0
  5. data/lib/generators/install_generator.rb +42 -0
  6. data/lib/generators/security_generator.rb +44 -0
  7. data/lib/generators/templates/Capfile +3 -0
  8. data/lib/generators/templates/database.yml +26 -0
  9. data/lib/generators/templates/deploy_multiple.rb +59 -0
  10. data/lib/generators/templates/deploy_single.rb +49 -0
  11. data/lib/visionbundles.rb +2 -1
  12. data/lib/visionbundles/asset_sync.rb +4 -0
  13. data/lib/visionbundles/helpers.rb +2 -0
  14. data/lib/visionbundles/helpers/configuration.rb +41 -0
  15. data/lib/visionbundles/helpers/git.rb +10 -0
  16. data/lib/visionbundles/helpers/logger.rb +27 -0
  17. data/lib/visionbundles/helpers/recipes.rb +17 -0
  18. data/lib/visionbundles/helpers/servers.rb +32 -0
  19. data/lib/visionbundles/helpers/templates.rb +19 -0
  20. data/lib/visionbundles/rails.rb +7 -0
  21. data/lib/visionbundles/recipes/db.rb +20 -34
  22. data/lib/visionbundles/recipes/dev.rb +0 -2
  23. data/lib/visionbundles/recipes/fast_assets.rb +25 -3
  24. data/lib/visionbundles/recipes/nginx.rb +3 -1
  25. data/lib/visionbundles/recipes/preconfig.rb +69 -0
  26. data/lib/visionbundles/recipes/preconfig/helpers.rb +28 -0
  27. data/lib/visionbundles/recipes/puma.rb +7 -8
  28. data/lib/visionbundles/recipes/secret.rb +14 -37
  29. data/lib/visionbundles/recipes/valid.rb +52 -0
  30. data/lib/visionbundles/templates/puma/config.erb +9 -7
  31. data/lib/visionbundles/version.rb +1 -1
  32. data/todo.md +15 -0
  33. data/visionbundles.gemspec +3 -2
  34. metadata +38 -5
  35. 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
@@ -1,2 +1,3 @@
1
1
  require "visionbundles/version"
2
- require "visionbundles/base"
2
+ require "visionbundles/helpers" if defined?(Capistrano)
3
+ require 'visionbundles/rails' if defined?(Rails)
@@ -6,5 +6,9 @@ module AssetSync
6
6
  def log_silently
7
7
  false
8
8
  end
9
+
10
+ def yml_exists?
11
+ false
12
+ end
9
13
  end
10
14
  end
@@ -0,0 +1,2 @@
1
+ require 'visionbundles/recipes/valid'
2
+ Dir.glob("#{File.dirname(__FILE__)}/helpers/*.rb").each { |file| require file }
@@ -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,10 @@
1
+ module Visionbundles
2
+ module Helpers
3
+ module Git
4
+ def file_in_source_control?(file_path)
5
+ `git ls-files #{file_path}` != ''
6
+ end
7
+ end
8
+ end
9
+ end
10
+ include Visionbundles::Helpers::Git
@@ -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
@@ -0,0 +1,7 @@
1
+ module Visionbundles
2
+ class Engine < Rails::Engine
3
+ generators do
4
+ require "generators/install_generator"
5
+ end
6
+ end
7
+ end
@@ -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
- namespace :db do
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
- desc "setup database configuration for application server"
13
- task :setup, roles: [:app, :web] do
14
- # It will check if shared database config exists will not over write
15
- mkdir("#{shared_path}/config")
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
- desc 'reset database config file from local'
35
- task :reset_config do
36
- copy_production_config
37
- overwrite_config!('database.yml')
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
- end
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
- end
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 "templates/nginx/nginx.conf.erb", "#{shared_path}/nginx", "vhost.conf"
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