popo 0.1.10 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +30 -2
- data/README.md +17 -0
- data/lib/popo.rb +1 -1
- data/lib/popo/constants.rb +16 -3
- data/lib/popo/error.rb +1 -1
- data/lib/popo/git_utils.rb +17 -15
- data/lib/popo/init.rb +8 -9
- data/lib/popo/opt_parse.rb +60 -0
- data/lib/popo/runner.rb +74 -90
- data/lib/popo/rvm.rb +18 -15
- data/lib/popo/sync.rb +22 -21
- data/lib/popo/utils.rb +33 -2
- data/lib/popo/version.rb +1 -1
- data/script/poporc +47 -30
- data/script/ps1_functions +162 -0
- metadata +12 -10
- data/README +0 -11
data/CHANGELOG
CHANGED
@@ -1,6 +1,34 @@
|
|
1
|
-
-- 0.
|
1
|
+
-- 1.0.0
|
2
|
+
* added popo update. this updates the manifest to respective branch and remigrates your
|
3
|
+
manifest data
|
4
|
+
|
5
|
+
* optparse separated
|
6
|
+
|
7
|
+
* interactive popo now displays colors for eye candy
|
8
|
+
|
9
|
+
* the runner is now passed to each command
|
10
|
+
|
11
|
+
* version bump
|
12
|
+
|
13
|
+
* a copy of ps1_functions scripts is now automatically sourced by poporc. if you're a fan of
|
14
|
+
changing your prompt with ps1_set, then you can call this inside popo.
|
15
|
+
remember that this will unset any PROMPT_COMMAND sourced before popo loads.
|
16
|
+
this means that any ps1_set or PROMPT_COMMAND defined and sourced in your local RVM will be gone.
|
17
|
+
this measure is to enforce prefixing your ps1 with the popo prompt to distinguish what world
|
18
|
+
you are in so that you will not be in limbo.
|
2
19
|
|
3
|
-
|
20
|
+
* ps1 functions in zsh are still not supported. sorry!
|
21
|
+
|
22
|
+
* for those who aren't familiar or interested with ps1_functions, you may visit
|
23
|
+
https://rvm.io/workflow/prompt/
|
24
|
+
|
25
|
+
* for CI, since colors render as pure text, you can export popo_no_color=true to your CI server.
|
26
|
+
this will disable the eye candy.
|
27
|
+
|
28
|
+
bug fixes
|
29
|
+
* changed some variable names for more readability
|
30
|
+
|
31
|
+
-- 0.1.10
|
4
32
|
* popo bash is now popo shell. bash still works though. just changed
|
5
33
|
underlying code to be more generic
|
6
34
|
|
data/README.md
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# Popo Repo Tool
|
2
|
+
Contain all of your related Rails applications into one single virtual system.
|
3
|
+
|
4
|
+
## Features:
|
5
|
+
* Customizable manifest
|
6
|
+
* Separate shell, rvm. No conflict worries
|
7
|
+
* Easy on resources (RAM and CPU)
|
8
|
+
* Integrated with cableguy to ease Rails configuration file deployment
|
9
|
+
* Is best used when deploying and working with multiple Rails applications
|
10
|
+
|
11
|
+
# HOWTO
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
> TODO
|
15
|
+
|
16
|
+
## Configuration
|
17
|
+
> TODO
|
data/lib/popo.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'palmade/cableguy'
|
3
|
-
require 'optparse'
|
4
3
|
require 'yaml'
|
5
4
|
require 'fileutils'
|
6
5
|
require File.join(File.dirname(__FILE__), 'popo/version')
|
@@ -13,6 +12,7 @@ module Popo
|
|
13
12
|
autoload :Error, File.join(POPO_LIB_ROOT, 'error')
|
14
13
|
autoload :GitUtils, File.join(POPO_LIB_ROOT, 'git_utils')
|
15
14
|
autoload :Init, File.join(POPO_LIB_ROOT, 'init')
|
15
|
+
autoload :OptParse, File.join(POPO_LIB_ROOT, 'opt_parse')
|
16
16
|
autoload :Runner, File.join(POPO_LIB_ROOT, 'runner')
|
17
17
|
autoload :RVM, File.join(POPO_LIB_ROOT, 'rvm')
|
18
18
|
autoload :Sync, File.join(POPO_LIB_ROOT, 'sync')
|
data/lib/popo/constants.rb
CHANGED
@@ -1,13 +1,26 @@
|
|
1
1
|
module Popo
|
2
2
|
module Constants
|
3
|
-
POPORC =
|
3
|
+
POPORC = "script/poporc"
|
4
4
|
DEFAULT_CONFIG_FILE = "popo_config.yml"
|
5
5
|
DEFAULT_POPO_TARGET = "development"
|
6
6
|
POPO_WORK_PATH = ".manifest"
|
7
7
|
POPO_YML_FILE = "popo.yml"
|
8
8
|
POPO_CONFIG = {}
|
9
9
|
POPO_DIR_KEY = "sync.directories"
|
10
|
-
POPO_KEY_VALUES =
|
11
|
-
POPO_COMMANDS =
|
10
|
+
POPO_KEY_VALUES = %w(branch repo)
|
11
|
+
POPO_COMMANDS = %w(sync rvm migrate diff info update)
|
12
|
+
REQUIRED_COMMANDS = %w(git env)
|
13
|
+
|
14
|
+
# colors
|
15
|
+
COLOR_RED = "\e[1;31m"
|
16
|
+
COLOR_GREEN = "\e[1;32m"
|
17
|
+
COLOR_YELLOW = "\e[1;33m"
|
18
|
+
COLOR_NONE = "\e[1;0m"
|
19
|
+
|
20
|
+
POST_INSTALL_NOTE = <<NOTE
|
21
|
+
You're almost done!\n
|
22
|
+
Do the following inside popo:\n
|
23
|
+
1. rvm reload\n
|
24
|
+
NOTE
|
12
25
|
end
|
13
26
|
end
|
data/lib/popo/error.rb
CHANGED
data/lib/popo/git_utils.rb
CHANGED
@@ -7,23 +7,23 @@ module Popo
|
|
7
7
|
branch = 'master' if branch.nil?
|
8
8
|
|
9
9
|
if clone_path.nil?
|
10
|
-
cmd = "#{GIT_CMD} clone -b #{branch} #{repo}"
|
10
|
+
cmd = "#{GIT_CMD} clone -b #{branch} #{repo} 2>&1"
|
11
11
|
else
|
12
|
-
cmd = "#{GIT_CMD} clone -b #{branch} #{repo} #{clone_path}"
|
12
|
+
cmd = "#{GIT_CMD} clone -b #{branch} #{repo} #{clone_path} 2>&1"
|
13
13
|
end
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
Utils.git_say_with_time "CLONE".green, nil do
|
16
|
+
Utils.say "#{'[Source]'.yellow} => #{repo}", true
|
17
|
+
Utils.say "#{'[Target]'.yellow} => #{clone_path}", true
|
18
|
+
Utils.say "#{'[Branch]'.yellow} => #{branch}", true
|
19
19
|
`#{cmd}`
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
23
|
def self.git_update(repo, branch)
|
24
|
-
Utils.
|
24
|
+
Utils.git_say_with_time "FETCH".green, "#{repo}" do
|
25
25
|
Dir.chdir(repo) do
|
26
|
-
`#{GIT_CMD} fetch`
|
26
|
+
`#{GIT_CMD} fetch 2>&1`
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
@@ -32,7 +32,7 @@ module Popo
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def self.git_stash(repo)
|
35
|
-
Utils.
|
35
|
+
Utils.git_say_with_time "STASH".yellow, "#{repo}" do
|
36
36
|
Dir.chdir(repo) do
|
37
37
|
`#{GIT_CMD} stash`
|
38
38
|
end
|
@@ -40,17 +40,19 @@ module Popo
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def self.git_reset(repo, branch)
|
43
|
-
Utils.
|
43
|
+
Utils.git_say_with_time "RESET".red, "#{repo}" do
|
44
44
|
Dir.chdir(repo) do
|
45
|
-
`#{GIT_CMD} reset --hard origin/#{branch}`
|
45
|
+
out = `#{GIT_CMD} reset --hard origin/#{branch} 2>&1`
|
46
|
+
Utils.say(out, true)
|
46
47
|
end
|
47
48
|
end
|
48
49
|
end
|
49
50
|
|
50
51
|
def self.git_checkout(repo, branch)
|
51
|
-
Utils.
|
52
|
+
Utils.git_say_with_time "CHECKOUT".yellow, "#{branch} branch" do
|
52
53
|
Dir.chdir(repo) do
|
53
|
-
`#{GIT_CMD} checkout #{branch}`
|
54
|
+
out = `#{GIT_CMD} checkout #{branch} 2>&1`
|
55
|
+
Utils.say(out, true)
|
54
56
|
end
|
55
57
|
end
|
56
58
|
end
|
@@ -62,14 +64,14 @@ module Popo
|
|
62
64
|
|
63
65
|
parsed = diff_msg.scan(/(commit [0-9a-f]+)\n+(.*?)\n+(.*?)(?:\n|$)/)
|
64
66
|
|
65
|
-
Utils.
|
67
|
+
Utils.git_say "#{File.basename(cwd).capitalize}"
|
66
68
|
|
67
69
|
parsed.each do |p|
|
68
70
|
commit_id = p[0].gsub(/commit/,'').strip
|
69
71
|
author = p[1].scan(/Author: (.*) <.*>/)
|
70
72
|
commit_msg = p[2].strip
|
71
73
|
|
72
|
-
Utils.
|
74
|
+
Utils.git_say "#{commit_id} \<#{author}\> #{commit_msg}", true
|
73
75
|
end
|
74
76
|
else
|
75
77
|
repos = Dir.entries(cwd) - [ '.', '..' ]
|
data/lib/popo/init.rb
CHANGED
@@ -2,17 +2,16 @@ module Popo
|
|
2
2
|
class Init
|
3
3
|
include Constants
|
4
4
|
|
5
|
-
def self.boot(
|
6
|
-
self.new(
|
5
|
+
def self.boot(runner)
|
6
|
+
self.new(runner)
|
7
7
|
end
|
8
8
|
|
9
|
-
def initialize(
|
10
|
-
@
|
11
|
-
@
|
12
|
-
@manifest = config['manifests'][@options[:manifest]]
|
13
|
-
@default_target = @options[:target] || 'development'
|
14
|
-
@deploy_path = File.absolute_path(@options[:path])
|
9
|
+
def initialize(runner)
|
10
|
+
@db = runner.database
|
11
|
+
@options = runner.options
|
15
12
|
@location = @options[:location] || nil
|
13
|
+
@manifest = runner.config['manifests'][@options[:manifest]]
|
14
|
+
@deploy_path = File.absolute_path(@options[:path])
|
16
15
|
end
|
17
16
|
|
18
17
|
def print_variables
|
@@ -43,7 +42,7 @@ module Popo
|
|
43
42
|
|
44
43
|
def write_config
|
45
44
|
popo = {}
|
46
|
-
popo['target'] =
|
45
|
+
popo['target'] = DEFAULT_POPO_TARGET
|
47
46
|
popo['path'] = @deploy_path
|
48
47
|
popo['location'] = @location if !@location.nil?
|
49
48
|
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module Popo
|
4
|
+
class OptParse
|
5
|
+
include Constants
|
6
|
+
|
7
|
+
attr_reader :optparse, :options
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@options = {}
|
11
|
+
@options[:verbose] = false
|
12
|
+
|
13
|
+
@optparse = OptionParser.new do |options|
|
14
|
+
options.banner = "Usage: popo COMMAND [options]"
|
15
|
+
options.separator ""
|
16
|
+
options.separator "Commands: #{POPO_COMMANDS.join(',')}"
|
17
|
+
options.separator ""
|
18
|
+
options.separator "options:"
|
19
|
+
|
20
|
+
options.on('-p', '--path PATH', 'Target path') do |path|
|
21
|
+
@options[:path] = path
|
22
|
+
end
|
23
|
+
|
24
|
+
options.on('-t', '--target TARGET', 'Deployment target') do |target|
|
25
|
+
@options[:target] = target
|
26
|
+
end
|
27
|
+
|
28
|
+
options.on('-u', '--user USER', 'Specify a username for git') do |user|
|
29
|
+
@options[:user] = user
|
30
|
+
end
|
31
|
+
|
32
|
+
options.on('-m', '--manifest MANIFEST', 'Specify a manifest repo') do |manifest|
|
33
|
+
@options[:manifest] = manifest
|
34
|
+
end
|
35
|
+
|
36
|
+
options.on('-l', '--location LOCATION', 'Set the deployment location') do |location|
|
37
|
+
@options[:location] = location
|
38
|
+
end
|
39
|
+
|
40
|
+
options.on('-r', '--reset', 'WARNING: It will not stash changes') do
|
41
|
+
@options[:reset] = true
|
42
|
+
end
|
43
|
+
|
44
|
+
options.on('-v', '--verbose', 'Prints more info about what you executed') do
|
45
|
+
@options[:verbose] = true
|
46
|
+
end
|
47
|
+
|
48
|
+
options.on('-V', '--version', 'Show popo\'s current version') do
|
49
|
+
puts Popo::VERSION
|
50
|
+
exit
|
51
|
+
end
|
52
|
+
|
53
|
+
options.on('-h', '--help', 'Display this screen') do
|
54
|
+
puts options
|
55
|
+
exit
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/popo/runner.rb
CHANGED
@@ -2,96 +2,67 @@ module Popo
|
|
2
2
|
class Runner
|
3
3
|
include Constants
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
5
|
+
attr_reader :config
|
6
|
+
attr_reader :database, :db_opts
|
7
|
+
attr_reader :app_root, :args, :options
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def boot(args)
|
11
|
+
Utils.colorize
|
12
|
+
self.new(args) if has_requirements?
|
12
13
|
end
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
def has_requirements?
|
16
|
+
if ENV.include?('SHELL')
|
17
|
+
Popo::Constants.const_set("SHELL", ENV['SHELL'])
|
18
|
+
else
|
19
|
+
Error.say("SHELL is empty.")
|
20
|
+
end
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
+
Popo::Constants::REQUIRED_COMMANDS.each do |b|
|
23
|
+
const_fn = "#{b.upcase}_CMD"
|
22
24
|
|
23
|
-
|
25
|
+
Popo::Constants.const_set(const_fn, get_bin_path(b))
|
24
26
|
|
25
|
-
|
26
|
-
|
27
|
+
if Popo::Constants.const_get(const_fn).empty?
|
28
|
+
Error.say "#{b} is needed and is not found in PATH!"
|
29
|
+
end
|
27
30
|
end
|
31
|
+
|
32
|
+
true
|
28
33
|
end
|
29
|
-
end
|
30
34
|
|
31
|
-
|
32
|
-
|
35
|
+
def get_bin_path(bin)
|
36
|
+
`which #{bin}`.strip
|
37
|
+
end
|
33
38
|
end
|
34
39
|
|
35
40
|
def initialize(args)
|
36
|
-
@
|
37
|
-
@
|
38
|
-
@
|
39
|
-
@
|
41
|
+
@args = args
|
42
|
+
@db_opts = {}
|
43
|
+
@options = {}
|
44
|
+
@app_root = ENV['popo_path'] || Dir.pwd
|
40
45
|
|
41
46
|
if Utils.has_popo_config?(@app_root)
|
42
47
|
@options[:target] = POPO_CONFIG['target']
|
43
48
|
end
|
44
49
|
|
45
|
-
|
46
|
-
opts.banner = "Popo tool."
|
47
|
-
opts.separator "Options:"
|
48
|
-
|
49
|
-
opts.on('-p', '--path PATH', 'Target path') do |path|
|
50
|
-
@options[:path] = path
|
51
|
-
end
|
52
|
-
|
53
|
-
opts.on('-t', '--target TARGET', 'Deployment target') do |target|
|
54
|
-
@options[:target] = target
|
55
|
-
end
|
56
|
-
|
57
|
-
opts.on('-u', '--user USER', 'Username') do |user|
|
58
|
-
@options[:user] = user
|
59
|
-
end
|
60
|
-
|
61
|
-
opts.on('-m', '--manifest MANIFEST', 'Manifest') do |manifest|
|
62
|
-
@options[:manifest] = manifest
|
63
|
-
end
|
64
|
-
|
65
|
-
opts.on('-l', '--location LOCATION', 'Location') do |location|
|
66
|
-
@options[:location] = location
|
67
|
-
end
|
68
|
-
|
69
|
-
opts.on('-v', '--verbose', 'Verbose') do
|
70
|
-
@options[:verbose] = true
|
71
|
-
end
|
72
|
-
|
73
|
-
opts.on('-V', '--version', 'Version') do
|
74
|
-
puts Popo::VERSION
|
75
|
-
exit
|
76
|
-
end
|
50
|
+
opt = OptParse.new
|
77
51
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
end
|
82
|
-
end
|
52
|
+
if @args.length >= 1
|
53
|
+
opt.optparse.parse!
|
54
|
+
@options.merge!(opt.options)
|
83
55
|
|
84
|
-
|
56
|
+
db_options = {
|
57
|
+
:path => File.join(@app_root, POPO_WORK_PATH),
|
58
|
+
:target => ENV['CABLING_TARGET'] || @options[:target] || DEFAULT_POPO_TARGET,
|
59
|
+
:verbose => @options[:verbose],
|
60
|
+
:location => ENV['CABLING_LOCATION'] || @options[:location]
|
61
|
+
}
|
85
62
|
|
86
|
-
|
87
|
-
Utils.say optparse.help
|
88
|
-
else
|
89
|
-
@db_opts[:path] = File.join(@app_root, POPO_WORK_PATH)
|
90
|
-
@db_opts[:target] = ENV['CABLING_TARGET'] || @options[:target] || DEFAULT_POPO_TARGET
|
91
|
-
@db_opts[:verbose] = @options[:verbose]
|
92
|
-
@db_opts[:location] = ENV['CABLING_LOCATION'] || @options[:location]
|
63
|
+
@db_opts.merge!(db_options)
|
93
64
|
|
94
|
-
# set
|
65
|
+
# let set these so we can use them in migration files
|
95
66
|
Object.const_set("POPO_PATH", @app_root)
|
96
67
|
Object.const_set("POPO_USER", @options[:user])
|
97
68
|
Object.const_set("POPO_TARGET", @db_opts[:target])
|
@@ -100,16 +71,18 @@ module Popo
|
|
100
71
|
mandatory = [:path, :manifest]
|
101
72
|
mandatory = mandatory.select { |p| @options[p].nil? }
|
102
73
|
|
103
|
-
if args.first
|
104
|
-
Error.say "
|
74
|
+
if @args.first.eql? 'init' and !mandatory.empty?
|
75
|
+
Error.say "Required options: #{mandatory.join(', ')}"
|
105
76
|
end
|
106
77
|
|
107
|
-
|
78
|
+
run
|
79
|
+
else
|
80
|
+
Utils.say opt.optparse.help
|
108
81
|
end
|
109
82
|
end
|
110
83
|
|
111
|
-
def run
|
112
|
-
if POPO_COMMANDS.include?(args.first)
|
84
|
+
def run
|
85
|
+
if POPO_COMMANDS.include?(@args.first)
|
113
86
|
Utils.in_popo?(@app_root)
|
114
87
|
end
|
115
88
|
|
@@ -117,31 +90,44 @@ module Popo
|
|
117
90
|
|
118
91
|
case cmd
|
119
92
|
when 'init'
|
120
|
-
config = get_config!
|
93
|
+
@config = get_config!
|
121
94
|
|
122
|
-
if config['manifests'][@options[:manifest]].nil?
|
95
|
+
if @config['manifests'][@options[:manifest]].nil?
|
123
96
|
Error.say "\'#{@options[:manifest]}\' does not exist in #{DEFAULT_CONFIG_FILE}!"
|
124
97
|
end
|
125
98
|
|
126
99
|
if !File.exist?(File.join(@app_root, @options[:path]))
|
127
100
|
@db_opts[:path] = File.join(@app_root, @options[:path], POPO_WORK_PATH)
|
128
|
-
|
101
|
+
get_database
|
129
102
|
|
130
|
-
Init.boot(
|
103
|
+
Init.boot(self).setup
|
131
104
|
else
|
132
105
|
Error.say "Path \'#{@options[:path]}\' already exists!"
|
133
106
|
end
|
134
107
|
when 'sync'
|
135
|
-
|
108
|
+
get_database
|
136
109
|
|
137
|
-
Sync.new(
|
110
|
+
Sync.new(self).sync
|
138
111
|
when 'rvm'
|
139
|
-
|
112
|
+
get_database
|
113
|
+
|
114
|
+
RVM.new(self).setup
|
115
|
+
when 'update'
|
116
|
+
manifest_path = File.join(@app_root, POPO_WORK_PATH)
|
140
117
|
|
141
|
-
|
118
|
+
case POPO_TARGET
|
119
|
+
when DEFAULT_POPO_TARGET
|
120
|
+
GitUtils.git_update(manifest_path, DEFAULT_POPO_TARGET)
|
121
|
+
else
|
122
|
+
GitUtils.git_update(manifest_path, 'master')
|
123
|
+
end
|
124
|
+
|
125
|
+
get_database
|
126
|
+
@database.migrate_database
|
142
127
|
when 'migrate'
|
143
|
-
get_database
|
144
|
-
|
128
|
+
get_database
|
129
|
+
@database.migrate_database
|
130
|
+
when 'info'
|
145
131
|
Utils.say `cat #{File.join(@app_root, POPO_WORK_PATH, POPO_YML_FILE)}`
|
146
132
|
when 'shell', 'bash'
|
147
133
|
sh!
|
@@ -152,7 +138,7 @@ module Popo
|
|
152
138
|
end
|
153
139
|
end
|
154
140
|
|
155
|
-
|
141
|
+
private
|
156
142
|
|
157
143
|
def sh!
|
158
144
|
if Utils.has_popo_config?(@app_root)
|
@@ -184,10 +170,8 @@ module Popo
|
|
184
170
|
end
|
185
171
|
|
186
172
|
def get_database
|
187
|
-
database = Database.new(@app_root, @db_opts)
|
188
|
-
database.boot_database
|
189
|
-
|
190
|
-
database
|
173
|
+
@database = Database.new(@app_root, @db_opts)
|
174
|
+
@database.boot_database
|
191
175
|
end
|
192
176
|
|
193
177
|
def get_config!
|
data/lib/popo/rvm.rb
CHANGED
@@ -1,18 +1,28 @@
|
|
1
1
|
module Popo
|
2
2
|
class RVM
|
3
|
-
|
4
|
-
@db = db
|
5
|
-
@app_root = app_root
|
6
|
-
@rvm_bin = File.join(@app_root, 'rvm/bin/rvm')
|
7
|
-
@repos = args
|
3
|
+
include Constants
|
8
4
|
|
9
|
-
|
10
|
-
@
|
11
|
-
@
|
5
|
+
def initialize(runner)
|
6
|
+
@db = runner.database
|
7
|
+
@repos = runner.args
|
8
|
+
@rvm_bin = File.join(runner.app_root, 'rvm/bin/rvm')
|
9
|
+
@app_root = runner.app_root
|
12
10
|
|
13
11
|
if @db.has_key?("rvm.gems.source")
|
14
12
|
@default_gem_source = @db.get("rvm.gems.source")
|
15
13
|
end
|
14
|
+
|
15
|
+
get_rubies
|
16
|
+
set_rubies
|
17
|
+
end
|
18
|
+
|
19
|
+
def get_rubies
|
20
|
+
@rubies = @db.get("rvm.rubies").split(",")
|
21
|
+
end
|
22
|
+
|
23
|
+
def set_rubies
|
24
|
+
@default_ruby = @db.get("rvm.ruby.default")
|
25
|
+
@default_gems = @db.get_children("rvm.gems.default")
|
16
26
|
end
|
17
27
|
|
18
28
|
def setup
|
@@ -60,11 +70,4 @@ module Popo
|
|
60
70
|
end
|
61
71
|
end
|
62
72
|
end
|
63
|
-
|
64
|
-
POST_INSTALL_NOTE = <<NOTE
|
65
|
-
You're almost done!\n
|
66
|
-
Do the following inside popo:\n
|
67
|
-
1. rvm reload\n
|
68
|
-
NOTE
|
69
|
-
|
70
73
|
end
|
data/lib/popo/sync.rb
CHANGED
@@ -2,22 +2,23 @@ module Popo
|
|
2
2
|
class Sync
|
3
3
|
include Constants
|
4
4
|
|
5
|
-
def initialize(
|
6
|
-
@db
|
5
|
+
def initialize(runner)
|
6
|
+
@db = runner.database
|
7
|
+
@cwd = Dir.pwd
|
8
|
+
@info = {}
|
9
|
+
@projects = runner.args
|
10
|
+
@options = runner.options
|
7
11
|
@sync_list = @db.get(POPO_DIR_KEY).split(',')
|
8
|
-
@
|
9
|
-
@cwd = Dir.pwd
|
10
|
-
@projects = args
|
11
|
-
@info = {}
|
12
|
+
@app_root = runner.app_root
|
12
13
|
end
|
13
14
|
|
14
15
|
def sync
|
15
16
|
@projects.each do |project|
|
16
17
|
if project =~ /\//
|
17
|
-
@info[
|
18
|
-
@info[
|
18
|
+
@info[:key] = convert_to_key(project)
|
19
|
+
@info[:path] = File.join(@app_root, project)
|
19
20
|
|
20
|
-
get_values(@info[
|
21
|
+
get_values(@info[:key])
|
21
22
|
|
22
23
|
get_project
|
23
24
|
else
|
@@ -26,11 +27,11 @@ module Popo
|
|
26
27
|
end
|
27
28
|
|
28
29
|
if @projects.empty?
|
29
|
-
if @cwd.eql? @
|
30
|
+
if @cwd.eql? @app_root
|
30
31
|
@sync_list.each { |p| sync_all(p) }
|
31
32
|
else
|
32
33
|
key = @cwd.split('/')
|
33
|
-
key.shift(@
|
34
|
+
key.shift(@app_root.split('/').count)
|
34
35
|
|
35
36
|
sync_all(convert_to_key(key))
|
36
37
|
end
|
@@ -46,38 +47,38 @@ module Popo
|
|
46
47
|
end
|
47
48
|
|
48
49
|
children.each do |c|
|
49
|
-
@info[
|
50
|
+
@info[:key] = [project, c].join('.')
|
50
51
|
|
51
|
-
get_values(@info[
|
52
|
+
get_values(@info[:key])
|
52
53
|
|
53
|
-
@info[
|
54
|
+
@info[:path] = File.join(@app_root, project.gsub('.','/'), c)
|
54
55
|
|
55
56
|
get_project
|
56
57
|
end
|
57
58
|
else
|
58
59
|
get_values(project)
|
59
60
|
|
60
|
-
@info[
|
61
|
+
@info[:path] = File.join(@app_root, project.gsub('.','/'))
|
61
62
|
|
62
63
|
get_project
|
63
64
|
end
|
64
65
|
end
|
65
66
|
|
66
67
|
def get_project
|
67
|
-
if !File.exists?(@info[
|
68
|
-
GitUtils.git_clone(@info[
|
68
|
+
if !File.exists?(@info[:path])
|
69
|
+
GitUtils.git_clone(@info[:repo], @info[:path], @info[:branch])
|
69
70
|
else
|
70
|
-
if POPO_CONFIG['target'] == DEFAULT_POPO_TARGET
|
71
|
-
GitUtils.git_stash(@info[
|
71
|
+
if POPO_CONFIG['target'] == DEFAULT_POPO_TARGET and !@options[:reset]
|
72
|
+
GitUtils.git_stash(@info[:path])
|
72
73
|
end
|
73
74
|
|
74
|
-
GitUtils.git_update(@info[
|
75
|
+
GitUtils.git_update(@info[:path], @info[:branch])
|
75
76
|
end
|
76
77
|
end
|
77
78
|
|
78
79
|
def get_values(key)
|
79
80
|
POPO_KEY_VALUES.each do |v|
|
80
|
-
@info[v] = @db.get("#{key}.#{v}")
|
81
|
+
@info[v.to_sym] = @db.get("#{key}.#{v}")
|
81
82
|
end
|
82
83
|
end
|
83
84
|
|
data/lib/popo/utils.rb
CHANGED
@@ -4,8 +4,26 @@ module Popo
|
|
4
4
|
module Utils
|
5
5
|
include Constants
|
6
6
|
|
7
|
+
def self.colorize
|
8
|
+
String.class_eval do
|
9
|
+
include Popo::Constants
|
10
|
+
|
11
|
+
constants.grep(/^COLOR/).each do |c|
|
12
|
+
color = c.to_s.gsub('COLOR_','').downcase.to_sym
|
13
|
+
|
14
|
+
define_method color do
|
15
|
+
if !ENV['popo_no_color']
|
16
|
+
"#{Popo::Constants.const_get(c)}#{self}#{COLOR_NONE}"
|
17
|
+
else
|
18
|
+
self
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
7
25
|
def self.say(message, subitem = false)
|
8
|
-
puts "#{subitem ? "
|
26
|
+
puts "#{subitem ? " \->" : "--"} #{message}"
|
9
27
|
end
|
10
28
|
|
11
29
|
def self.say_with_time(message)
|
@@ -17,6 +35,20 @@ module Popo
|
|
17
35
|
result
|
18
36
|
end
|
19
37
|
|
38
|
+
def self.git_say(action, message, subitem = false)
|
39
|
+
puts "#{subitem ? "\r->" : "\[#{action}\]"} #{message}"
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.git_say_with_time(action, message)
|
43
|
+
git_say(action, message)
|
44
|
+
result = nil
|
45
|
+
time = Benchmark.measure { result = yield }
|
46
|
+
say "%.4fs" % time.real, :subitem
|
47
|
+
say("#{result} rows", :subitem) if result.is_a?(Integer)
|
48
|
+
result
|
49
|
+
end
|
50
|
+
|
51
|
+
|
20
52
|
def self.in_popo?(root_path)
|
21
53
|
if !ENV.include?('popo_path')
|
22
54
|
Error.say "You must be inside popo!"
|
@@ -46,4 +78,3 @@ module Popo
|
|
46
78
|
end
|
47
79
|
end
|
48
80
|
end
|
49
|
-
|
data/lib/popo/version.rb
CHANGED
data/script/poporc
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
#!/usr/bin/env bash
|
2
|
+
|
2
3
|
if [[ -z "$popo_path" ]] ; then
|
3
4
|
echo "FAIL Please set the popo_path environment var"
|
4
5
|
exit -1
|
@@ -9,62 +10,78 @@ if [[ -z "$popo_target" ]] ; then
|
|
9
10
|
exit -1
|
10
11
|
fi
|
11
12
|
|
12
|
-
|
13
|
-
NORMAL="\[\033[0;0m\]"
|
14
|
-
BASEPATH=`which basename`
|
15
|
-
BASENAME=`$BASEPATH $popo_path`
|
16
|
-
CUSTOMRC=$popo_path/.manifest/script/customrc
|
17
|
-
|
18
|
-
export rvm_prefix=$popo_path
|
13
|
+
# remove all rvm paths
|
19
14
|
unset $(env | awk -F= '/^rvm_/{print $1" "}')
|
20
|
-
|
21
|
-
|
22
|
-
|
15
|
+
|
16
|
+
# variables
|
17
|
+
scriptdir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
18
|
+
basename=`which basename`
|
19
|
+
path_basename=`$basename $popo_path`
|
20
|
+
customrc=$popo_path/.manifest/script/customrc
|
21
|
+
|
22
|
+
# load self contained rvm
|
23
|
+
rvm_prefix=$popo_path
|
24
|
+
rvm_path=$popo_path/rvm
|
23
25
|
rubies_path=$rvm_path/rubies
|
24
26
|
|
25
|
-
#
|
27
|
+
# load all relative rvm scripts
|
28
|
+
source $rvm_path/scripts/rvm
|
29
|
+
|
30
|
+
# load default rvm if rubies folder is not empty
|
26
31
|
if [ -d $rubies_path ] && [ "$(ls -A $rubies_path)" ] ; then
|
27
32
|
rvm default
|
28
33
|
fi
|
29
34
|
|
30
|
-
unset PALMADE_GEMS_DIR
|
31
|
-
export PATH="$rvm_path/bin:$popo_path/tools:$PATH"
|
32
|
-
|
33
35
|
if [[ $PS1 != "" ]] ; then
|
34
36
|
if [[ -f /etc/bash.bashrc ]] ; then
|
35
37
|
source /etc/bash.bashrc
|
36
38
|
fi
|
37
39
|
|
40
|
+
if [[ -f /etc/bashrc ]] ; then
|
41
|
+
source /etc/bashrc
|
42
|
+
fi
|
43
|
+
|
38
44
|
if [[ -f $HOME/.bashrc ]] ; then
|
39
45
|
source $HOME/.bashrc
|
40
46
|
fi
|
41
47
|
|
42
|
-
if [[
|
43
|
-
|
48
|
+
if [[ `uname` == 'Darwin' && -f $HOME/.bash_profile ]] ; then
|
49
|
+
source $HOME/.bash_profile
|
50
|
+
fi
|
51
|
+
|
52
|
+
if [[ $path_basename != $popo_target ]] ; then
|
53
|
+
popo_ps1="\[\033[1;33m\][popo $popo_target/$path_basename] \[\033[0m\]"
|
44
54
|
else
|
45
|
-
|
55
|
+
popo_ps1="\[\033[0;33m\][popo $popo_target] \[\033[0m\]"
|
46
56
|
fi
|
57
|
+
|
58
|
+
PS1="$popo_ps1 $PS1"
|
47
59
|
fi
|
48
60
|
|
61
|
+
# lets unset PROMPT_COMMAND. We use our own PS1!
|
62
|
+
unset PROMPT_COMMAND
|
63
|
+
|
64
|
+
# source popo ps1 functions
|
65
|
+
source $scriptdir/ps1_functions
|
66
|
+
|
49
67
|
rvm reload
|
50
68
|
|
51
69
|
if [[ -z "$PS1" ]] ; then
|
52
70
|
rvm $*
|
53
71
|
else
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
echo ""
|
72
|
+
echo "Welcome to the popoed environment, where you can play with your very own popo."
|
73
|
+
echo ""
|
74
|
+
echo " popo_path: $popo_path"
|
75
|
+
echo " popo_target: $popo_target"
|
76
|
+
echo ""
|
77
|
+
echo "Remember to keep things clean and civil around here."
|
78
|
+
echo "To go back to your normal world, just hit exit."
|
79
|
+
echo ""
|
80
|
+
echo "But wait, you ask, who needs popo? Baahh, popo's for n00bs!"
|
81
|
+
echo ""
|
65
82
|
fi
|
66
83
|
|
67
84
|
# source a custom rc file
|
68
|
-
if [[ -s $
|
69
|
-
source $
|
85
|
+
if [[ -s $customrc ]] ; then
|
86
|
+
source $customrc
|
70
87
|
fi
|
@@ -0,0 +1,162 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
#
|
4
|
+
# Source this file in your ~/.bash_profile or interactive startup file.
|
5
|
+
# This is done like so:
|
6
|
+
#
|
7
|
+
# [[ -s "$HOME/.rvm/contrib/ps1_functions" ]] &&
|
8
|
+
# source "$HOME/.rvm/contrib/ps1_functions"
|
9
|
+
#
|
10
|
+
# Then in order to set your prompt you simply do the following for example
|
11
|
+
#
|
12
|
+
# Examples:
|
13
|
+
#
|
14
|
+
# ps1_set --prompt ∫
|
15
|
+
#
|
16
|
+
# or
|
17
|
+
#
|
18
|
+
# ps1_set --prompt ∴
|
19
|
+
#
|
20
|
+
# This will yield a prompt like the following, for example,
|
21
|
+
#
|
22
|
+
# 00:00:50 wayneeseguin@GeniusAir:~/projects/db0/rvm/rvm (git:master:156d0b4) ruby-1.8.7-p334@rvm
|
23
|
+
# ∴
|
24
|
+
#
|
25
|
+
ps1_titlebar()
|
26
|
+
{
|
27
|
+
case $TERM in
|
28
|
+
(xterm*|rxvt*)
|
29
|
+
printf "%s" "\033]0;\\u@\\h: \W\\007"
|
30
|
+
;;
|
31
|
+
esac
|
32
|
+
}
|
33
|
+
|
34
|
+
ps1_identity()
|
35
|
+
{
|
36
|
+
if (( $UID == 0 )) ; then
|
37
|
+
printf "%s" "\[\033[36m\]\\u\[\033[0m\]@\[\033[36m\]\\h\[\033[35m\]:\w\[\033[0m\] "
|
38
|
+
else
|
39
|
+
printf "%s" "\[\033[32m\]\\u\[\033[0m\]@\[\033[36m\]\\h\[\033[35m\]:\w\[\033[0m\] "
|
40
|
+
fi
|
41
|
+
}
|
42
|
+
|
43
|
+
ps1_git()
|
44
|
+
{
|
45
|
+
local branch="" sha1="" line="" attr="" color=0
|
46
|
+
|
47
|
+
shopt -s extglob # Important, for our nice matchers :)
|
48
|
+
|
49
|
+
command -v git >/dev/null 2>&1 || {
|
50
|
+
printf " \033[1;37m\033[41m[git not found]\033[m "
|
51
|
+
return 0
|
52
|
+
}
|
53
|
+
|
54
|
+
branch=$(git symbolic-ref -q HEAD 2>/dev/null) || return 0 # Not in git repo.
|
55
|
+
branch=${branch##refs/heads/}
|
56
|
+
|
57
|
+
# Now we display the branch.
|
58
|
+
sha1=$(git rev-parse --short --quiet HEAD)
|
59
|
+
|
60
|
+
case "${branch:-"(no branch)"}" in
|
61
|
+
production|prod) attr="1;37m\033[" ; color=41 ;; # red
|
62
|
+
master|deploy) color=31 ;; # red
|
63
|
+
stage|staging) color=33 ;; # yellow
|
64
|
+
dev|develop|development) color=34 ;; # blue
|
65
|
+
next) color=36 ;; # gray
|
66
|
+
*)
|
67
|
+
if [[ -n "${branch}" ]] ; then # Feature Branch :)
|
68
|
+
color=32 # green
|
69
|
+
else
|
70
|
+
color=0 # reset
|
71
|
+
fi
|
72
|
+
;;
|
73
|
+
esac
|
74
|
+
|
75
|
+
[[ $color -gt 0 ]] &&
|
76
|
+
printf "\[\033[${attr}${color}m\](git:${branch}$(ps1_git_status):$sha1)\[\033[0m\] "
|
77
|
+
}
|
78
|
+
|
79
|
+
ps1_git_status()
|
80
|
+
{
|
81
|
+
local git_status="$(git status 2>/dev/null)"
|
82
|
+
|
83
|
+
[[ "${git_status}" = *deleted* ]] && printf "%s" "-"
|
84
|
+
[[ "${git_status}" = *Untracked[[:space:]]files:* ]] && printf "%s" "+"
|
85
|
+
[[ "${git_status}" = *modified:* ]] && printf "%s" "*"
|
86
|
+
}
|
87
|
+
|
88
|
+
ps1_rvm()
|
89
|
+
{
|
90
|
+
command -v rvm-prompt >/dev/null 2>&1 && printf "%s" " $(rvm-prompt) "
|
91
|
+
}
|
92
|
+
|
93
|
+
ps1_update()
|
94
|
+
{
|
95
|
+
local prompt_char='$' separator="\n" notime=0
|
96
|
+
|
97
|
+
(( $UID == 0 )) && prompt_char='#'
|
98
|
+
|
99
|
+
while [[ $# -gt 0 ]] ; do
|
100
|
+
local token="$1" ; shift
|
101
|
+
|
102
|
+
case "$token" in
|
103
|
+
--trace)
|
104
|
+
export PS4="+ \${BASH_SOURCE##\${rvm_path:-}} : \${FUNCNAME[0]:+\${FUNCNAME[0]}()} \${LINENO} > "
|
105
|
+
set -o xtrace
|
106
|
+
;;
|
107
|
+
--prompt)
|
108
|
+
prompt_char="$1"
|
109
|
+
shift
|
110
|
+
;;
|
111
|
+
--noseparator)
|
112
|
+
separator=""
|
113
|
+
;;
|
114
|
+
--separator)
|
115
|
+
separator="$1"
|
116
|
+
shift
|
117
|
+
;;
|
118
|
+
--notime)
|
119
|
+
notime=1
|
120
|
+
;;
|
121
|
+
*)
|
122
|
+
true # Ignore everything else.
|
123
|
+
;;
|
124
|
+
esac
|
125
|
+
done
|
126
|
+
|
127
|
+
if (( notime > 0 )) ; then
|
128
|
+
PS1="$popo_ps1$(ps1_titlebar)$(ps1_identity)$(ps1_git)$(ps1_rvm)${separator}${prompt_char} "
|
129
|
+
else
|
130
|
+
PS1="$popo_ps1$(ps1_titlebar)\D{%H:%M:%S} $(ps1_identity)$(ps1_git)$(ps1_rvm)${separator}${prompt_char} "
|
131
|
+
fi
|
132
|
+
}
|
133
|
+
|
134
|
+
ps2_set()
|
135
|
+
{
|
136
|
+
PS2=" \[\033[0;40m\]\[\033[0;33m\]> \[\033[1;37m\]\[\033[1m\]"
|
137
|
+
}
|
138
|
+
|
139
|
+
ps4_set()
|
140
|
+
{
|
141
|
+
export PS4="+ \${BASH_SOURCE##\${rvm_path:-}} : \${FUNCNAME[0]:+\${FUNCNAME[0]}()} \${LINENO} > "
|
142
|
+
}
|
143
|
+
|
144
|
+
# WARNING: This clobbers your PROMPT_COMMAND so if you need to write your own, call
|
145
|
+
# ps1_update within your PROMPT_COMMAND with the same arguments you pass
|
146
|
+
# to ps1_set
|
147
|
+
#
|
148
|
+
# The PROMPT_COMMAND is used to help the prompt work if the separator is not a new line.
|
149
|
+
# In the event that the separtor is not a new line, the prompt line may become distored if
|
150
|
+
# you add or delete a certian number of characters, making the string wider than the
|
151
|
+
# $COLUMNS + len(your_input_line).
|
152
|
+
# This orginally was done with callbacks within the PS1 to add in things like the git
|
153
|
+
# commit, but this results in the PS1 being of an unknown width which results in the prompt
|
154
|
+
# being distored if you add or remove a certain number of characters. To work around this
|
155
|
+
# it now uses the PROMPT_COMMAND callback to re-set the PS1 with a known width of chracters
|
156
|
+
# each time a new command is entered. see PROMPT_COMMAND for more details.
|
157
|
+
#
|
158
|
+
ps1_set()
|
159
|
+
{
|
160
|
+
PROMPT_COMMAND="ps1_update $@"
|
161
|
+
}
|
162
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: popo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-05-07 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: sequel
|
16
|
-
requirement: &
|
16
|
+
requirement: &70203560628300 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70203560628300
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: sqlite3
|
27
|
-
requirement: &
|
27
|
+
requirement: &70203560627740 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70203560627740
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: cableguy
|
38
|
-
requirement: &
|
38
|
+
requirement: &70203560627200 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,7 +43,7 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70203560627200
|
47
47
|
description: Ruby and rails repo tool
|
48
48
|
email:
|
49
49
|
- poymode@gmail.com
|
@@ -56,7 +56,7 @@ files:
|
|
56
56
|
- .gitignore
|
57
57
|
- CHANGELOG
|
58
58
|
- Gemfile
|
59
|
-
- README
|
59
|
+
- README.md
|
60
60
|
- Rakefile
|
61
61
|
- bin/envy
|
62
62
|
- bin/popo
|
@@ -69,6 +69,7 @@ files:
|
|
69
69
|
- lib/popo/error.rb
|
70
70
|
- lib/popo/git_utils.rb
|
71
71
|
- lib/popo/init.rb
|
72
|
+
- lib/popo/opt_parse.rb
|
72
73
|
- lib/popo/runner.rb
|
73
74
|
- lib/popo/rvm.rb
|
74
75
|
- lib/popo/sync.rb
|
@@ -81,6 +82,7 @@ files:
|
|
81
82
|
- script/envy.template
|
82
83
|
- script/envyrc
|
83
84
|
- script/poporc
|
85
|
+
- script/ps1_functions
|
84
86
|
homepage: https://github.com/poymode/popo
|
85
87
|
licenses: []
|
86
88
|
post_install_message:
|
@@ -101,7 +103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
101
103
|
version: '0'
|
102
104
|
requirements: []
|
103
105
|
rubyforge_project:
|
104
|
-
rubygems_version: 1.8.
|
106
|
+
rubygems_version: 1.8.17
|
105
107
|
signing_key:
|
106
108
|
specification_version: 3
|
107
109
|
summary: Popo ruby and rails repo tool
|
data/README
DELETED
@@ -1,11 +0,0 @@
|
|
1
|
-
Popo Repo Tool
|
2
|
-
|
3
|
-
Deploy your rails app
|
4
|
-
|
5
|
-
Features:
|
6
|
-
- Customizable manifest
|
7
|
-
- Separate shell, rvm. No conflict worries
|
8
|
-
- Easy on resources (RAM and CPU)
|
9
|
-
- Integrated with cableguy to ease Rails configuration file deployment
|
10
|
-
- Is best used when deploying and working with multiple Rails applications
|
11
|
-
|