Create a gist now

Instantly share code, notes, and snippets.

Deployer thing

Super cheapo Capistrano replacement. Inspired by github.com/cyx/tell.
This is more of a proof-of-concept than production-grade code... be sure you scrutinize it before you use it.

Contents:

  • deploy.yml (the config file)
  • deploy.rb (the deployer itself)

Usage

./deploy.rb production    # Deploys to production servers

Example output

$ ./deploy.rb production

server1.myapp.com$ git pull
  ...done.

server1.myapp.com$ rake cdn:propogate
  Sending new assets to Akamai...
  done.

server1.myapp.com$ thin restart
  Restarting thin.

server2.myapp.com$ git pull
  ...
  ...
  ...etc
# This is the config file describing the servers.
#
# This describes a cluster that looks like this:
#
# - 3 production servers (server1.myapp.com ... server3.myapp.com)
# - 1 development server (dev.myapp.com)
# - 1 test server (test.myapp.com)
#
# With this setup, you can:
#
# ./deploy.rb production # Deploys to server1 to server3.myapp.com
# ./deploy.rb test # Deploys to test.myapp.com
#
production.1: &production
host: server1.myapp.com
deploy:
- "git pull"
- "rake cdn:propogate"
- "sudo thin restart"
# Notice how these take advantage of YAML's inheritance
production.2:
<<: *production
host: server2.myapp.com
production.3:
<<: *production
host: server2.myapp.com
development: &dev
host: dev.myapp.com
deploy:
- "git pull"
- "touch tmp/restart.txt"
test:
<<: *dev
host: test.myapp.com
identity: id_rsa.pub
login: rsc
# ^-- not supported, but support for these can easily be added in as
# an exercise to the reader.
#!/usr/bin/env ruby
# The deployer class.
#
# Basic usage:
#
# d = Deployer.new yaml_filename
# d.run!('production') # deploys to servers matching production*
#
class Deployer
include Process
def initialize(file='deploy.yml')
require 'yaml'
@data = YAML::load_file(file)
end
# Deploys to given hosts matching /to/.
def run!(to, options={})
select(to).each do |(key, data)|
host = data['host'] || key
fork?(options[:fork]) {
data['deploy'].each { |cmd| tell host, cmd }
}
end
wait if options[:fork]
end
# Forks if the given parameter is truthy; else, runs as is.
def fork?(fork_it)
fork_it ? fork { yield } : yield
end
# Tells a server `host` to do a command `cmd`.
def tell(host, cmd)
log host, cmd
system "ssh #{host} -- #{cmd}"
end
# Logs something to stdout.
def log(host, cmd)
c1 = "\033[0;30m"
c2 = "\033[0m"
puts "\n#{c1}%s$#{c2} %s" % [ host, cmd ]
end
# Returns servers matching the spec given in `to`.
def select(to)
@data.select { |(key, data)| key.match(/^#{to}/) }
end
end
if $0 == __FILE__
if ARGV.size < 0
$stderr << "Usage: $0 <to>\n"
exit
end
Deployer.new.run! ARGV[0]
# Yes if you've noticed: you can add `, :fork => true` to the end of
# the previous line to do the deployments in parallel.
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment