mina 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/.rspec +1 -0
- data/.travis.yml +13 -0
- data/Gemfile +10 -0
- data/HISTORY.md +148 -0
- data/LICENSE +23 -0
- data/README.md +620 -0
- data/Rakefile +33 -0
- data/bin/mina +49 -0
- data/data/deploy.rb +30 -0
- data/data/deploy.sh.erb +98 -0
- data/lib/mina.rb +20 -0
- data/lib/mina/bundler.rb +23 -0
- data/lib/mina/default.rb +97 -0
- data/lib/mina/deploy.rb +57 -0
- data/lib/mina/deploy_helpers.rb +25 -0
- data/lib/mina/git.rb +14 -0
- data/lib/mina/helpers.rb +278 -0
- data/lib/mina/rails.rb +69 -0
- data/lib/mina/rake.rb +6 -0
- data/lib/mina/settings.rb +32 -0
- data/lib/mina/tools.rb +14 -0
- data/lib/mina/version.rb +5 -0
- data/mina.gemspec +17 -0
- data/spec/command_helper.rb +52 -0
- data/spec/commands/command_spec.rb +65 -0
- data/spec/commands/deploy_spec.rb +36 -0
- data/spec/commands/outside_project_spec.rb +35 -0
- data/spec/commands/real_deploy_spec.rb +53 -0
- data/spec/commands/verbose_spec.rb +20 -0
- data/spec/dsl/queue_spec.rb +49 -0
- data/spec/dsl/settings_in_rake_spec.rb +42 -0
- data/spec/dsl/settings_spec.rb +60 -0
- data/spec/dsl/to_spec.rb +20 -0
- data/spec/spec_helper.rb +20 -0
- data/test_env/config/deploy.rb +54 -0
- metadata +117 -0
data/Rakefile
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
require 'bundler/gem_tasks'
|
3
|
+
|
4
|
+
# Do these:
|
5
|
+
#
|
6
|
+
# *do `gem install reacco`
|
7
|
+
# * install http://github.com/rstacruz/git-update-ghpages
|
8
|
+
|
9
|
+
ENV['github'] ||= 'nadarei/mina'
|
10
|
+
|
11
|
+
namespace :doc do
|
12
|
+
|
13
|
+
task :build do
|
14
|
+
cmd = "reacco --literate --toc --github #{ENV['github']}"
|
15
|
+
cmd << " --analytics=#{ENV['analytics_id']}" if ENV['analytics_id']
|
16
|
+
|
17
|
+
system cmd
|
18
|
+
raise "Failed" unless $?.to_i == 0
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
desc "Updates online documentation"
|
23
|
+
task :deploy => :build do
|
24
|
+
system "git update-ghpages #{ENV['github']} -i doc"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
task :spec do
|
29
|
+
system "rm Gemfile.lock; sh -c 'rake=0.8 bundle exec rspec'"
|
30
|
+
system "rm Gemfile.lock; sh -c 'rake=0.9 bundle exec rspec'"
|
31
|
+
end
|
32
|
+
|
33
|
+
task :default => :spec
|
data/bin/mina
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$:.unshift File.expand_path('../../lib', __FILE__)
|
3
|
+
|
4
|
+
require 'rubygems' unless Object.const_defined?(:Gem)
|
5
|
+
require 'mina'
|
6
|
+
require 'rake'
|
7
|
+
|
8
|
+
# Intercept: if invoked as 'mina --help', don't let it pass through Rake, or else
|
9
|
+
# we'll see the Rake help screen. Redirect it to 'mina help'.
|
10
|
+
if ARGV.delete('--help') || ARGV.delete('-h')
|
11
|
+
ARGV << 'help'
|
12
|
+
end
|
13
|
+
|
14
|
+
if ARGV.delete('--version') || ARGV.delete('-V')
|
15
|
+
puts "Mina, version v#{Mina.version}"
|
16
|
+
exit
|
17
|
+
end
|
18
|
+
|
19
|
+
if ARGV.delete('--simulate') || ARGV.delete('-S')
|
20
|
+
ENV['simulate'] = '1'
|
21
|
+
end
|
22
|
+
|
23
|
+
scope = self
|
24
|
+
|
25
|
+
Rake.application.instance_eval do
|
26
|
+
standard_exception_handling do
|
27
|
+
# Initialize Rake and make it think it's Mina.
|
28
|
+
init 'mina'
|
29
|
+
@rakefiles = ['Minafile', 'config/deploy.rb']
|
30
|
+
|
31
|
+
# Workaround: Rake 0.9+ doesn't record task descriptions unless it's needed.
|
32
|
+
# Need it for 'mina help'
|
33
|
+
if Rake::TaskManager.respond_to?(:record_task_metadata)
|
34
|
+
Rake::TaskManager.record_task_metadata = true
|
35
|
+
end
|
36
|
+
|
37
|
+
# Load the Mina Rake DSL.
|
38
|
+
require 'mina/rake'
|
39
|
+
|
40
|
+
# Allow running without a Rakefile
|
41
|
+
load_rakefile if have_rakefile
|
42
|
+
|
43
|
+
# Run tasks
|
44
|
+
top_level
|
45
|
+
|
46
|
+
scope.mina_cleanup! if top_level_tasks.any?
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
data/data/deploy.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'mina/bundler'
|
2
|
+
require 'mina/rails'
|
3
|
+
require 'mina/git'
|
4
|
+
|
5
|
+
# Basic settings:
|
6
|
+
# host - The hostname to SSH to
|
7
|
+
# deploy_to - Path to deploy into
|
8
|
+
# repository - Git repo to clone from (needed by mina/git)
|
9
|
+
# user - Username in the server to SSH to (optional)
|
10
|
+
|
11
|
+
set :host, 'foobar.com'
|
12
|
+
set :deploy_to, '/var/www/foobar.com'
|
13
|
+
set :repository, 'git://...'
|
14
|
+
# set :user, 'foobar.com'
|
15
|
+
|
16
|
+
desc "Deploys the current version to the server."
|
17
|
+
task :deploy do
|
18
|
+
deploy do
|
19
|
+
# Put things that will set up an empty directory into a fully set-up
|
20
|
+
# instance of your project.
|
21
|
+
invoke :'git:checkout'
|
22
|
+
invoke :'bundle:install'
|
23
|
+
invoke :'rails:db_migrate'
|
24
|
+
invoke :'rails:assets_precompile'
|
25
|
+
|
26
|
+
to :launch do
|
27
|
+
queue 'touch tmp/restart.txt'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/data/deploy.sh.erb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
<%
|
2
|
+
prepare = commands(:default).map { |s| "(\n\n#{indent 2, s}\n\n)" }.join(" && ")
|
3
|
+
launch = commands(:launch).map { |s| "(\n\n#{indent 2, s}\n\n)" }.join(" && ")
|
4
|
+
clean = commands(:clean).map { |s| "(\n\n#{indent 2, s}\n\n)" }.join(" && ")
|
5
|
+
%>
|
6
|
+
#!/usr/bin/env bash
|
7
|
+
|
8
|
+
# Go to the deploy path
|
9
|
+
cd "<%= deploy_to %>" || (
|
10
|
+
echo "=====> ERROR: not set up."
|
11
|
+
echo "The path '<%= deploy_to %>' is not accessible on the server."
|
12
|
+
echo "You may need to run 'mina setup' first."
|
13
|
+
false
|
14
|
+
) || exit 16
|
15
|
+
|
16
|
+
# Check lockfile
|
17
|
+
if [ -e "<%= lock_file %>" ]; then
|
18
|
+
echo "=====> ERROR: another deployment is ongoing."
|
19
|
+
echo "The file '<%= lock_file %>' was found."
|
20
|
+
echo "If no other deployment is ongoing, delete the file to continue."
|
21
|
+
exit 17
|
22
|
+
fi
|
23
|
+
|
24
|
+
# Determine $previous_path and other variables
|
25
|
+
[ -h "<%= current_path %>" ] && [ -d "<%= current_path %>" ] && previous_path=$(cd "<%= current_path %>" >/dev/null && pwd -LP)
|
26
|
+
build_path="./tmp/build-`date +%s`$RANDOM"
|
27
|
+
version=$((`cat "<%= deploy_to %>/last_version" 2>/dev/null`+1))
|
28
|
+
release_path="<%= releases_path %>/$version"
|
29
|
+
|
30
|
+
# Sanity check
|
31
|
+
if [ -e "$build_path" ]; then
|
32
|
+
echo "=====> ERROR: Path already exists"
|
33
|
+
exit 18
|
34
|
+
fi
|
35
|
+
|
36
|
+
# Bootstrap script (in deployer)
|
37
|
+
(
|
38
|
+
echo "-----> Creating a temporary build path"
|
39
|
+
<%= echo_cmd %[touch "#{lock_file}"] %> &&
|
40
|
+
<%= echo_cmd %[mkdir -p "$build_path"] %> &&
|
41
|
+
<%= echo_cmd %[cd "$build_path"] %> &&
|
42
|
+
(
|
43
|
+
<%= indent 4, (prepare.empty? ? "true" : prepare) %>
|
44
|
+
)
|
45
|
+
) &&
|
46
|
+
|
47
|
+
#
|
48
|
+
# Rename to the real release path, then symlink 'current'
|
49
|
+
(
|
50
|
+
echo "-----> Build finished"
|
51
|
+
echo "-----> Moving build to $release_path"
|
52
|
+
<%= echo_cmd %[mv "$build_path" "$release_path"] %> &&
|
53
|
+
|
54
|
+
echo "-----> Updating the <%= current_path %> symlink" &&
|
55
|
+
<%= echo_cmd %[ln -nfs "$release_path" "#{current_path}"] %>
|
56
|
+
) &&
|
57
|
+
|
58
|
+
# ============================
|
59
|
+
# === Start up serve => (in deployer)
|
60
|
+
(
|
61
|
+
echo "-----> Launching"
|
62
|
+
<%= echo_cmd %[cd "$release_path"] %>
|
63
|
+
<%= indent 2, (launch.empty? ? "true" : launch) %>
|
64
|
+
) &&
|
65
|
+
|
66
|
+
# ============================
|
67
|
+
# === Complete & unlock
|
68
|
+
(
|
69
|
+
rm -f "<%= lock_file %>"
|
70
|
+
echo "$version" > "./last_version"
|
71
|
+
echo "-----> Done. Deployed v$version"
|
72
|
+
) ||
|
73
|
+
|
74
|
+
# ============================
|
75
|
+
# === Failed deployment
|
76
|
+
(
|
77
|
+
echo "=====> ERROR: Deploy failed."
|
78
|
+
|
79
|
+
<%= indent 2, clean %>
|
80
|
+
|
81
|
+
echo "-----> Cleaning up build"
|
82
|
+
[ -e "$build_path" ] && (
|
83
|
+
<%= echo_cmd %[rm -rf "$build_path"] %>
|
84
|
+
)
|
85
|
+
[ -e "$release_path" ] && (
|
86
|
+
echo "Deleting release"
|
87
|
+
<%= echo_cmd %[rm -rf "$release_path"] %>
|
88
|
+
)
|
89
|
+
(
|
90
|
+
echo "Unlinking current"
|
91
|
+
[ -n "$previous_path" ] && <%= echo_cmd %[ln -nfs "$previous_path" "#{current_path}"] %>
|
92
|
+
)
|
93
|
+
|
94
|
+
# Unlock
|
95
|
+
<%= echo_cmd %[rm -f "#{lock_file}"] %>
|
96
|
+
echo "OK"
|
97
|
+
exit 8
|
98
|
+
)
|
data/lib/mina.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
module Mina
|
2
|
+
PREFIX = File.dirname(__FILE__)
|
3
|
+
ROOT = File.expand_path('../../', __FILE__)
|
4
|
+
|
5
|
+
require 'mina/version'
|
6
|
+
|
7
|
+
autoload :DeployHelpers, 'mina/deploy_helpers'
|
8
|
+
autoload :Helpers, 'mina/helpers'
|
9
|
+
autoload :Settings, 'mina/settings'
|
10
|
+
autoload :Tools, 'mina/tools'
|
11
|
+
|
12
|
+
Error = Class.new(Exception)
|
13
|
+
class Failed < Error
|
14
|
+
attr_accessor :exitstatus
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.root_path(*a)
|
18
|
+
File.join ROOT, *a
|
19
|
+
end
|
20
|
+
end
|
data/lib/mina/bundler.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
|
2
|
+
settings.bundle_path ||= './vendor/bundle'
|
3
|
+
settings.bundle_options ||= lambda { %{--without development:test --path "#{bundle_path}" --binstubs bin/ --deployment} }
|
4
|
+
|
5
|
+
namespace :bundle do
|
6
|
+
desc "Install gem dependencies using Bundler."
|
7
|
+
task :install do
|
8
|
+
if bundle_path.nil?
|
9
|
+
queue %{
|
10
|
+
echo "-----> Installing gem dependencies using Bundler"
|
11
|
+
#{echo_cmd %[bundle install #{bundle_options}]}
|
12
|
+
}
|
13
|
+
else
|
14
|
+
queue %{
|
15
|
+
echo "-----> Installing gem dependencies using Bundler"
|
16
|
+
#{echo_cmd %[mkdir -p "#{deploy_to}/#{shared_path}/bundle"]}
|
17
|
+
#{echo_cmd %[mkdir -p "#{File.dirname bundle_path}"]}
|
18
|
+
#{echo_cmd %[ln -s "#{deploy_to}/#{shared_path}/bundle" "#{bundle_path}"]}
|
19
|
+
#{echo_cmd %[bundle install #{bundle_options}]}
|
20
|
+
}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/mina/default.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
set_default :term_mode, nil
|
2
|
+
|
3
|
+
# Default tasks here
|
4
|
+
desc "Creates a sample config file."
|
5
|
+
task :init do
|
6
|
+
name = Rake.application.name
|
7
|
+
config_file = Rake.application.rakefile
|
8
|
+
|
9
|
+
unless config_file.to_s.empty?
|
10
|
+
error "You already have #{config_file}."
|
11
|
+
exit 8
|
12
|
+
end
|
13
|
+
|
14
|
+
require 'fileutils'
|
15
|
+
FileUtils.mkdir_p './config'
|
16
|
+
FileUtils.cp Mina.root_path('data/deploy.rb'), './config/deploy.rb'
|
17
|
+
|
18
|
+
puts 'Created deploy.rb.'
|
19
|
+
puts 'Edit it, then run `#{name} setup` after.'
|
20
|
+
end
|
21
|
+
|
22
|
+
task :default => :help
|
23
|
+
|
24
|
+
module HelpHelpers
|
25
|
+
def get_tasks(&blk)
|
26
|
+
Rake.application.tasks.select &blk
|
27
|
+
end
|
28
|
+
|
29
|
+
def print_tasks(tasks, width=nil)
|
30
|
+
name = Rake.application.name
|
31
|
+
|
32
|
+
width ||= tasks.map { |t| t.name_with_args.length }.max || 10
|
33
|
+
tasks.each do |t|
|
34
|
+
if t.comment
|
35
|
+
puts " #{name} %-#{width}s # %s" % [ t.name_with_args, t.comment ]
|
36
|
+
else
|
37
|
+
puts " #{name} %s" % [ t.name_with_args ]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
SYSTEM_TASKS = %w[help tasks init]
|
43
|
+
def system_tasks() get_tasks { |t| SYSTEM_TASKS.include? t.name }; end
|
44
|
+
def top_tasks() get_tasks { |t| ! t.name.include?(':') && t.comment && !system_tasks.include?(t) }; end
|
45
|
+
def sub_tasks() get_tasks { |t| t.name.include?(':') }; end
|
46
|
+
|
47
|
+
def show_task_help(options={})
|
48
|
+
puts "Basic usage:"
|
49
|
+
print_tasks system_tasks
|
50
|
+
|
51
|
+
if top_tasks.any?
|
52
|
+
puts "\nServer tasks:"
|
53
|
+
print_tasks top_tasks
|
54
|
+
end
|
55
|
+
|
56
|
+
if sub_tasks.any? && options[:full]
|
57
|
+
puts "\nMore tasks:"
|
58
|
+
print_tasks sub_tasks
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
extend HelpHelpers
|
64
|
+
|
65
|
+
desc "Show help."
|
66
|
+
task :help do
|
67
|
+
name = Rake.application.name
|
68
|
+
|
69
|
+
puts "#{name} - Really fast server deployment and automation tool\n\n"
|
70
|
+
puts "Options:"
|
71
|
+
|
72
|
+
opts = [
|
73
|
+
[ "-h, --help", "Show help" ],
|
74
|
+
[ "-V, --version", "Show program version" ],
|
75
|
+
[ "-v, --verbose", "Show commands as they happen" ],
|
76
|
+
[ "-S, --simulate", "Run in simulation mode" ],
|
77
|
+
[ "-t, --trace", "Show backtraces when errors occur" ]
|
78
|
+
]
|
79
|
+
opts.each { |args| puts " %-17s %s" % args }
|
80
|
+
puts ""
|
81
|
+
|
82
|
+
show_task_help
|
83
|
+
|
84
|
+
unless Rake.application.have_rakefile
|
85
|
+
puts ""
|
86
|
+
puts "Run this command in a project with a 'config/deploy.rb' file to see more options."
|
87
|
+
end
|
88
|
+
|
89
|
+
puts ""
|
90
|
+
puts "All of Rake's options are also available as '#{name}' options. See 'rake --help'"
|
91
|
+
puts "for more information."
|
92
|
+
end
|
93
|
+
|
94
|
+
desc "Show all tasks."
|
95
|
+
task :tasks do
|
96
|
+
show_task_help :full => true
|
97
|
+
end
|
data/lib/mina/deploy.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
set_default :releases_path, "releases"
|
2
|
+
set_default :shared_path, "shared"
|
3
|
+
set_default :current_path, "current"
|
4
|
+
set_default :lock_file, "deploy.lock"
|
5
|
+
|
6
|
+
namespace :deploy do
|
7
|
+
desc "Forces a deploy unlock."
|
8
|
+
task :force_unlock do
|
9
|
+
queue %{echo "-----> Unlocking"}
|
10
|
+
queue echo_cmd %{rm -f "#{deploy_to}/#{lock_file}"}
|
11
|
+
end
|
12
|
+
|
13
|
+
desc "Links paths set in :shared_paths."
|
14
|
+
task :link_shared_paths do
|
15
|
+
dirs = settings.shared_paths!.map { |file| File.dirname("./#{file}") }.uniq
|
16
|
+
|
17
|
+
cmds = dirs.map do |dir|
|
18
|
+
echo_cmd %{mkdir -p "#{dir}"}
|
19
|
+
end
|
20
|
+
|
21
|
+
cmds += shared_paths.map do |file|
|
22
|
+
echo_cmd %{ln -s "#{shared_path}/#{file}" "./#{file}"}
|
23
|
+
end
|
24
|
+
|
25
|
+
queue %{
|
26
|
+
echo "-----> Symlinking shared paths"
|
27
|
+
#{cmds.join(" &&\n")}
|
28
|
+
}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
desc "Sets up a site."
|
33
|
+
task :setup do
|
34
|
+
settings.deploy_to!
|
35
|
+
|
36
|
+
queue %{echo "-----> Setting up #{deploy_to}"}
|
37
|
+
queue echo_cmd %{mkdir -p "#{deploy_to}"}
|
38
|
+
queue echo_cmd %{chown -R `whoami` "#{deploy_to}"}
|
39
|
+
queue echo_cmd %{chmod g+rx,u+rwx "#{deploy_to}"}
|
40
|
+
queue echo_cmd %{cd "#{deploy_to}"}
|
41
|
+
queue echo_cmd %{mkdir -p "#{releases_path}"}
|
42
|
+
queue echo_cmd %{chmod g+rx,u+rwx "#{releases_path}"}
|
43
|
+
queue echo_cmd %{mkdir -p "#{shared_path}"}
|
44
|
+
queue echo_cmd %{chmod g+rx,u+rwx "#{shared_path}"}
|
45
|
+
queue %{echo "-----> Done"}
|
46
|
+
end
|
47
|
+
|
48
|
+
desc "Runs a command in the server."
|
49
|
+
task :run, :command do |t, args|
|
50
|
+
command = args[:command]
|
51
|
+
unless command
|
52
|
+
puts %[You need to provide a command. Try: mina "run[ls -la]"]
|
53
|
+
exit 1
|
54
|
+
end
|
55
|
+
|
56
|
+
queue %[cd #{deploy_to!} && #{command}]
|
57
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Mina
|
2
|
+
module DeployHelpers
|
3
|
+
# Wraps the things inside it in a deploy script, and queues it.
|
4
|
+
# This generates a script using deploy_script and queues it.
|
5
|
+
def deploy(&blk)
|
6
|
+
queue deploy_script(&blk)
|
7
|
+
end
|
8
|
+
|
9
|
+
# Wraps the things inside it in a deploy script.
|
10
|
+
#
|
11
|
+
# script = deploy_script do
|
12
|
+
# invoke :'git:checkout'
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# queue script
|
16
|
+
#
|
17
|
+
def deploy_script(&blk)
|
18
|
+
set_default :term_mode, :pretty
|
19
|
+
code = isolate do
|
20
|
+
yield
|
21
|
+
erb Mina.root_path('data/deploy.sh.erb')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/mina/git.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
namespace :git do
|
2
|
+
desc "Clones the Git repository to the release path."
|
3
|
+
task :clone do
|
4
|
+
settings.revision ||= `git rev-parse HEAD`.strip
|
5
|
+
|
6
|
+
queue %{
|
7
|
+
echo "-----> Cloning the Git repository"
|
8
|
+
#{echo_cmd %[git clone "#{repository!}" . -n --recursive]} &&
|
9
|
+
echo "-----> Using revision #{revision}" &&
|
10
|
+
#{echo_cmd %[git checkout "#{revision}" -b current_release 1>/dev/null]} &&
|
11
|
+
#{echo_cmd %[rm -rf .git]}
|
12
|
+
}
|
13
|
+
end
|
14
|
+
end
|