Skip to content
This repository
branch: master

Merge pull request #38 from jhalickman/master

Rename :public to :public_folder because of changes in sinatra
latest commit 702aa7a262
John Goulah jgoulah authored
Octocat-spinner-32 config Had default_stacks twice, typo fix in config/test.rb October 17, 2011
Octocat-spinner-32 helpers Merge pull request #18 from tomvachon/graphite_TCPSocket March 15, 2012
Octocat-spinner-32 lib Rename :public to :public_folder because of changes in sinatra October 30, 2013
Octocat-spinner-32 log initial import of deployinator July 29, 2011
Octocat-spinner-32 public initial import of deployinator July 29, 2011
Octocat-spinner-32 run_logs initial import of deployinator July 29, 2011
Octocat-spinner-32 stacks Issue #3 | Using open4 instead of open3 to track process exit status August 17, 2011
Octocat-spinner-32 templates Fixing up compare link on the deploy button to use the current_build … September 12, 2012
Octocat-spinner-32 test initial import of deployinator July 29, 2011
Octocat-spinner-32 tmp initial import of deployinator July 29, 2011
Octocat-spinner-32 views Some fixes, some patches, some modifications, this should fix general… September 29, 2011
Octocat-spinner-32 .gitignore ignore the .DS_Store file August 03, 2011
Octocat-spinner-32 .travis.yml Don't need the env vars for travis. August 11, 2011
Octocat-spinner-32 .yardopts documentation and shuffling July 31, 2011
Octocat-spinner-32 Gemfile Update bundler source to use https May 13, 2013
Octocat-spinner-32 Gemfile.lock Update bundler source to use https May 13, 2013
Octocat-spinner-32 LICENSE adding license file August 01, 2011
Octocat-spinner-32 README.md Note on stack naming rule July 04, 2013
Octocat-spinner-32 Rakefile Cargo culting is bad m'kay? August 11, 2011
Octocat-spinner-32 config.ru documentation and shuffling July 31, 2011
Octocat-spinner-32 deployinator.rb Some fixes, some patches, some modifications, this should fix general… September 29, 2011
Octocat-spinner-32 helpers.rb Merge pull request #28 from nkammah/fix_use_github_helper September 13, 2012
README.md
                  _________               ______                _____                 _____
                  ______  /_____ ________ ___  /______ _____  _____(_)_______ ______ ___  /_______ ________
                  _  __  / _  _ \___  __ \__  / _  __ \__  / / /__  / __  __ \_  __ `/_  __/_  __ \__  ___/
                  / /_/ /  /  __/__  /_/ /_  /  / /_/ /_  /_/ / _  /  _  / / // /_/ / / /_  / /_/ /_  / 
                  \__,_/   \___/ _  .___/ /_/   \____/ _\__, /  /_/   /_/ /_/ \__,_/  \__/  \____/ /_/ 
                                 /_/                   /____/             Deploy with style!

Deployinator - Deploy code like Etsy

Deployinator is a deployment framework extracted from Etsy. We've been using it since late 2009 / early 2010.

Here is a blog post explaining our rationale behind it and how it helps us out.

Stacks

Deployments are grouped by "stacks". You might have a "web" and "search" stack.

Each of those stacks might have different deployment environments, such as "staging" or "production".

You can map a button to each of these environments, to create multi-stage pushes within each stack.

Install

Deployinator is a standard Rack app (mostly Sinatra). Point your rack-capable web server at the config.ru, and it should mostly work. All dependencies are managed with bundler, so a bundle install should get your gems set up.

It has been tested with ruby 1.8.6, 1.8.7 and 1.9.2. For local development, you can use Pow!. Assuming you are using OSX, you'd want to do the following:

  • curl get.pow.cx | sh
  • cd ~/.pow
  • ln -s /path/to/deployinator ./deployinator
  • cd /path/to/deployinator
  • bundle install
  • echo "export HTTP_X_USERNAME=$USER\nexport HTTP_X_GROUPS=foo" > .powenv
  • visit deployinator.dev in your browser

If you are using RVM you may need to echo the ruby version like so:

  • echo "rvm ruby-1.9.3-head" > .rvmrc

You may want to tell Pow to restart before each request for development:

  • touch ./tmp/always_restart.txt

If you are not using Pow, you can of-course run it like a typical rack application.

  • bundle exec rackup

Authentication

At Etsy, we use an internal SSO similar to GodAuth, which sets http headers that are checked in deployinator.

This code is abstractable, and will be made more generic soon.

There are a list of urls that don't require authentication, those are currently defined at the top of helpers.rb but should be either set in a config, or maybe a sinatra plugin?

Creating a new stack

To create a new stack, run the rake task "new_stack"

STACK=my_blog rake new_stack

Note: a stack name must not begin with a capital letter

Customizing your stack

A stack can be customized so that you have flexibility over the different environments within it (which correspond to buttons) and the methods that correspond to each button press.

By default, you will see a button called "deploy stackname" where stackname is the STACK defined in the rake command above. In your stack file, you can add a function called stackname_environments that returns an array of hashes. Each hash will correspond to a new environment, or button. For example if your stack is called web, you can define a function like so to define qa and production environments within your web stack:

  def web_environments
    [
      {
        :name            => "qa",
        :method          => "qa_rsync",
        :current_version => qa_version,
        :current_build   => current_qa_build,
        :next_build      => next_qa_build
      },
      {
        :name            => "production",
        :method          => "prod_rsync",
        :current_version => prod_version,
        :current_build   => current_prod_build,
        :next_build      => next_prod_build
      }
    ]
  end

The keys of each hash describe what you will be pushing for that environment:

  • :name - name of the environment
  • :method - method name (string) that gets invoked when you press the button
  • :current_version - method that returns the version that is currently deployed in this environment
  • :current_build - method that returns the build that is currently deployed (usually inferred from the version)
  • :next_build - method that returns the next build that is about to be deployed

Configuration

Configuration settings live in config/*.rb. For example, your config/development.rb might look like this:

Deployinator.log_file = Deployinator.root(["log", "development.log"])
Deployinator.domain = "awesomeco.com"
Deployinator.hostname = "deployinator.dev"
Deployinator.graphite_host = "localhost"
Deployinator.github_host = "github.com"
Deployinator.default_user = "joboblee"
Deployinator.devbot_url = "http://botserver/bot/announce"

Deployinator.new_relic_options = {
  :apikey => "12345",
  :appid  => "4567"
}

Pony.options = {
  :via         => :smtp,
  :from        => "deployinator@#{Deployinator.domain}",
  :headers     => {"List-ID" => "deploy-announce"},
  :to          => "joboblee@#{Deployinator.domain}",
  :via_options => {
    :address              => 'smtp.gmail.com',
    :port                 => '587',
    :enable_starttls_auto => true,
    :user_name            => 'gmailuser@gmail.com',
    :password             => 'gmail-password',
    :authentication       => :plain,
    :domain               => Deployinator.domain
  }
}

Helpers

There are a few helpers built in that you can use after creating a new stack to assist you

run_cmd

Shell out to run a command line program. Includes timing information streams and logs the output of the command.

For example you could wrap your capistrano deploy:

run_cmd %Q{cap deploy}

log_and_stream

Output information to the log file, and the streaming output handler. The real time output console renders HTML so you should use markup here.

log_and_stream "starting deploy<br>"

log_and_shout

Output an announcement message with build related information.
Also includes hooks for Email and IRC.

log_and_shout({
    :old_build  => old_build,
    :build      => build,
    :send_email => true
});

The supported keys for log_and_shout are:

  • :env - the environment that is being pushed
  • :user - the user that pushed
  • :start - the start time of the push (if provided the command will log timing output)
  • :end - the end time of the push (defaults to "now")
  • :old_build - the existing version to be replaced
  • :build - the new version to be pushed
  • :irc_channels - comma separated list of IRC channels
  • :send_email - true if you want it to email the announcement (make sure to define settings in config)

IRC bot announcements

The IRC bot works by Deployinator issuing a POST request with the announcement to your internal web page, which should in turn initiate the bot communication. The page must accept at least a message parameter which deployinator populates.

For example you should define devbot in the config to be a url

http://mybot/announce

which accepts a POST request with the message key and channels key. The channels values are supplied from the :irc_channels parameter passed to log_and_shout. Its up to you to implement what happens with the message and channels POST request parameters, so you can plug in more than just IRC bot communication here.

Passenger Configuration

Prerequisite: Disable mod_deflate/mod_gzip on Apache

Passenger versions < 3.0.11

  • Standard configurations will work

Passenger versions >= 3.0.11

  • Set PassengerBufferResponse to off. This is a change as of 3.0.11 and will break streaming of the output.

Contributing

Once you've made your great commits (with tests!):

  1. Fork Deployinator
  2. Create a topic branch - git checkout -b my_branch
  3. Push to your branch - git push origin my_branch
  4. Create an Issue with a link to your branch

More Info

Come by #deployinator on Freenode. Mailing list coming soon!


Something went wrong with that request. Please try again.