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.
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