shuttle-deploy 0.2.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +25 -0
- data/.magnum.yml +1 -0
- data/.rspec +2 -0
- data/Gemfile +3 -0
- data/LICENSE +18 -0
- data/README.md +230 -0
- data/Rakefile +10 -0
- data/bin/shuttle +60 -0
- data/examples/rails.yml +22 -0
- data/examples/static.yml +10 -0
- data/examples/wordpress.yml +34 -0
- data/lib/shuttle/config.rb +5 -0
- data/lib/shuttle/deploy.rb +58 -0
- data/lib/shuttle/deployment/nodejs.rb +48 -0
- data/lib/shuttle/deployment/php.rb +17 -0
- data/lib/shuttle/deployment/rails.rb +116 -0
- data/lib/shuttle/deployment/ruby.rb +40 -0
- data/lib/shuttle/deployment/static.rb +5 -0
- data/lib/shuttle/deployment/wordpress/cli.rb +27 -0
- data/lib/shuttle/deployment/wordpress/core.rb +50 -0
- data/lib/shuttle/deployment/wordpress/plugins.rb +112 -0
- data/lib/shuttle/deployment/wordpress/vip.rb +84 -0
- data/lib/shuttle/deployment/wordpress.rb +191 -0
- data/lib/shuttle/errors.rb +5 -0
- data/lib/shuttle/helpers.rb +50 -0
- data/lib/shuttle/runner.rb +153 -0
- data/lib/shuttle/session.rb +52 -0
- data/lib/shuttle/support/bundler.rb +45 -0
- data/lib/shuttle/support/foreman.rb +7 -0
- data/lib/shuttle/support/thin.rb +59 -0
- data/lib/shuttle/target.rb +23 -0
- data/lib/shuttle/tasks.rb +264 -0
- data/lib/shuttle/version.rb +3 -0
- data/lib/shuttle.rb +35 -0
- data/shuttle-deploy.gemspec +28 -0
- data/spec/deploy_spec.rb +4 -0
- data/spec/fixtures/.gitkeep +0 -0
- data/spec/fixtures/static.yml +11 -0
- data/spec/helpers_spec.rb +42 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/target_spec.rb +41 -0
- metadata +232 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
ZTM3MDZmNjBlN2EzYTRkZmRlNDc5OTk1ZWM5MGU0MzhmMGVhZTg2Mg==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NGRkOTVmY2IzYzU3MDZmMTRmNWQ1YzE3MTYzMjk4NGFlZjc4NzdkNQ==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
ZjBhNjhmOGQyNjNjMTA3MDJjNzM3MjcxOTBhMjIzMmJmYmU2YTk2ZmI0MjMz
|
10
|
+
MDM0MTc5YzMwZjlmMTQxNTdmNGQyZjJhOTdlYjRkMWY5ODUwMDJhYjY2MDY5
|
11
|
+
ZjRmYTczN2VhOGYyNjNiNGY5OTc2ZWQ3YWQ0YTI3MDE2MGI1ZjM=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
M2RkNzViNGNlZmY2ZWRjMTczOTVhNWFkNmYyMmRiZjQ3ZTE4NmNjNzViNWM3
|
14
|
+
YTVjMGI5ZjViYzhiNjQ0MzUwOGRhZDFlMDFlZmM0MDNiNTJjZGFiZGViMWE4
|
15
|
+
NTA4MTQyMTI4MGZmOGQ1YTYyMjI4ODVmNGYyMzJkNzE0MDAwNjg=
|
data/.gitignore
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
*.swp
|
4
|
+
*.tmproj
|
5
|
+
*~
|
6
|
+
.DS_Store
|
7
|
+
.\#*
|
8
|
+
.bundle
|
9
|
+
.config
|
10
|
+
.yardoc
|
11
|
+
Gemfile.lock
|
12
|
+
InstalledFiles
|
13
|
+
\#*
|
14
|
+
_yardoc
|
15
|
+
coverage
|
16
|
+
doc/
|
17
|
+
lib/bundler/man
|
18
|
+
pkg
|
19
|
+
rdoc
|
20
|
+
spec/reports
|
21
|
+
test/tmp
|
22
|
+
test/version_tmp
|
23
|
+
tmp
|
24
|
+
tmtags
|
25
|
+
Gemfile.lock
|
data/.magnum.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby: 1.9.3
|
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Copyright (c) 2012-2013 Dan Sosedoff.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
4
|
+
this software and associated documentation files (the "Software"), to deal in
|
5
|
+
the Software without restriction, including without limitation the rights to
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
7
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
8
|
+
subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
15
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
16
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
17
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
18
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,230 @@
|
|
1
|
+
# Shuttle
|
2
|
+
|
3
|
+
Shuttle is a minimalistic application deployment tool designed for small applications
|
4
|
+
and one-server deployments. Configuration is stored as YAML-encoded file, no need to use ruby code.
|
5
|
+
Operations are performed on SSH connection with target server.
|
6
|
+
|
7
|
+
*Under heavy development*
|
8
|
+
|
9
|
+
## Install
|
10
|
+
|
11
|
+
Clone repository and run:
|
12
|
+
|
13
|
+
```
|
14
|
+
rake install
|
15
|
+
```
|
16
|
+
|
17
|
+
Supported ruby versions:
|
18
|
+
|
19
|
+
- 1.8.7
|
20
|
+
- 1.9.2
|
21
|
+
- 1.9.3
|
22
|
+
- 2.0.0
|
23
|
+
|
24
|
+
## Structure
|
25
|
+
|
26
|
+
Deployment structure is very similar to capistrano.
|
27
|
+
|
28
|
+
Application files will be stored in `deploy_to` path that you specify in config.
|
29
|
+
Directory structure:
|
30
|
+
|
31
|
+
- `releases` - Main directory to store all application releases
|
32
|
+
- `current` - Symbolic link to the latest release
|
33
|
+
- `shared` - Shared directory to store assets, configs, etc
|
34
|
+
- `scm` - Code repository directory
|
35
|
+
- `version` - File that contains current release number
|
36
|
+
|
37
|
+
Shared directory structure:
|
38
|
+
|
39
|
+
- `tmp` - Temporary files
|
40
|
+
- `pids` - Shared process IDs files
|
41
|
+
- `log` - Shared log files
|
42
|
+
|
43
|
+
## Process
|
44
|
+
|
45
|
+
Deployment flow is split into steps:
|
46
|
+
|
47
|
+
- Establish connection with target server
|
48
|
+
- Prepare application structure. It'll create all required directories or skip if they already exist.
|
49
|
+
- Clone repository or check out latest code. Submodules will be automatically updated as well.
|
50
|
+
- Switch to specified branch (`master` by default)
|
51
|
+
- Create a new release directory and checkout application code
|
52
|
+
- Perform strategy-related tasks.
|
53
|
+
- Create a symbolic link to the latest release
|
54
|
+
- Clean up old releases (default count: 5)
|
55
|
+
|
56
|
+
## Strategies
|
57
|
+
|
58
|
+
Available deployment strategies:
|
59
|
+
|
60
|
+
- `static`
|
61
|
+
- `wordpress`
|
62
|
+
- `ruby`
|
63
|
+
- `rails`
|
64
|
+
- `nodejs`
|
65
|
+
|
66
|
+
### Static Strategy
|
67
|
+
|
68
|
+
This is a default strategy that does not perform any application-related tasks.
|
69
|
+
Example configuration:
|
70
|
+
|
71
|
+
```yaml
|
72
|
+
app:
|
73
|
+
name: my-application
|
74
|
+
strategy: static
|
75
|
+
git: git@github.com:my-site.git
|
76
|
+
|
77
|
+
target:
|
78
|
+
host: my-host.com
|
79
|
+
user: username
|
80
|
+
password: password
|
81
|
+
deploy_to: /home/deployer/www
|
82
|
+
```
|
83
|
+
|
84
|
+
### Wordpress Strategy
|
85
|
+
|
86
|
+
This strategy is designed to deploy wordpress sites developed as a separate theme.
|
87
|
+
It requires `subversion` installed on the server (will be automatically installed).
|
88
|
+
|
89
|
+
## Deployment Config
|
90
|
+
|
91
|
+
Deployment config has a few main sections: `app` and `target`.
|
92
|
+
|
93
|
+
### Application
|
94
|
+
|
95
|
+
Application section defines deployment strategy, source code location and other options:
|
96
|
+
|
97
|
+
```yml
|
98
|
+
app:
|
99
|
+
name: my-app
|
100
|
+
strategy: static
|
101
|
+
git: https://site-url.com/repo.git
|
102
|
+
branch: master
|
103
|
+
keep_releases: 5
|
104
|
+
```
|
105
|
+
|
106
|
+
Options:
|
107
|
+
|
108
|
+
- `name` - Your application name
|
109
|
+
- `strategy` - Deployment strategy. Defaults to `static`
|
110
|
+
- `git` - Git repository url
|
111
|
+
- `branch` - Git repository branch. Defaults to `master`
|
112
|
+
- `keep_releases` - Number of releases to keep. Defaults to `10`
|
113
|
+
|
114
|
+
You can also use Subversion as a main source:
|
115
|
+
|
116
|
+
```yml
|
117
|
+
app:
|
118
|
+
svn: http://site-url.com/repo.git
|
119
|
+
```
|
120
|
+
|
121
|
+
If your repository requires authentication, use url in the following format:
|
122
|
+
|
123
|
+
```
|
124
|
+
http://username:password@yourdomain.com/project
|
125
|
+
```
|
126
|
+
|
127
|
+
### Target
|
128
|
+
|
129
|
+
Target is a set of remote machine credentials:
|
130
|
+
|
131
|
+
```yml
|
132
|
+
target:
|
133
|
+
host: yourdomain.com
|
134
|
+
user: deployer
|
135
|
+
password: password
|
136
|
+
deploy_to: /home/deployer/myapp
|
137
|
+
```
|
138
|
+
|
139
|
+
Options:
|
140
|
+
|
141
|
+
- `host` - Remote server host or ip
|
142
|
+
- `user` - Remote server user account
|
143
|
+
- `password` - Optional password. Use passwordless authentication if possible.
|
144
|
+
- `deploy_to` - Primary directory where all releases will be stored
|
145
|
+
|
146
|
+
You can also define multiple targets per config if environments does not have any specific
|
147
|
+
configuration settings:
|
148
|
+
|
149
|
+
```yml
|
150
|
+
targets:
|
151
|
+
production:
|
152
|
+
host: mydomain.com
|
153
|
+
user: deployer
|
154
|
+
deploy_to: /home/production/myapp
|
155
|
+
staging:
|
156
|
+
host: mydomain.com
|
157
|
+
user: deployer
|
158
|
+
deploy_to: /home/staging/myapp
|
159
|
+
```
|
160
|
+
|
161
|
+
## Usage
|
162
|
+
|
163
|
+
To execute a new deploy, simply type (in your project folder):
|
164
|
+
|
165
|
+
```
|
166
|
+
shuttle deploy
|
167
|
+
```
|
168
|
+
|
169
|
+
Output will look like this:
|
170
|
+
|
171
|
+
```
|
172
|
+
Shuttle v0.2.0
|
173
|
+
|
174
|
+
-----> Connected to deployer@mysite.com
|
175
|
+
-----> Preparing application structure
|
176
|
+
-----> Fetching latest code
|
177
|
+
-----> Using branch 'master'
|
178
|
+
-----> Linking release
|
179
|
+
-----> Release v35 has been deployed
|
180
|
+
-----> Cleaning up old releases: 1
|
181
|
+
|
182
|
+
Execution time: 2s
|
183
|
+
```
|
184
|
+
|
185
|
+
If using multiple targets in config, you can specify which target to use with:
|
186
|
+
|
187
|
+
```
|
188
|
+
shuttle staging deploy
|
189
|
+
```
|
190
|
+
|
191
|
+
Specify a path to config with `-f` flag:
|
192
|
+
|
193
|
+
```
|
194
|
+
shuttle -f /path/to/config.yml deploy
|
195
|
+
```
|
196
|
+
|
197
|
+
To run in debug mode, add `-d` flag:
|
198
|
+
|
199
|
+
```
|
200
|
+
shuttle deploy -d
|
201
|
+
```
|
202
|
+
|
203
|
+
## Test
|
204
|
+
|
205
|
+
To run project test suite execute:
|
206
|
+
|
207
|
+
```
|
208
|
+
bundle exec rake test
|
209
|
+
```
|
210
|
+
|
211
|
+
## License
|
212
|
+
|
213
|
+
Copyright (c) 2012-2013 Dan Sosedoff.
|
214
|
+
|
215
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
216
|
+
this software and associated documentation files (the "Software"), to deal in
|
217
|
+
the Software without restriction, including without limitation the rights to
|
218
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
219
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
220
|
+
subject to the following conditions:
|
221
|
+
|
222
|
+
The above copyright notice and this permission notice shall be included in all
|
223
|
+
copies or substantial portions of the Software.
|
224
|
+
|
225
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
226
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
227
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
228
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
229
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
230
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
data/bin/shuttle
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
|
4
|
+
$LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
|
5
|
+
|
6
|
+
require 'rubygems'
|
7
|
+
require 'optparse'
|
8
|
+
require 'shuttle'
|
9
|
+
|
10
|
+
config_name = Dir.pwd.split('/').last + ".yml"
|
11
|
+
|
12
|
+
options = {
|
13
|
+
:path => File.join(ENV['HOME'], '.shuttle', config_name),
|
14
|
+
:target => 'production',
|
15
|
+
:log => false
|
16
|
+
}
|
17
|
+
|
18
|
+
optparse = OptionParser.new do |opts|
|
19
|
+
opts.on('-v', '--version', 'Show version') do
|
20
|
+
puts "Shuttle version #{Shuttle::VERSION}"
|
21
|
+
exit 0
|
22
|
+
end
|
23
|
+
|
24
|
+
opts.on('-e', '--environment NAME', 'Deployment target environment') do |v|
|
25
|
+
options[:target] = v
|
26
|
+
end
|
27
|
+
|
28
|
+
opts.on('-d', '--debug', 'Enable debugging') do
|
29
|
+
options[:log] = true
|
30
|
+
end
|
31
|
+
|
32
|
+
opts.on('-f', '--file PATH', 'Configuration file path') do |v|
|
33
|
+
options[:path] = v
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
begin
|
38
|
+
optparse.parse!
|
39
|
+
rescue OptionParser::ParseError => e
|
40
|
+
puts "Error: #{e.message}"
|
41
|
+
exit 1
|
42
|
+
end
|
43
|
+
|
44
|
+
case ARGV.size
|
45
|
+
when 2
|
46
|
+
options[:target] = ARGV.shift
|
47
|
+
command = ARGV.shift
|
48
|
+
when 1
|
49
|
+
command = ARGV.shift
|
50
|
+
else
|
51
|
+
puts "Command required"
|
52
|
+
exit 1
|
53
|
+
end
|
54
|
+
|
55
|
+
begin
|
56
|
+
runner = Shuttle::Runner.new(options)
|
57
|
+
runner.execute(command.dup)
|
58
|
+
rescue Shuttle::ConfigError => err
|
59
|
+
puts "Error: #{err.message}."
|
60
|
+
end
|
data/examples/rails.yml
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
app:
|
2
|
+
name: rails-app
|
3
|
+
strategy: rails
|
4
|
+
git: git@github.com:user/repo.git
|
5
|
+
|
6
|
+
target:
|
7
|
+
host: rails-app.com
|
8
|
+
user: deployer
|
9
|
+
password: password
|
10
|
+
deploy_to: /home/deployer/www
|
11
|
+
|
12
|
+
rails:
|
13
|
+
environment: production
|
14
|
+
precompile_assets: true
|
15
|
+
start_server: true
|
16
|
+
shared_paths:
|
17
|
+
static_assets: public/static
|
18
|
+
|
19
|
+
thin:
|
20
|
+
host: 127.0.0.1
|
21
|
+
port: 9000
|
22
|
+
servers: 5
|
data/examples/static.yml
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
app:
|
2
|
+
name: sample-site
|
3
|
+
strategy: wordpress
|
4
|
+
git: git@github.com/username/sample-site.git
|
5
|
+
branch: production
|
6
|
+
|
7
|
+
target:
|
8
|
+
host: sample-site.com
|
9
|
+
user: deployer
|
10
|
+
password: password
|
11
|
+
deploy_to: /home/deployer/www
|
12
|
+
|
13
|
+
wordpress:
|
14
|
+
theme: theme_name
|
15
|
+
site:
|
16
|
+
title: "Site Title"
|
17
|
+
url: "http://sample-site.com"
|
18
|
+
admin_name: "admin"
|
19
|
+
admin_email: "admin@admin.com"
|
20
|
+
admin_password: "password"
|
21
|
+
mysql:
|
22
|
+
host: 127.0.0.1
|
23
|
+
user: mysql-user
|
24
|
+
password: mysql-password
|
25
|
+
database: mysql-dadtabase
|
26
|
+
plugins:
|
27
|
+
- advanced-custom-fields: git://github.com/elliotcondon/acf.git
|
28
|
+
- be-subpages-widget
|
29
|
+
- contact-form-7
|
30
|
+
- contact-form-7-to-database-extension
|
31
|
+
- featured-page-widget
|
32
|
+
- share-this
|
33
|
+
- twitter-widget-pro
|
34
|
+
- types
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Shuttle
|
2
|
+
class Deploy
|
3
|
+
include Shuttle::Tasks
|
4
|
+
include Shuttle::Helpers
|
5
|
+
|
6
|
+
attr_reader :ssh
|
7
|
+
attr_reader :target
|
8
|
+
attr_reader :environment
|
9
|
+
attr_reader :version
|
10
|
+
attr_reader :config
|
11
|
+
|
12
|
+
def initialize(config, ssh, target, environment)
|
13
|
+
@config = config
|
14
|
+
@target = target
|
15
|
+
@ssh = ssh
|
16
|
+
@environment = environment
|
17
|
+
|
18
|
+
if ssh.file_exists?(version_path)
|
19
|
+
res = ssh.capture("cat #{version_path}")
|
20
|
+
@version = (res.empty? ? 1 : Integer(res) + 1).to_s
|
21
|
+
else
|
22
|
+
@version = 1
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def deploy_path(path=nil)
|
27
|
+
[target.deploy_to, path].compact.join('/')
|
28
|
+
end
|
29
|
+
|
30
|
+
def shared_path(path=nil)
|
31
|
+
[deploy_path, 'shared', path].compact.join('/')
|
32
|
+
end
|
33
|
+
|
34
|
+
def current_path(path=nil)
|
35
|
+
[deploy_path, 'current', path].compact.join('/')
|
36
|
+
end
|
37
|
+
|
38
|
+
def version_path
|
39
|
+
deploy_path('version')
|
40
|
+
end
|
41
|
+
|
42
|
+
def release_path(path=nil)
|
43
|
+
[deploy_path, 'releases', version, path].compact.join('/')
|
44
|
+
end
|
45
|
+
|
46
|
+
def scm_path
|
47
|
+
deploy_path('scm')
|
48
|
+
end
|
49
|
+
|
50
|
+
def deploy
|
51
|
+
setup
|
52
|
+
update_code
|
53
|
+
checkout_code
|
54
|
+
link_release
|
55
|
+
cleanup_releases
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Shuttle
|
2
|
+
class Nodejs < Shuttle::Deploy
|
3
|
+
def setup
|
4
|
+
if node_installed?
|
5
|
+
log "Using Node.js v#{node_version}, Npm v#{npm_version}"
|
6
|
+
else
|
7
|
+
error "Node.js is not installed."
|
8
|
+
end
|
9
|
+
|
10
|
+
super
|
11
|
+
end
|
12
|
+
|
13
|
+
def deploy
|
14
|
+
setup
|
15
|
+
update_code
|
16
|
+
checkout_code
|
17
|
+
install_dependencies
|
18
|
+
link_release
|
19
|
+
cleanup_releases
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def node_installed?
|
25
|
+
ssh.run("which node").success?
|
26
|
+
end
|
27
|
+
|
28
|
+
def node_version
|
29
|
+
ssh.run("node -v").output.strip.gsub('v', '')
|
30
|
+
end
|
31
|
+
|
32
|
+
def npm_version
|
33
|
+
ssh.run("npm -v").output.strip
|
34
|
+
end
|
35
|
+
|
36
|
+
def install_dependencies
|
37
|
+
if ssh.file_exists?("#{release_path}/package.json")
|
38
|
+
log "Installing application dependencies"
|
39
|
+
|
40
|
+
result = ssh.run("cd #{release_path} && npm install")
|
41
|
+
|
42
|
+
if result.failure?
|
43
|
+
error "Unable to install dependencies: #{result.output}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
module Shuttle
|
2
|
+
class Rails < Shuttle::Deploy
|
3
|
+
include Shuttle::Support::Bundler
|
4
|
+
include Shuttle::Support::Thin
|
5
|
+
|
6
|
+
def rails_env
|
7
|
+
if config.rails && config.rails.environment
|
8
|
+
config.rails.environment
|
9
|
+
else
|
10
|
+
environment
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def precompile_assets?
|
15
|
+
config.rails && config.rails.precompile_assets != false
|
16
|
+
end
|
17
|
+
|
18
|
+
def start_server?
|
19
|
+
if config.rails && !config.rails.start_server.nil?
|
20
|
+
config.rails.start_server
|
21
|
+
else
|
22
|
+
true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def setup_bundler
|
27
|
+
if !bundler_installed?
|
28
|
+
log "Bundler is missing. Installing"
|
29
|
+
|
30
|
+
res = ssh.run("sudo gem install bundler")
|
31
|
+
if res.success?
|
32
|
+
log "Bundler v#{bundler_version} installed"
|
33
|
+
else
|
34
|
+
error "Unable to install bundler: #{res.output}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def rake(command)
|
40
|
+
res = ssh.run("cd #{release_path} && rake #{command}")
|
41
|
+
if res.failure?
|
42
|
+
error "Unable to run rake command: #{command}. Reason: #{res.output}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def precompile_assets
|
47
|
+
if precompile_assets?
|
48
|
+
log "Precompiling assets"
|
49
|
+
rake 'assets:precompile'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def deploy
|
54
|
+
ssh.export('RACK_ENV', rails_env)
|
55
|
+
ssh.export('RAILS_ENV', rails_env)
|
56
|
+
|
57
|
+
log "Rails environment is set to #{rails_env}"
|
58
|
+
|
59
|
+
setup
|
60
|
+
setup_bundler
|
61
|
+
update_code
|
62
|
+
checkout_code
|
63
|
+
bundle_install
|
64
|
+
migrate_database
|
65
|
+
precompile_assets
|
66
|
+
link_shared_paths
|
67
|
+
|
68
|
+
if start_server?
|
69
|
+
thin_restart
|
70
|
+
end
|
71
|
+
|
72
|
+
link_release
|
73
|
+
cleanup_releases
|
74
|
+
end
|
75
|
+
|
76
|
+
def migrate_database
|
77
|
+
return if !ssh.file_exists?(release_path('db/schema.rb'))
|
78
|
+
|
79
|
+
migrate = true # Will migrate by default
|
80
|
+
schema = ssh.read_file(release_path('db/schema.rb'))
|
81
|
+
schema_file = shared_path('schema')
|
82
|
+
checksum = Digest::SHA1.hexdigest(schema)
|
83
|
+
|
84
|
+
if ssh.file_exists?(schema_file)
|
85
|
+
old_checksum = ssh.read_file(schema_file).strip
|
86
|
+
if old_checksum == checksum
|
87
|
+
migrate = false
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
if migrate == true
|
92
|
+
log "Migrating database"
|
93
|
+
rake 'db:migrate'
|
94
|
+
ssh.run("echo #{checksum} > #{schema_file}")
|
95
|
+
else
|
96
|
+
log "Database migration skipped"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def link_shared_paths
|
101
|
+
ssh.run("mkdir -p #{release_path('tmp')}")
|
102
|
+
ssh.run("rm -rf #{release_path}/log")
|
103
|
+
ssh.run("ln -s #{shared_path('pids')} #{release_path('tmp/pids')}")
|
104
|
+
ssh.run("ln -s #{shared_path('log')} #{release_path('log')}")
|
105
|
+
|
106
|
+
if config.rails
|
107
|
+
if config.rails.shared_paths
|
108
|
+
config.rails.shared_paths.each_pair do |name, path|
|
109
|
+
log "Linking shared path: #{name}"
|
110
|
+
ssh.run("ln -s #{shared_path}/#{name} #{release_path}/#{path}")
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|