foreplay 0.7.6 → 0.8.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +28 -25
- data/.ruby-version +1 -1
- data/foreplay.gemspec +12 -14
- data/lib/foreplay/cli.rb +42 -43
- data/lib/foreplay/engine/remote.rb +121 -0
- data/lib/foreplay/engine/role.rb +23 -0
- data/lib/foreplay/engine/server.rb +123 -0
- data/lib/foreplay/engine/step.rb +52 -0
- data/lib/foreplay/engine/steps.yml +75 -0
- data/lib/foreplay/engine.rb +138 -0
- data/lib/foreplay/foreplay.rb +10 -0
- data/lib/foreplay/launcher.rb +13 -0
- data/lib/foreplay/setup.rb +24 -26
- data/lib/foreplay/version.rb +1 -1
- data/lib/foreplay.rb +7 -3
- data/lib/string.rb +17 -0
- data/spec/lib/foreplay/deploy_spec.rb +79 -24
- data/spec/lib/foreplay/engine_spec.rb +32 -0
- data/spec/lib/foreplay/setup_spec.rb +1 -1
- metadata +47 -60
- data/lib/foreplay/deploy.rb +0 -395
- data/lib/foreplay/utility.rb +0 -30
- data/spec/lib/foreplay/utility_spec.rb +0 -28
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
require 'yaml'
|
|
2
|
+
require 'string'
|
|
3
|
+
|
|
4
|
+
class Foreplay::Engine
|
|
5
|
+
include Foreplay
|
|
6
|
+
attr_reader :environment, :filters, :mode
|
|
7
|
+
|
|
8
|
+
DEFAULTS_KEY = 'defaults'
|
|
9
|
+
CONFIG_FILE = "#{Dir.getwd}/config/foreplay.yml"
|
|
10
|
+
|
|
11
|
+
def initialize(e, f)
|
|
12
|
+
@environment = e
|
|
13
|
+
@filters = f
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def deploy
|
|
17
|
+
@mode = :deploy
|
|
18
|
+
execute
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def check
|
|
22
|
+
@mode = :check
|
|
23
|
+
execute
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def execute
|
|
27
|
+
# Explain what we're going to do
|
|
28
|
+
puts "#{mode.capitalize}ing #{environment.dup.yellow} environment, "\
|
|
29
|
+
"#{explanatory_text(filters, 'role')}, #{explanatory_text(filters, 'server')}"
|
|
30
|
+
|
|
31
|
+
threads = []
|
|
32
|
+
|
|
33
|
+
roles.each do |role, additional_instructions|
|
|
34
|
+
next if role == DEFAULTS_KEY # 'defaults' is not a role
|
|
35
|
+
next if filters.key?('role') && filters['role'] != role
|
|
36
|
+
|
|
37
|
+
threads.concat Foreplay::Engine::Role.new(
|
|
38
|
+
environment,
|
|
39
|
+
mode,
|
|
40
|
+
build_instructions(role, additional_instructions)
|
|
41
|
+
).threads
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
threads.each(&:join)
|
|
45
|
+
|
|
46
|
+
puts mode == :deploy ? 'Finished deployment' : 'Deployment configuration check was successful'
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Returns a new hash with +hash+ and +other_hash+ merged recursively, including arrays.
|
|
50
|
+
#
|
|
51
|
+
# h1 = { x: { y: [4,5,6] }, z: [7,8,9] }
|
|
52
|
+
# h2 = { x: { y: [7,8,9] }, z: 'xyz' }
|
|
53
|
+
# h1.supermerge(h2)
|
|
54
|
+
# #=> {:x=>{:y=>[4, 5, 6, 7, 8, 9]}, :z=>[7, 8, 9, "xyz"]}
|
|
55
|
+
def supermerge(hash, other_hash)
|
|
56
|
+
fail 'supermerge only works if you pass two hashes. '\
|
|
57
|
+
"You passed a #{hash.class} and a #{other_hash.class}." unless hash.is_a?(Hash) && other_hash.is_a?(Hash)
|
|
58
|
+
|
|
59
|
+
new_hash = hash.deep_dup.with_indifferent_access
|
|
60
|
+
|
|
61
|
+
other_hash.each_pair do |k, v|
|
|
62
|
+
tv = new_hash[k]
|
|
63
|
+
|
|
64
|
+
if tv.is_a?(Hash) && v.is_a?(Hash)
|
|
65
|
+
new_hash[k] = supermerge(tv, v)
|
|
66
|
+
elsif tv.is_a?(Array) || v.is_a?(Array)
|
|
67
|
+
new_hash[k] = Array.wrap(tv) + Array.wrap(v)
|
|
68
|
+
else
|
|
69
|
+
new_hash[k] = v
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
new_hash
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
def explanatory_text(hsh, key)
|
|
79
|
+
hsh.key?(key) ? "#{hsh[key].dup.yellow} #{key}" : "all #{key.pluralize}"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def build_instructions(role, additional_instructions)
|
|
83
|
+
instructions = supermerge(defaults, additional_instructions).symbolize_keys
|
|
84
|
+
instructions[:role] = role
|
|
85
|
+
required_keys = [:name, :environment, :role, :servers, :path, :repository]
|
|
86
|
+
|
|
87
|
+
required_keys.each do |key|
|
|
88
|
+
next if instructions.key? key
|
|
89
|
+
terminate("Required key #{key} not found in instructions for #{environment} environment.\nCheck #{CONFIG_FILE}")
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Apply server filter
|
|
93
|
+
instructions[:servers] &= server_filter if server_filter
|
|
94
|
+
instructions
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def server_filter
|
|
98
|
+
@server_filter ||= filters['server'].split(',') if filters.key?('server')
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def defaults
|
|
102
|
+
return @defaults if @defaults
|
|
103
|
+
|
|
104
|
+
# Establish defaults
|
|
105
|
+
# First the default defaults
|
|
106
|
+
@defaults = {
|
|
107
|
+
name: File.basename(Dir.getwd),
|
|
108
|
+
environment: environment,
|
|
109
|
+
env: { 'RAILS_ENV' => environment },
|
|
110
|
+
port: 50_000
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
@defaults = supermerge(@defaults, roles_all[DEFAULTS_KEY]) if roles_all.key? DEFAULTS_KEY
|
|
114
|
+
@defaults = supermerge(@defaults, roles[DEFAULTS_KEY]) if roles.key? DEFAULTS_KEY
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def roles
|
|
118
|
+
@roles ||= roles_all[environment]
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def roles_all
|
|
122
|
+
return @roles_all if @roles_all
|
|
123
|
+
|
|
124
|
+
@roles_all = YAML.load(File.read(CONFIG_FILE))
|
|
125
|
+
|
|
126
|
+
# This environment
|
|
127
|
+
unless @roles_all.key? environment
|
|
128
|
+
terminate("No deployment configuration defined for #{environment} environment.\nCheck #{CONFIG_FILE}")
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
@roles_all
|
|
132
|
+
rescue Errno::ENOENT
|
|
133
|
+
terminate "Can't find configuration file #{CONFIG_FILE}.\nPlease run foreplay setup or create the file manually."
|
|
134
|
+
rescue Psych::SyntaxError
|
|
135
|
+
terminate "I don't understand the configuration file #{CONFIG_FILE}.\n"\
|
|
136
|
+
'Please run foreplay setup or edit the file manually.'
|
|
137
|
+
end
|
|
138
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
require 'thor/group'
|
|
2
|
+
|
|
3
|
+
class Foreplay::Launcher < Thor::Group
|
|
4
|
+
include Thor::Actions
|
|
5
|
+
|
|
6
|
+
argument :mode, type: :string, required: true
|
|
7
|
+
argument :environment, type: :string, required: true
|
|
8
|
+
argument :filters, type: :hash, required: false
|
|
9
|
+
|
|
10
|
+
def parse
|
|
11
|
+
Foreplay::Engine.new(environment, filters).__send__ mode
|
|
12
|
+
end
|
|
13
|
+
end
|
data/lib/foreplay/setup.rb
CHANGED
|
@@ -1,33 +1,31 @@
|
|
|
1
1
|
require 'thor/group'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
include Thor::Actions
|
|
3
|
+
class Foreplay::Setup < Thor::Group
|
|
4
|
+
include Thor::Actions
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
6
|
+
class_option :name, aliases: '-n', default: File.basename(Dir.getwd)
|
|
7
|
+
class_option :repository, aliases: '-r'
|
|
8
|
+
class_option :user, aliases: '-u'
|
|
9
|
+
class_option :password
|
|
10
|
+
class_option :keyfile
|
|
11
|
+
class_option :private_key, aliases: '-k'
|
|
12
|
+
class_option :port, aliases: '-p', default: 50_000
|
|
13
|
+
class_option :path, aliases: '-f'
|
|
14
|
+
class_option :servers, aliases: '-s', type: :array
|
|
15
|
+
class_option :db_adapter, aliases: '-a', default: 'postgresql'
|
|
16
|
+
class_option :db_encoding, aliases: '-e', default: 'utf8'
|
|
17
|
+
class_option :db_pool, default: 5
|
|
18
|
+
class_option :db_name, aliases: '-d'
|
|
19
|
+
class_option :db_host, aliases: '-h'
|
|
20
|
+
class_option :db_user
|
|
21
|
+
class_option :db_password
|
|
22
|
+
class_option :resque_redis
|
|
24
23
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
def self.source_root
|
|
25
|
+
File.dirname(__FILE__)
|
|
26
|
+
end
|
|
28
27
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
end
|
|
28
|
+
def config
|
|
29
|
+
template('setup/foreplay.yml', 'config/foreplay.yml')
|
|
32
30
|
end
|
|
33
31
|
end
|
data/lib/foreplay/version.rb
CHANGED
data/lib/foreplay.rb
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
require 'foreplay/version'
|
|
2
|
-
require 'foreplay/
|
|
3
|
-
require 'foreplay/
|
|
4
|
-
require 'foreplay/
|
|
2
|
+
require 'foreplay/foreplay'
|
|
3
|
+
require 'foreplay/launcher'
|
|
4
|
+
require 'foreplay/engine'
|
|
5
|
+
require 'foreplay/engine/remote'
|
|
6
|
+
require 'foreplay/engine/role'
|
|
7
|
+
require 'foreplay/engine/server'
|
|
8
|
+
require 'foreplay/engine/step'
|
data/lib/string.rb
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Some useful additions to the String class
|
|
2
|
+
class String
|
|
3
|
+
colors = %w(black red green yellow blue magenta cyan white)
|
|
4
|
+
|
|
5
|
+
colors.each_with_index do |fg_color, i|
|
|
6
|
+
fg = 30 + i
|
|
7
|
+
define_method(fg_color) { ansi_attributes(fg) }
|
|
8
|
+
|
|
9
|
+
colors.each_with_index do |bg_color, j|
|
|
10
|
+
define_method("#{fg_color}_on_#{bg_color}") { ansi_attributes(fg, 40 + j) }
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def ansi_attributes(*args)
|
|
15
|
+
"\e[#{args.join(';')}m#{self}\e[0m"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
|
-
require 'foreplay'
|
|
3
2
|
require 'net/ssh/shell'
|
|
3
|
+
require 'foreplay'
|
|
4
4
|
|
|
5
|
-
describe Foreplay::
|
|
5
|
+
describe Foreplay::Launcher do
|
|
6
6
|
let(:session) { double(Net::SSH::Connection::Session) }
|
|
7
7
|
let(:shell) { double(Net::SSH::Shell) }
|
|
8
8
|
let(:process) { double(Net::SSH::Shell::Process) }
|
|
@@ -10,7 +10,13 @@ describe Foreplay::Deploy do
|
|
|
10
10
|
before :each do
|
|
11
11
|
# Setup foreplay
|
|
12
12
|
`rm -f config/foreplay.yml`
|
|
13
|
-
|
|
13
|
+
command = 'foreplay setup '\
|
|
14
|
+
'-r git@github.com:Xenapto/foreplay.git '\
|
|
15
|
+
'-s web1.example.com web2.example.com '\
|
|
16
|
+
'-f apps/%a '\
|
|
17
|
+
'-u fred '\
|
|
18
|
+
'--password trollope'
|
|
19
|
+
`#{command}`
|
|
14
20
|
|
|
15
21
|
# Stub all the things
|
|
16
22
|
allow(Net::SSH).to receive(:start).and_return(session)
|
|
@@ -24,37 +30,68 @@ describe Foreplay::Deploy do
|
|
|
24
30
|
|
|
25
31
|
it "complains on check if there's no config file" do
|
|
26
32
|
`rm -f config/foreplay.yml`
|
|
27
|
-
expect { Foreplay::
|
|
33
|
+
expect { Foreplay::Launcher.start([:check, 'production', '']) }
|
|
28
34
|
.to raise_error(RuntimeError, /.*Please run foreplay setup or create the file manually.*/)
|
|
29
35
|
end
|
|
30
36
|
|
|
31
37
|
it "complains on deploy if there's no config file" do
|
|
32
38
|
`rm -f config/foreplay.yml`
|
|
33
|
-
expect { Foreplay::
|
|
39
|
+
expect { Foreplay::Launcher.start([:deploy, 'production', '']) }
|
|
34
40
|
.to raise_error(RuntimeError, /.*Please run foreplay setup or create the file manually.*/)
|
|
35
41
|
end
|
|
36
42
|
|
|
43
|
+
it 'complains on check if the config file is not valid YAML' do
|
|
44
|
+
`echo %*:*: > config/foreplay.yml`
|
|
45
|
+
expect { Foreplay::Launcher.start([:check, 'production', '']) }
|
|
46
|
+
.to raise_error(RuntimeError, /.*Please run foreplay setup or edit the file manually.*/)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it 'complains on deploy if the config file is not valid YAML' do
|
|
50
|
+
`echo %*:*: > config/foreplay.yml`
|
|
51
|
+
expect { Foreplay::Launcher.start([:deploy, 'production', '']) }
|
|
52
|
+
.to raise_error(RuntimeError, /.*Please run foreplay setup or edit the file manually.*/)
|
|
53
|
+
end
|
|
54
|
+
|
|
37
55
|
it 'complains if there are no authentication methods defined in the config file' do
|
|
56
|
+
command = 'foreplay setup '\
|
|
57
|
+
'-r git@github.com:Xenapto/foreplay.git '\
|
|
58
|
+
'-s web.example.com '\
|
|
59
|
+
'-f apps/%a '\
|
|
60
|
+
'-u fred '\
|
|
61
|
+
'--password ""'
|
|
62
|
+
|
|
63
|
+
match = 'No authentication methods supplied. '\
|
|
64
|
+
'You must supply a private key, key file or password in the configuration file.'
|
|
65
|
+
|
|
38
66
|
`rm -f config/foreplay.yml`
|
|
39
|
-
`
|
|
40
|
-
|
|
67
|
+
`#{command}`
|
|
68
|
+
|
|
69
|
+
expect { Foreplay::Launcher.start([:deploy, 'production', '']) }
|
|
41
70
|
.to raise_error(
|
|
42
71
|
RuntimeError,
|
|
43
|
-
|
|
72
|
+
/.*#{Regexp.quote(match)}*/
|
|
44
73
|
)
|
|
45
74
|
end
|
|
46
75
|
|
|
47
76
|
it "complains if the private keyfile defined in the config file doesn't exist" do
|
|
77
|
+
command = 'foreplay setup '\
|
|
78
|
+
'-r git@github.com:Xenapto/foreplay.git '\
|
|
79
|
+
'-s web.example.com '\
|
|
80
|
+
'-f apps/%a '\
|
|
81
|
+
'-u fred '\
|
|
82
|
+
'--keyfile "/home/fred/no-such-file"'
|
|
83
|
+
|
|
48
84
|
`rm -f config/foreplay.yml`
|
|
49
|
-
`
|
|
50
|
-
|
|
85
|
+
`#{command}`
|
|
86
|
+
|
|
87
|
+
expect { Foreplay::Launcher.start([:deploy, 'production', '']) }
|
|
51
88
|
.to raise_error(Errno::ENOENT, %r{.*No such file or directory @ rb_sysopen - /home/fred/no-such-file.*})
|
|
52
89
|
end
|
|
53
90
|
|
|
54
91
|
it 'complains if a mandatory key is missing from the config file' do
|
|
55
92
|
`sed -i 's/path:/pxth:/' config/foreplay.yml`
|
|
56
93
|
|
|
57
|
-
expect { Foreplay::
|
|
94
|
+
expect { Foreplay::Launcher.start([:deploy, 'production', '']) }
|
|
58
95
|
.to raise_error(
|
|
59
96
|
RuntimeError,
|
|
60
97
|
/.*Required key path not found in instructions for production environment.*/
|
|
@@ -62,7 +99,7 @@ describe Foreplay::Deploy do
|
|
|
62
99
|
end
|
|
63
100
|
|
|
64
101
|
it 'complains if we try to deploy an environment that isn\'t defined' do
|
|
65
|
-
expect { Foreplay::
|
|
102
|
+
expect { Foreplay::Launcher.start([:deploy, 'unknown', '']) }
|
|
66
103
|
.to raise_error(
|
|
67
104
|
RuntimeError,
|
|
68
105
|
/.*No deployment configuration defined for unknown environment.*/
|
|
@@ -71,18 +108,18 @@ describe Foreplay::Deploy do
|
|
|
71
108
|
|
|
72
109
|
it 'terminates if a remote process exits with a non-zero status' do
|
|
73
110
|
allow(process).to receive(:exit_status).and_return(1)
|
|
74
|
-
expect { Foreplay::
|
|
111
|
+
expect { Foreplay::Launcher.start([:deploy, 'production', '']) }.to raise_error(RuntimeError, /.*output message.*/)
|
|
75
112
|
end
|
|
76
113
|
|
|
77
114
|
it "terminates if a connection can't be established with the remote server" do
|
|
78
115
|
allow(Net::SSH).to receive(:start).and_call_original
|
|
79
|
-
expect { Foreplay::
|
|
116
|
+
expect { Foreplay::Launcher.start([:deploy, 'production', '']) }
|
|
80
117
|
.to raise_error(RuntimeError, /.*There was a problem starting an ssh session on web1.example.com.*/)
|
|
81
118
|
end
|
|
82
119
|
|
|
83
120
|
it 'checks the config' do
|
|
84
121
|
expect($stdout).to receive(:puts).at_least(:once)
|
|
85
|
-
Foreplay::
|
|
122
|
+
Foreplay::Launcher.start [:check, 'production', '']
|
|
86
123
|
end
|
|
87
124
|
|
|
88
125
|
it 'deploys to the environment' do
|
|
@@ -93,7 +130,9 @@ describe Foreplay::Deploy do
|
|
|
93
130
|
.and_return(session)
|
|
94
131
|
|
|
95
132
|
[
|
|
96
|
-
|
|
133
|
+
"echo Foreplay version #{Foreplay::VERSION}",
|
|
134
|
+
'mkdir -p apps/foreplay && cd apps/foreplay && rm -rf 50000 && '\
|
|
135
|
+
'git clone -b master git@github.com:Xenapto/foreplay.git 50000',
|
|
97
136
|
'rvm rvmrc trust 50000',
|
|
98
137
|
'rvm rvmrc warning ignore 50000',
|
|
99
138
|
'cd 50000 && mkdir -p tmp doc log config',
|
|
@@ -113,11 +152,13 @@ describe Foreplay::Deploy do
|
|
|
113
152
|
'echo " password: TODO Put here the database user\'s password" >> config/database.yml',
|
|
114
153
|
'if [ -d ../cache/vendor/bundle/bundle ] ; then rm -rf ../cache/vendor/bundle/bundle'\
|
|
115
154
|
' ; else echo No evidence of legacy copy bug ; fi',
|
|
116
|
-
'if [ -d ../cache/vendor/bundle ] ; then
|
|
155
|
+
'if [ -d ../cache/vendor/bundle ] ; then '\
|
|
156
|
+
'rsync -aW --no-compress --delete --info=STATS1 ../cache/vendor/bundle/ vendor/bundle'\
|
|
117
157
|
' ; else echo No bundle to restore ; fi',
|
|
118
158
|
'sudo ln -f `which bundle` /usr/bin/bundle || echo Using default version of bundle',
|
|
119
159
|
'bundle install --deployment --clean --jobs 2 --without development test',
|
|
120
|
-
'mkdir -p ../cache/vendor &&
|
|
160
|
+
'mkdir -p ../cache/vendor && '\
|
|
161
|
+
'rsync -aW --no-compress --delete --info=STATS1 vendor/bundle/ ../cache/vendor/bundle',
|
|
121
162
|
'if [ -f public/assets/manifest.yml ] ; then echo "Not precompiling assets"'\
|
|
122
163
|
' ; else RAILS_ENV=production bundle exec foreman run rake assets:precompile ; fi',
|
|
123
164
|
'sudo bundle exec foreman export upstart /etc/init',
|
|
@@ -135,24 +176,38 @@ describe Foreplay::Deploy do
|
|
|
135
176
|
expect(shell).to receive(:execute).with(command).and_return(process)
|
|
136
177
|
end
|
|
137
178
|
|
|
138
|
-
Foreplay::
|
|
179
|
+
Foreplay::Launcher.start [:deploy, 'production', '']
|
|
139
180
|
end
|
|
140
181
|
|
|
141
182
|
it "uses another port if there's already an installed instance" do
|
|
142
183
|
allow(process).to receive(:on_output).and_yield(process, "50000\n")
|
|
143
184
|
expect(shell).to receive(:execute).with('echo 51000 > $HOME/.foreplay/foreplay/current_port').and_return(process)
|
|
144
|
-
Foreplay::
|
|
185
|
+
Foreplay::Launcher.start [:deploy, 'production', '']
|
|
145
186
|
end
|
|
146
187
|
|
|
147
188
|
it 'uses the private key provided in the config file' do
|
|
189
|
+
command = 'foreplay setup '\
|
|
190
|
+
'-r git@github.com:Xenapto/foreplay.git '\
|
|
191
|
+
'-s web.example.com '\
|
|
192
|
+
'-f apps/%a '\
|
|
193
|
+
'-u fred '\
|
|
194
|
+
'-k "top secret private key"'
|
|
195
|
+
|
|
148
196
|
`rm -f config/foreplay.yml`
|
|
149
|
-
`
|
|
150
|
-
Foreplay::
|
|
197
|
+
`#{command}`
|
|
198
|
+
Foreplay::Launcher.start([:deploy, 'production', ''])
|
|
151
199
|
end
|
|
152
200
|
|
|
153
201
|
it 'adds Redis details for Resque' do
|
|
202
|
+
command = 'foreplay setup '\
|
|
203
|
+
'-r git@github.com:Xenapto/foreplay.git '\
|
|
204
|
+
'-s web.example.com '\
|
|
205
|
+
'-f apps/%a '\
|
|
206
|
+
'-u fred '\
|
|
207
|
+
'--resque-redis "redis://localhost:6379"'
|
|
208
|
+
|
|
154
209
|
`rm -f config/foreplay.yml`
|
|
155
|
-
`
|
|
156
|
-
Foreplay::
|
|
210
|
+
`#{command}`
|
|
211
|
+
Foreplay::Launcher.start([:deploy, 'production', ''])
|
|
157
212
|
end
|
|
158
213
|
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'foreplay'
|
|
3
|
+
|
|
4
|
+
describe Foreplay::Engine do
|
|
5
|
+
context '#supermerge' do
|
|
6
|
+
let(:engine) { Foreplay::Engine.new('b', 'b') }
|
|
7
|
+
|
|
8
|
+
it 'should complain unless two hashes are passed to it' do
|
|
9
|
+
expect { engine.supermerge('x', 'y') }.to raise_error(RuntimeError)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it 'should merge two simple hashes' do
|
|
13
|
+
expect(engine.supermerge({ a: 'x' }, b: 'y')).to eq('a' => 'x', 'b' => 'y')
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'should merge two hashes both with arrays at the same key' do
|
|
17
|
+
expect(engine.supermerge({ a: ['x'] }, a: ['y'])).to eq('a' => %w(x y))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'should merge an array and a value at the same key' do
|
|
21
|
+
expect(engine.supermerge({ a: 'x' }, a: ['y'])).to eq('a' => %w(x y))
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it 'should replace a value at the same key' do
|
|
25
|
+
expect(engine.supermerge({ a: 'x' }, a: 'y')).to eq('a' => 'y')
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it 'should merge two subhashes at the same key' do
|
|
29
|
+
expect(engine.supermerge({ a: { b: 'x' } }, a: { c: 'y' })).to eq('a' => { 'b' => 'x', 'c' => 'y' })
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|