-
-
Save ChandleWEi/cca4a8b236ad0cbc3ea6665149ca6881 to your computer and use it in GitHub Desktop.
Mina deployment file to setup new host for Rails applications. Creates the folder structure, fill up the database.yml file, create the associated DB and user and set up new Apache virtual host file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'mina/bundler' | |
require 'mina/rails' | |
require 'mina/git' | |
require 'mina/rvm' | |
# Usually mina focuses on deploying to one host and the deploy options are therefore simple. | |
# In our case, there is a number of possible servers to deploy to, it is therefore necessary to | |
# specify the host that we are targeting. | |
server = ENV['server'] | |
# Since the same host can have multiple applications running in parallel, it is necessary to | |
# specify further which application we want to deploy | |
version = ENV['version'] | |
# Set the repository (here on BitBucket) | |
set :repository, '[email protected]:username/project.git' | |
# setting the term_mode to system disable the "pretty-print" but prevent some other issues | |
set :term_mode, :system | |
# Manually create these paths in shared/ (eg: shared/config/database.yml) in your server. | |
# They will be linked in the 'deploy:link_shared_paths' step. | |
set :shared_paths, ['config/database.yml', 'log'] | |
# Optional SSH settings: | |
# SSH forward agent to ensure that credentials are passed through for git operations | |
set :forward_agent, true | |
########################################################################## | |
# | |
# Setup environment | |
# | |
########################################################################## | |
# This task is the environment that is loaded for most commands, such as | |
# `mina deploy` or `mina rake`. | |
task :environment do | |
# Ensure that a server has been set | |
unless server | |
print_error "A server needs to be specified." | |
exit | |
end | |
# Remote application folder | |
set :deploy_to, "/home/username/project/#{version}" | |
# Set the basic environment variables based on the server and version | |
case server | |
when 'qa' | |
# The hostname to SSH to | |
set :domain, 'qa-domain.com' | |
# SSH Optional settings | |
set :user, 'foobar' # Username in the server to SSH to. | |
# set :port, '30000' # SSH port number. | |
# Rails environment | |
set :rails_env, 'qa' | |
when 'prod' | |
# The hostname to SSH to | |
set :domain, 'prod-domain.com' | |
# SSH Optional settings | |
set :user, 'foobar' # Username in the server to SSH to. | |
# set :port, '30000' # SSH port number. | |
# Rails environment | |
set :rails_env, 'production' | |
end | |
# For those using RVM, use this to load an RVM version@gemset. | |
invoke :'rvm:use[ruby-1.9.3-p327@project]' | |
end | |
########################################################################## | |
# | |
# Create new host tasks | |
# Tasks below are related to deploying a new version of the application | |
# | |
########################################################################## | |
# Function extracted from http://blog.nicolai86.eu/posts/2013-05-06/syncing-database-content-down-with-mina | |
# allowing to read the content of the database.yml file | |
RYAML = <<-BASH | |
function ryaml { | |
ruby -ryaml -e 'puts ARGV[1..-1].inject(YAML.load(File.read(ARGV[0]))) {|acc, key| acc[key] }' "$@" | |
}; | |
BASH | |
# Execute all setup tasks defined below | |
desc "Create new folder structure + database.yml + DB + VirtualHost" | |
task :'setup:all' => :environment do | |
queue! %[echo "-----> Setup folder structure on server"] | |
invoke :setup | |
queue! %[echo "-----> Setup the DB (create user / DB)"] | |
invoke :'setup:db' | |
queue! %[echo "-----> Setup Apache VirtualHost Configuration"] | |
invoke :'setup:apache' | |
queue! %[echo "-----> Deploy Master for this version"] | |
invoke :deploy | |
queue! %[echo "-----> Enable Apache host and restart Apache"] | |
invoke :'apache:enable' | |
end | |
# Put any custom mkdir's in here for when `mina setup` is ran. | |
# For Rails apps, we'll make some of the shared paths that are shared between | |
# all releases. | |
task :setup => :environment do | |
queue! %[mkdir -p "#{deploy_to}/shared/log"] | |
queue! %[chmod g+rx,u+rwx "#{deploy_to}/shared/log"] | |
queue! %[mkdir -p "#{deploy_to}/shared/config"] | |
queue! %[chmod g+rx,u+rwx "#{deploy_to}/shared/config"] | |
queue! %[touch "#{deploy_to}/shared/config/database.yml"] | |
queue %[echo "-----> Fill in information below to populate 'shared/config/database.yml'."] | |
invoke :'setup:db:database_yml' | |
end | |
# Populate file database.yml with the appropriate rails_env | |
# Database name and user name are based on convention | |
# Password is defined by the user during setup | |
desc "Populate database.yml" | |
task :'setup:db:database_yml' => :environment do | |
puts "Enter a name for the new database" | |
db_name = STDIN.gets.chomp | |
puts "Enter a user for the new database" | |
db_username = STDIN.gets.chomp | |
puts "Enter a password for the new database" | |
db_pass = STDIN.gets.chomp | |
# Virtual Host configuration file | |
database_yml = <<-DATABASE.dedent | |
#{rails_env}: | |
adapter: mysql2 | |
encoding: utf8 | |
database: #{db_name} | |
username: #{db_username} | |
password: #{db_pass} | |
host: localhost | |
timeout: 5000 | |
DATABASE | |
queue! %{ | |
echo "-----> Populating database.yml" | |
echo "#{database_yml}" > #{deploy_to!}/shared/config/database.yml | |
echo "-----> Done" | |
} | |
end | |
# Create the new database based on information from database.yml | |
# In this application DB, user is given full access to the new DB | |
desc "Create new database" | |
task :'setup:db' => :environment do | |
queue! %{ | |
echo "-----> Import RYAML function" | |
#{RYAML} | |
echo "-----> Read database.yml" | |
USERNAME=$(ryaml #{deploy_to!}/#{shared_path!}/config/database.yml #{rails_env} username) | |
PASSWORD=$(ryaml #{deploy_to!}/#{shared_path!}/config/database.yml #{rails_env} password) | |
DATABASE=$(ryaml #{deploy_to!}/#{shared_path!}/config/database.yml #{rails_env} database) | |
echo "-----> Create SQL query" | |
Q1="CREATE DATABASE IF NOT EXISTS $DATABASE;" | |
Q2="GRANT USAGE ON *.* TO $USERNAME@localhost IDENTIFIED BY '$PASSWORD';" | |
Q3="GRANT ALL PRIVILEGES ON $DATABASE.* TO $USERNAME@localhost;" | |
Q4="FLUSH PRIVILEGES;" | |
SQL="${Q1}${Q2}${Q3}${Q4}" | |
echo "-----> Execute SQL query to create DB and user" | |
echo "-----> Enter MySQL root password on prompt below" | |
#{echo_cmd %[mysql -uroot -p -e "$SQL"]} | |
echo "-----> Done" | |
} | |
end | |
# Create a new VirtualHost file | |
# Server name is defined by convention | |
# Script executes some sudo operations | |
desc "Create Apache site file" | |
task :'setup:apache' => :environment do | |
# Get variable for virtual host configuration file | |
fqdn = get_fqdn(server, version) | |
fqdn_ext = external_fqdn(server, version) | |
# Virtual Host configuration file | |
vhost = <<-HOSTFILE.dedent | |
<VirtualHost *:80> | |
ServerAdmin [email protected] | |
ServerName #{get_fqdn(server, version)} | |
DocumentRoot #{deploy_to!}/#{current_path!}/public | |
RailsEnv #{rails_env} | |
<Directory #{deploy_to!}/#{current_path!}/public> | |
Options -MultiViews | |
AllowOverride all | |
</Directory> | |
PassengerMinInstances 5 | |
# Maintenance page | |
ErrorDocument 503 /503.html | |
RewriteEngine On | |
RewriteCond %{REQUEST_URI} !.(css|gif|jpg|png)$ | |
RewriteCond %{DOCUMENT_ROOT}/503.html -f | |
RewriteCond %{SCRIPT_FILENAME} !503.html | |
RewriteRule ^.*$ - [redirect=503,last] | |
</VirtualHost> | |
HOSTFILE | |
queue! %{ | |
echo "-----> Create Temporary Apache Virtual Host" | |
echo "#{vhost}" > #{fqdn}.tmp | |
echo "-----> Copy Virtual Host file to /etc/apache2/sites-available/ (requires sudo)" | |
#{echo_cmd %[sudo cp #{fqdn}.tmp /etc/apache2/sites-available/#{fqdn}]} | |
echo "-----> Remove Temporary Apache Virtual Host" | |
rm #{fqdn}.tmp | |
echo "-----> Done" | |
} | |
end | |
# Enable the new Virtual Host and restart Apache | |
desc "Enable new Apache host file" | |
task :'apache:enable' => :environment do | |
fqdn = get_fqdn(server, version) | |
queue! %{ | |
echo "-----> Enable Apache Virtual Host" | |
#{echo_cmd %[sudo a2ensite #{fqdn}]} | |
echo "-----> Remove Temporary Apache Virtual Host" | |
#{echo_cmd %[sudo service apache2 reload]} | |
} | |
end | |
########################################################################## | |
# | |
# Deployment related task | |
# | |
########################################################################## | |
desc "Deploys the current version to the server." | |
task :deploy => :environment do | |
deploy do | |
# Put things that will set up an empty directory into a fully set-up | |
# instance of your project. | |
invoke :'git:clone' | |
invoke :'deploy:link_shared_paths' | |
invoke :'bundle:install' | |
invoke :'rails:db_migrate' | |
invoke :'rails:assets_precompile:force' | |
to :launch do | |
queue "touch #{deploy_to}/#{current_path}/tmp/restart.txt" | |
end | |
end | |
end | |
######################################################################### | |
# | |
# Helper functions | |
# | |
########################################################################## | |
# | |
# Get the main domain based on the server | |
# | |
# @return [String] the main domain | |
def main_domain(server) | |
case server | |
when 'qa' | |
"qa-domain.com" | |
when 'prod' | |
"prod-domain.com" | |
end | |
end | |
# | |
# Fully Qualified Domain Name of the host | |
# Concatenation of the version and the domain name | |
# | |
# @return [String] the FQDN | |
def get_fqdn(server, version) | |
fqdn = "#{version}.#{main_domain(server)}" | |
return fqdn | |
end | |
######################################################################### | |
# | |
# Libraries | |
# | |
########################################################################## | |
# | |
# See https://github.com/cespare/ruby-dedent/blob/master/lib/dedent.rb | |
# | |
class String | |
def dedent | |
lines = split "\n" | |
return self if lines.empty? | |
indents = lines.map do |line| | |
line =~ /\S/ ? (line.start_with?(" ") ? line.match(/^ +/).offset(0)[1] : 0) : nil | |
end | |
min_indent = indents.compact.min | |
return self if min_indent.zero? | |
lines.map { |line| line =~ /\S/ ? line.gsub(/^ {#{min_indent}}/, "") : line }.join "\n" | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment