/mina

Really fast deployer and server automation tool.

Primary LanguageRubyOtherNOASSERTION

Mina

Really fast deployer and server automation tool.

Mina works really fast because it's a deploy Bash script generator. It generates an entire procedure as a Bash script and runs it remotely in the server.

Compare this to the likes of Vlad or Capistrano, where each command is ran separately on their own SSH sessions. Mina only creates one SSH session per deploy, minimizing the SSH connection overhead.

$ gem install mina
$ mina

Build Status Gem Version

User guide

Setting up a project

Let's deploy a project using Mina.

Step 1: Create a config/deploy.rb

In your project, type mina init to create a sample of this file.

$ mina init
Created config/deploy.rb.

This is just a Rake file with tasks! See About deploy.rb for more info on what deploy.rb is. You will want to at least configure your server:

# config/deploy.rb
set :user, 'username'
set :domain, 'your.server.com'
set :deploy_to, '/var/www/flipstack.com'
...

Step 2: Set up your server

Make a directory in your server called /var/www/flipstack.com (in deploy_to) change it's ownership to the correct user.

$ ssh username@your.server.com

# Once in your server, create the deploy folder:
~@your.server.com$ mkdir /var/www/flipstack.com
~@your.server.com$ chown -R username /var/www/flipstack.com

Step 3: Run 'mina setup'

Back at your computer, do mina setup to set up the folder structure in this path. This will connect to your server via SSH and create the right directories.

$ mina setup
-----> Creating folders... done.

See directory structure for more info.

Step 4: Deploy!

Use mina deploy to run the deploy task defined in config/deploy.rb.

$ mina deploy
-----> Deploying to 2012-06-12-040248
       ...
       Lots of things happening...
       ...
-----> Done.

About deploy.rb

The file deploy.rb is simply a Rakefile invoked by Rake. In fact, mina is mostly an alias that invokes Rake to load deploy.rb.

# Sample config/deploy.rb
set :domain, 'your.server.com'

task :restart do
  queue 'sudo service restart apache'
end

As it's all Rake, you can define tasks that you can invoke using mina. In this example, it provides the mina restart command.

The magic of Mina is in the new commands it gives you.

The queue command queues up Bash commands to be ran on the remote server. If you invoke mina restart, it will invoke the task above and run the queued commands on the remote server your.server.com via SSH.

See the command queue for more information on the queue command.

The command queue

At the heart of it, Mina is merely sugar on top of Rake to queue commands and execute them remotely at the end. Take a look at this minimal deploy.rb configuration:

# config/deploy.rb
set :user, 'john'
set :domain, 'flipstack.com'

task :logs do
  queue 'echo "Contents of the log file are as follows:"'
  queue "tail -f /var/log/apache.log"
end

Once you type mina logs in your terminal, it invokes the queued commands remotely on the server using the command ssh john@flipstack.com.

$ mina logs --simulate
# Execute the following commands via
# ssh john@flipstack.com:
#
echo "Contents of the log file are as follows:"
tail -f /var/log/apache.log

Subtasks

Mina provides the helper invoke to invoke other tasks from a task.

# config/deploy.rb
task :down do
  invoke :maintenance_on
  invoke :restart
end

task :maintenance_on
  queue 'touch maintenance.txt'
end

task :restart
  queue 'sudo service restart apache'
end

In this example above, if you type mina down, it simply invokes the other subtasks which queues up their commands. The commands will be ran after everything.

Directory structure

The deploy procedures make the assumption that you have a folder like so:

/var/www/flipstack.com/     # The deploy_to path
 |-  releases/              # Holds releases, one subdir per release
 |   |- 1/
 |   |- 2/
 |   |- 3/
 |   '- ...
 |-  shared/                # Holds files shared between releases
 |   |- logs/               # Log files are usually stored here
 |   `- ...
 '-  current/               # A symlink to the current release in releases/

It also assumes that the deploy_to path is fully writeable/readable for the user we're going to SSH with.

Deploying

Mina provides the deploy command which queues up a deploy script for you.

# config/deploy.rb
set :domain, 'flipstack.com'
set :user, 'flipstack'
set :deploy_to, '/var/www/flipstack.com'
set :repository, 'http://github.com/flipstack/flipstack.git'

task :deploy do
  deploy do
    # Put things that prepare the empty release folder here.
    # Commands queued here will be ran on a new release directory.
    invoke :'git:clone'
    invoke :'bundle:install'

    # These are instructions to start the app after it's been prepared.
    to :launch do
      queue 'touch tmp/restart.txt'
    end

    # This optional block defines how a broken release should be cleaned up.
    to :clean do
      queue 'log "failed deployment"'
    end
  end
end

It works by capturing the queued commands inside the block, wrapping them in a deploy script, then queueing them back in.

How deploying works

Here is an example of a deploy! (Note that some commands have been simplified to illustrate the point better.)

Step 1: Build it

The deploy process builds a new temp folder with instructions you provide. In this example, it will do git:clone and bundle:install.

$ mina deploy --verbose
-----> Creating the build path
       $ mkdir tmp/build-128293482394
-----> Cloning the Git repository
       $ git clone https://github.com/flipstack/flipstack.git . -n --recursive
       Cloning... done.
-----> Installing gem dependencies using Bundler
       $ bundle install --without development:test
       Using i18n (0.6.0)
       Using multi_json (1.0.4)
       ...
       Your bundle is complete! It was installed to ./vendor/bundle

Step 2: Move it to releases

Once the project has been built, it will be moved to releases/. A symlink called current/ will be created to point to the active release.

$
-----> Moving to releases/4
       $ mv "./tmp/build-128293482394" "releases/4"
-----> Symlinking to current
       $ ln -nfs releases/4 current

Step 3: Launch it

Invoke the commands queued up in the to :launch block. These often commands to restart the webserver process. Once this in complete, you're done!

$
-----> Launching
       $ cd releases/4
       $ sudo service nginx restart
-----> Done. Deployed v4

What about failure?

If it fails at any point, the release path will be deleted. If any commands are queued using the to :clean block, they will be ran. It will be as if nothing happened. Lets see what happens if a build fails:

$
-----> Launching
       $ cd releases/4
       $ sudo service nginx restart
       Starting nginx... error: can't start service
-----> ERROR: Deploy failed.
-----> Cleaning up build
       $ rm -rf tmp/build-128293482394
-----> Unlinking current
       $ ln -nfs releases/3 current
       OK

Command line options

Basic usage:

$ mina [OPTIONS] [TASKS] [VAR1=val VAR2=val ...]

Options

  • -v / --verbose - This will show commands being done on the server. Off by default.

  • -S / --simulate - This will not invoke any SSH connections; instead, it will simply output the script it builds.

  • -t / --trace - Show backtraces when errors occur.

  • -f FILE - Use a custom deploy.rb configuration.

  • -V / --version - Shows the current version.

Tasks

There are many tasks available. See the tasks reference, or type mina tasks.

Variables

You may specify additional variables in the KEY=value style, just like Rake. You can add as many variables as needed.

$ mina restart on=staging

# This sets the ENV['on'] variable to 'staging'.

Helpers

invoke

Invokes another Rake task.

Invokes the task given in task. Returns nothing.

invoke :'git:clone'
invoke :restart

Options: reenable (bool) - Execute the task even next time.

erb

Evaluates an ERB block in the current scope and returns a string.

a = 1
b = 2
# Assuming foo.erb is <%= a %> and <%= b %>
puts erb('foo.erb')
#=> "1 and 2"

Returns the output string of the ERB template.

run!

SSHs into the host and runs the code that has been queued.

This is already automatically invoked before Rake exits to run all commands that have been queued up.

queue "sudo restart"
run!

Returns nothing.

report_time

Report time elapsed in the block. Returns the output of the block.

report_time do
  sleep 2
  # do other things
end
# Output:
# Elapsed time: 2.00 seconds

measure

Measures the time (in seconds) a block takes. Returns a [time, output] tuple.

mina_cleanup

Internal: Invoked when Rake exits.

Returns nothing.

Errors

die

Exits with a nice looking message. Returns nothing.

die 2
die 2, "Tests failed"

error

Internal: Prints to stdout. Consider using print_error instead.

Queueing

queue

Queues code to be ran.

This queues code to be ran to the current code bucket (defaults to :default). To get the things that have been queued, use commands[:default]

Returns nothing.

queue "sudo restart"
queue "true"
commands == ['sudo restart', 'true']

queue!

Shortcut for queueing a command that shows up in verbose mode.

echo_cmd

Converts a bash command to a command that echoes before execution. Used to show commands in verbose mode. This does nothing unless verbose mode is on.

Returns a string of the compound bash command, typically in the format of echo xx && xx. However, if verbose_mode? is false, it returns the input string unharmed.

echo_cmd("ln -nfs releases/2 current")
#=> echo "$ ln -nfs releases/2 current" && ln -nfs releases/2 current

Commands

commands

Returns an array of queued code strings.

You may give an optional aspect.

Returns an array of strings.

queue "sudo restart"
queue "true"
to :clean do
  queue "rm"
end
commands == ["sudo restart", "true"]
commands(:clean) == ["rm"]

isolate

Starts a new block where new commands are collected.

Returns nothing.

queue "sudo restart"
queue "true"
commands.should == ['sudo restart', 'true']
isolate do
  queue "reload"
  commands.should == ['reload']
end
commands.should == ['sudo restart', 'true']

in_directory

Starts a new block where #commands are collected, to be executed inside path.

Returns nothing.

in_directory './webapp' do
  queue "./reload"
end
commands.should == ['cd ./webapp && (./reload && true)']

to

Defines instructions on how to do a certain thing. This makes the commands that are queued go into a different bucket in commands.

Returns nothing.

to :prepare do
  run "bundle install"
end
to :launch do
  run "nginx -s restart"
end
commands(:prepare) == ["bundle install"]
commands(:restart) == ["nginx -s restart"]

Settings helpers

set

Sets settings. Sets given symbol key to value in value.

Returns the value.

set :domain, 'kickflip.me'

set_default

Sets default settings. Sets given symbol key to value in value only if the key isn't set yet.

Returns the value.

set_default :term_mode, :pretty
set :term_mode, :system
settings.term_mode.should == :system
set :term_mode, :system
set_default :term_mode, :pretty
settings.term_mode.should == :system

settings

Accesses the settings hash.

set :domain, 'kickflip.me'
settings.domain  #=> 'kickflip.me'
domain           #=> 'kickflip.me'

method_missing

Hook to get settings. See #settings for an explanation.

Returns things.

Command line mode helpers

verbose_mode?

Checks if Rake was invoked with --verbose.

Returns true or false.

if verbose_mode?
  queue %[echo "-----> Starting a new process"]
end

simulate_mode?

Checks if Rake was invoked with --simulate.

Returns true or false.

Internal helpers

indent

Indents a given code block with count spaces before it.

unindent

Internal: Normalizes indentation on a given string.

Returns the normalized string without extraneous indentation.

puts unindent %{
  Hello
    There
}
# Output:
# Hello
#   There

reindent

Resets the indentation on a given code block.

capture

Returns the output of command via SSH.

Helpers: Deploy helpers

Helpers for deployment.

deploy

Wraps the things inside it in a deploy script and queues it. This generates a script using deploy_script and queues it.

Returns nothing.

deploy_script

Wraps the things inside it in a deploy script.

script = deploy_script do
  invoke :'git:checkout'
end
queue script

Returns the deploy script as a string, ready for queueing.

Modules: Bundler

Adds settings and tasks for managing Ruby Bundler.

require 'mina/bundler'

Settings

Any and all of these settings can be overriden in your deploy.rb.

bundle_bin

Sets the bundle path.

bundle_path

Sets the path to where the gems are expected to be.

This path will be symlinked to ./shared/bundle so that the gems cache will be shared between all releases.

bundle_options

Sets the options for installing gems via Bundler.

Deploy tasks

These tasks are meant to be invoked inside deploy scripts, not invoked on their own.

bundle:install

Installs gems.

Modules: Default

This module is loaded when invoking mina with or without a project.

Settings

Here are some of the common settings. All settings are optional unless otherwise noted.

deploy_to

(Required) Path to deploy to.

domain

(Required) Host name to deploy to.

port

SSH port number.

forward_agent

If set to true, enables SSH agent forwarding.

identity_file

The local path to the SSH private key file.

ssh_options

Switches to be passed to the ssh command.

Tasks

Any and all of these settings can be overriden in your deploy.rb.

environment

Make the :environment task exist by default. This is meant to be overridden by users.

init

Initializes a new Mina project.

$ mina init

help

Shows the help screen.

tasks

Display all tasks in a nice table.

$ mina tasks

Modules: Deployment

This module is automatically loaded for all Mina projects.

Settings

Any and all of these settings can be overriden in your deploy.rb.

releases_path

(default: 'releases')

shared_path

(default: 'shared')

current_path

(default: 'current_path')

lock_file

Name of the file to generate while a deploy is currently ongoing. (default: 'deploy.lock')

keep_releases

Number of releases to keep when doing the deploy:cleanup task. (default: 5)

Tasks

deploy:force_unlock

Forces a deploy unlock by deleting the lock file.

$ mina deploy:force_unlock

You can also combine that task with deploy:

$ mina deploy:force_unlock deploy

deploy:link_shared_paths

Links the shared paths in the shared_paths setting.

deploy:cleanup

Cleans up old releases.

By default, the last 5 releases are kept on each server (though you can change this with the keep_releases setting). All other deployed revisions are removed from the servers."

setup

Sets up a site's directory structure.

run[]

Runs a command on a server.

$ mina run[tail -f logs.txt]

Modules: Foreman

Adds settings and tasks for managing projects with foreman.

NOTE: Requires sudo privileges

require 'mina/foreman'

Common usage

set :application, "app-name"

task :deploy => :environment do

 deploy do
   # ...
   invoke 'foreman:export'
   # ...
 end
 to :launch do
   invoke 'foreman:restart'
 end

end

Settings

Any and all of these settings can be overriden in your deploy.rb.

foreman_app

Sets the service name that foreman will export to upstart. Uses application variable as a default. It should be set, otherwise export command will fail.

foreman_user

Sets the user under which foreman will execute the service. Defaults to user

foreman_log

Sets the foreman log path. Defaults to shared/log

encoding: utf-8

Modules: Git

Adds settings and tasks related to managing Git.

require 'mina/git'

Settings

Any and all of these settings can be overriden in your deploy.rb.

branch

Sets the branch to be deployed.

Deploy tasks

These tasks are meant to be invoked inside deploy scripts, not invoked on their own.

git:clone

Clones the Git repository. Meant to be used inside a deploy script.

Modules: Rails

Adds settings and tasks for managing Rails projects.

require 'mina/rails'

Settings

Any and all of these settings can be overriden in your deploy.rb.

rails_env

Sets the Rails environment for rake and rails commands.

Note that changing this will NOT change the environment that your application is ran in.

bundle_prefix

Prefix for Bundler commands. Often to something like RAILS_ENV=production bundle exec.

queue! "#{bundle_prefix} annotate -r"

rake

The prefix for rake commands. Use like so:

queue! "#{rake} db:migrate"

rails

The prefix for rails commands. Use like so:

queue! "#{rails} console"

asset_paths

The paths to be checked.

Whenever assets are compiled, the asset files are checked if they have changed from the previous release.

If they're unchanged, compiled assets will simply be copied over to the new release.

Override this if you have custom asset paths declared in your Rails's config.assets.paths setting.

rake_assets_precompile

The command to invoke when precompiling assets. Override me if you like.


Macro used later by :rails, :rake, etc

Command-line tasks

These tasks can be invoked in the command line.

rails[]

Invokes a rails command.

$ mina rails[console]

rake[]

Invokes a rake command.

$ mina rake db:cleanup

console

Opens the Ruby console for the currently-deployed version.

$ mina console

Deploy tasks

These tasks are meant to be invoked inside deploy scripts, not invoked on their own.

rails:db_migrate

rails:db_migrate:force

rails:assets_precompile:force

rails:assets_precompile

Modules: rbenv

Adds settings and tasks for managing rbenv installations.

require 'mina/rbenv'

Common usage

task :environment do
  invoke :'rbenv:load'
end
task :deploy => :environment do
  ...
end

Settings

Any and all of these settings can be overriden in your deploy.rb.

rbenv_path

Sets the path where rbenv is installed.

You may override this if rbenv is placed elsewhere in your setup.

Tasks

rbenv:load

Loads the rbenv runtime.

Modules: RVM

Adds settings and tasks for managing RVM installations.

require 'mina/rvm'

Common usage

task :environment do
  invoke :'rvm:use[ruby-1.9.3-p125@gemset_name]'
end
task :deploy => :environment do
  ...
end

Settings

Any and all of these settings can be overriden in your deploy.rb.

rvm_path

Sets the path to RVM.

You can override this in your projects if RVM is installed in a different path, say, if you have a system-wide RVM install.

Tasks

rvm:use[]

Uses a given RVM environment provided as an argument.

This is usually placed in the :environment task.

task :environment do
  invoke :'rvm:use[ruby-1.9.3-p125@gemset_name]'
end

rvm:wrapper[]

Creates a rvm wrapper for a given executable.

This is usually placed in the :setup task.

task ::setup => :environment do
  ...
  invoke :'rvm:wrapper[ruby-1.9.3-p125@gemset_name,wrapper_name,binary_name]'
end

Adds settings and tasks for managing projects with [whenever]. [whenever]: http://rubygems.org/gems/whenever

Acknowledgements

© 2012-2013, Nadarei. Released under the MIT License.

Mina is authored and maintained by Rico Sta. Cruz and Michael Galero with help from its contributors. It is sponsored by our startup, Nadarei.

Rico:

Michael: