foreplay 0.0.1
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/.gitignore +34 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +110 -0
- data/Rakefile +1 -0
- data/bin/foreplay +3 -0
- data/features/config.feature +66 -0
- data/features/generator.feature +97 -0
- data/features/support/setup.rb +1 -0
- data/foreplay.gemspec +33 -0
- data/foreplay.rake +28 -0
- data/foreplay.rb +246 -0
- data/lib/foreplay/cli.rb +23 -0
- data/lib/foreplay/config.rb +25 -0
- data/lib/foreplay/generators/foreplay.yml +85 -0
- data/lib/foreplay/generators/setup.rb +20 -0
- data/lib/foreplay/version.rb +3 -0
- data/lib/foreplay.rb +6 -0
- data/spec/foreplay_spec.rb +7 -0
- metadata +268 -0
data/.gitignore
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
*.rbc
|
|
2
|
+
*.sassc
|
|
3
|
+
.sass-cache
|
|
4
|
+
capybara-*.html
|
|
5
|
+
.rspec
|
|
6
|
+
/.bundle
|
|
7
|
+
/vendor/bundle
|
|
8
|
+
/log/*
|
|
9
|
+
/tmp/*
|
|
10
|
+
/db/*.sqlite3
|
|
11
|
+
/public/system/*
|
|
12
|
+
/coverage/
|
|
13
|
+
/spec/tmp/*
|
|
14
|
+
**.orig
|
|
15
|
+
rerun.txt
|
|
16
|
+
pickle-email-*.html
|
|
17
|
+
|
|
18
|
+
*.gem
|
|
19
|
+
.bundle
|
|
20
|
+
.config
|
|
21
|
+
.yardoc
|
|
22
|
+
Gemfile.lock
|
|
23
|
+
InstalledFiles
|
|
24
|
+
_yardoc
|
|
25
|
+
coverage
|
|
26
|
+
doc/
|
|
27
|
+
lib/bundler/man
|
|
28
|
+
pkg
|
|
29
|
+
rdoc
|
|
30
|
+
spec/reports
|
|
31
|
+
test/tmp
|
|
32
|
+
test/version_tmp
|
|
33
|
+
tmp
|
|
34
|
+
config
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2013 Dominic Sayers
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Foreplay
|
|
2
|
+
|
|
3
|
+
Foreplay: deploying Rails projects to Ubuntu using Foreman
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add this line to your application's Gemfile:
|
|
8
|
+
|
|
9
|
+
gem 'foreplay'
|
|
10
|
+
|
|
11
|
+
And then execute:
|
|
12
|
+
|
|
13
|
+
$ bundle
|
|
14
|
+
|
|
15
|
+
Or install it yourself as:
|
|
16
|
+
|
|
17
|
+
$ gem install foreplay
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
ENV=production rake foreplay:push
|
|
22
|
+
|
|
23
|
+
or
|
|
24
|
+
|
|
25
|
+
ENV=production ROLE=web rake foreplay:push
|
|
26
|
+
|
|
27
|
+
You can set the environment variables `ENV` and `ROLE` elsewhere if you wish.
|
|
28
|
+
If `ROLE` is not defined then we deploy all roles.
|
|
29
|
+
|
|
30
|
+
### How it works
|
|
31
|
+
|
|
32
|
+
Foreplay does this:
|
|
33
|
+
|
|
34
|
+
1. Opens an SSH connection to the deloyment target
|
|
35
|
+
2. Grabs a copy of your code from the repository
|
|
36
|
+
3. Builds a `.env` file, a `.foreman` file and a `database.yml` file
|
|
37
|
+
4. Does a `bundle install`
|
|
38
|
+
5. Uses `foreman` to create an Upstart service (`foreman export`) for your app
|
|
39
|
+
6. Launches the app
|
|
40
|
+
7. Directs incoming traffic on port 80 to your app
|
|
41
|
+
8. If there's a previous instance of the app running, Foreplay shuts it down gracefully after it has switched `iptables` to the new instance
|
|
42
|
+
|
|
43
|
+
There should be little or no downtime. If the app is b0rked then you can easily switch back to the previous instance: the Upstart service is still configured.
|
|
44
|
+
|
|
45
|
+
### foreplay.yml
|
|
46
|
+
|
|
47
|
+
Format:
|
|
48
|
+
|
|
49
|
+
```YAML
|
|
50
|
+
defaults: # global defaults for all environments
|
|
51
|
+
name: # app name (if omitted then Rails.application.class.parent_name.underscore is used)
|
|
52
|
+
servers: [server1, server2, server3] # which servers to deploy the app on
|
|
53
|
+
user: # The username to connect with (must have SSH permissions)
|
|
54
|
+
password: # The password to use to connect (not necessary if you've set up SSH keys)
|
|
55
|
+
keyfile: # ...or a file containing a private key that allows the named user access to the server
|
|
56
|
+
key: # ...or a private key that allows the named user access to the server
|
|
57
|
+
path: # absolute path to deploy the app on each server. %s will be translated to the application name
|
|
58
|
+
database: # the database.yml elements to write to the config folder
|
|
59
|
+
env: # contents of the .env file
|
|
60
|
+
key: value # will go into the .env file as key=value
|
|
61
|
+
foreman: # contents of the .foreman file
|
|
62
|
+
key: value # will go into the .foreman file as key: value
|
|
63
|
+
production: # deployment configuration for the production environment
|
|
64
|
+
defaults: # defaults for all roles in this environment (structure same as global defaults)
|
|
65
|
+
role1: # settings for the a particular role (e.g. web, worker, etc.) (structure same as global defaults)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Environment
|
|
69
|
+
|
|
70
|
+
Settings for the `.env` files and `.foreman` files in specific sections will add to the defaults specified earlier. `.env` files will get a `RAILS_ENV=environment` entry (where `environment` is as specified in `foreplay.yml`). You can override this by adding a different `RAILS_ENV` setting to this configuration here.
|
|
71
|
+
|
|
72
|
+
The first instance of the first entry in `Procfile` that is instantiated by your Foreman concurrency settings will
|
|
73
|
+
be started on port 50100 or 51100 and the external port 80 will be mapped to this port by `iptables`. You cannot
|
|
74
|
+
configure the ports yourself. As an example, if your `Procfile` has a `web` entry on the first line and at
|
|
75
|
+
least one `web` instance is configured in the `.foreman` concurrency setting then the first instance of your `web`
|
|
76
|
+
process will be available to the outside world on port 80.
|
|
77
|
+
|
|
78
|
+
### Path
|
|
79
|
+
|
|
80
|
+
You can use `%u` in the path. This will be substituted with the `user` value. You can use `%a` in the path. This will be substituted with the app's `name`
|
|
81
|
+
|
|
82
|
+
Example:
|
|
83
|
+
|
|
84
|
+
user: fred
|
|
85
|
+
name: myapp
|
|
86
|
+
path: /home/%u/apps/%a
|
|
87
|
+
|
|
88
|
+
### Dependencies
|
|
89
|
+
|
|
90
|
+
```ruby
|
|
91
|
+
gem 'foreman'
|
|
92
|
+
gem 'net-ssh-shell'
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
You can constrain this to whatever groups you use for initiating deployments, e.g.
|
|
96
|
+
|
|
97
|
+
```ruby
|
|
98
|
+
group :development, :test do
|
|
99
|
+
gem 'foreman'
|
|
100
|
+
gem 'net-ssh-shell'
|
|
101
|
+
end
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Contributing
|
|
105
|
+
|
|
106
|
+
1. Fork it
|
|
107
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
108
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
109
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
110
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/foreplay
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
Feature: Config
|
|
2
|
+
In order to configure Foreplay
|
|
3
|
+
As a CLI
|
|
4
|
+
I want to be as usable as possible
|
|
5
|
+
|
|
6
|
+
Scenario: Check configuration
|
|
7
|
+
When I run `foreplay check`
|
|
8
|
+
Then the output should contain "OK"
|
|
9
|
+
|
|
10
|
+
Scenario: Check configuration parameters - invalid parameter
|
|
11
|
+
When I run `foreplay check --invalid xyz`
|
|
12
|
+
Then the output should contain:
|
|
13
|
+
"""
|
|
14
|
+
ERROR: foreplay check was called with arguments ["--invalid", "xyz"]
|
|
15
|
+
Usage: "foreplay check".
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
Scenario: Check configuration parameters - short invalid parameter
|
|
19
|
+
When I run `foreplay check -x xyz`
|
|
20
|
+
Then the output should contain:
|
|
21
|
+
"""
|
|
22
|
+
ERROR: foreplay check was called with arguments ["-x", "xyz"]
|
|
23
|
+
Usage: "foreplay check".
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
Scenario: Check configuration parameters - environment parameter
|
|
27
|
+
When I run `foreplay check --environment production`
|
|
28
|
+
Then the output should contain "Checking configuration for"
|
|
29
|
+
And the output should contain "production environment"
|
|
30
|
+
And the output should contain "all roles"
|
|
31
|
+
And the output should contain "all servers"
|
|
32
|
+
|
|
33
|
+
Scenario: Check configuration parameters - role parameter
|
|
34
|
+
When I run `foreplay check --role worker`
|
|
35
|
+
Then the output should contain "Checking configuration for"
|
|
36
|
+
And the output should contain "all environments"
|
|
37
|
+
And the output should contain "worker role"
|
|
38
|
+
And the output should contain "all servers"
|
|
39
|
+
|
|
40
|
+
Scenario: Check configuration parameters - server parameter
|
|
41
|
+
When I run `foreplay check --server worker.example.com`
|
|
42
|
+
Then the output should contain "Checking configuration for"
|
|
43
|
+
And the output should contain "all environments"
|
|
44
|
+
And the output should contain "all roles"
|
|
45
|
+
And the output should contain "worker.example.com server"
|
|
46
|
+
|
|
47
|
+
Scenario: Check configuration parameters - short environment parameter
|
|
48
|
+
When I run `foreplay check -e production`
|
|
49
|
+
Then the output should contain "Checking configuration for"
|
|
50
|
+
And the output should contain "production environment"
|
|
51
|
+
And the output should contain "all roles"
|
|
52
|
+
And the output should contain "all servers"
|
|
53
|
+
|
|
54
|
+
Scenario: Check configuration parameters - short role parameter
|
|
55
|
+
When I run `foreplay check -r worker`
|
|
56
|
+
Then the output should contain "Checking configuration for"
|
|
57
|
+
And the output should contain "all environments"
|
|
58
|
+
And the output should contain "worker role"
|
|
59
|
+
And the output should contain "all servers"
|
|
60
|
+
|
|
61
|
+
Scenario: Check configuration parameters - short server parameter
|
|
62
|
+
When I run `foreplay check -s worker.example.com`
|
|
63
|
+
Then the output should contain "Checking configuration for"
|
|
64
|
+
And the output should contain "all environments"
|
|
65
|
+
And the output should contain "all roles"
|
|
66
|
+
And the output should contain "worker.example.com server"
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
Feature: Setup
|
|
2
|
+
In order to setup Foreplay
|
|
3
|
+
As a CLI user
|
|
4
|
+
I want to be able to create the config scaffold
|
|
5
|
+
|
|
6
|
+
Scenario: Setup
|
|
7
|
+
When I run `foreplay setup`
|
|
8
|
+
Then the following files should exist:
|
|
9
|
+
| config/foreplay.yml |
|
|
10
|
+
And the file "config/foreplay.yml" should contain:
|
|
11
|
+
"""
|
|
12
|
+
# Format:
|
|
13
|
+
#
|
|
14
|
+
# There is a section for each environment that you will deploy to, plus a section that defines global default
|
|
15
|
+
# values for all environments, like this:
|
|
16
|
+
#
|
|
17
|
+
# defaults:
|
|
18
|
+
# ...
|
|
19
|
+
# production:
|
|
20
|
+
# ...
|
|
21
|
+
# staging:
|
|
22
|
+
# ...
|
|
23
|
+
#
|
|
24
|
+
# Within each section you can define the server roles for that environment: web, worker, database etc. (the
|
|
25
|
+
# names of these roles are up to you). You can also define environment-level defaults that apply to all roles.
|
|
26
|
+
# Like this:
|
|
27
|
+
#
|
|
28
|
+
# production:
|
|
29
|
+
# defaults:
|
|
30
|
+
# ...
|
|
31
|
+
# web:
|
|
32
|
+
# ...
|
|
33
|
+
# worker:
|
|
34
|
+
# ...
|
|
35
|
+
# scheduler:
|
|
36
|
+
# ...
|
|
37
|
+
# database:
|
|
38
|
+
# ...
|
|
39
|
+
# staging:
|
|
40
|
+
# defaults:
|
|
41
|
+
# ...
|
|
42
|
+
# web:
|
|
43
|
+
# ...
|
|
44
|
+
# worker:
|
|
45
|
+
# ...
|
|
46
|
+
# scheduler:
|
|
47
|
+
# ...
|
|
48
|
+
# database:
|
|
49
|
+
# ...
|
|
50
|
+
#
|
|
51
|
+
# Within each role section you can define how the deployment is configured for the servers in that role.
|
|
52
|
+
# Some of these values will normally be defined as a default, some will be specific to a particular role.
|
|
53
|
+
# The values you can configure are as follows:
|
|
54
|
+
#
|
|
55
|
+
# value Normally defined as Notes
|
|
56
|
+
# ------------- -------------------- -------------------------------------------------------------------
|
|
57
|
+
# name: Global default App name (if omitted then
|
|
58
|
+
# Rails.application.class.parent_name.underscore is used)
|
|
59
|
+
# user: Global default The username to connect with (must have SSH permissions)
|
|
60
|
+
# password: Global default The password to use to connect (not necessary if you've set up SSH
|
|
61
|
+
# keys - see below)
|
|
62
|
+
# keyfile: Global default A file containing a private key that allows the named user access
|
|
63
|
+
# to the server, or...
|
|
64
|
+
# key: Global default A private key that allows the named user access to the server
|
|
65
|
+
# path: Global default An absolute path to deploy the app on each server. %a will be
|
|
66
|
+
# translated to the application name. %u will be translated to the
|
|
67
|
+
# login user name
|
|
68
|
+
# database: Environment default The database.yml elements to write to the config folder
|
|
69
|
+
# key: value
|
|
70
|
+
# servers: [server1, server2, server3]
|
|
71
|
+
# Role level Which servers to deploy the app on
|
|
72
|
+
# env: Role level Contents of the .env file
|
|
73
|
+
# key: value Values will go into the .env file as key=value
|
|
74
|
+
# foreman: Role level Contents of the .foreman file
|
|
75
|
+
# key: value
|
|
76
|
+
#
|
|
77
|
+
defaults:
|
|
78
|
+
name: %q{TODO: Add the app name}
|
|
79
|
+
repository: %q{TODO: Add the git repository path}
|
|
80
|
+
user: %q{TODO: Add the user to logon to the deployment server}
|
|
81
|
+
password: %q{TODO: Add the password for the user on the deployment server}
|
|
82
|
+
path: %q{TODO: Add the path to deploy to on the deployment server}
|
|
83
|
+
production:
|
|
84
|
+
defaults:
|
|
85
|
+
database:
|
|
86
|
+
adapter: postgresql
|
|
87
|
+
encoding: utf8
|
|
88
|
+
database: %q{TODO: Add the database name}
|
|
89
|
+
pool: 5
|
|
90
|
+
host: %q{TODO: Add the database host name}
|
|
91
|
+
username: %q{TODO: Add the database user}
|
|
92
|
+
password: %q{TODO: Add the database user's password}
|
|
93
|
+
web:
|
|
94
|
+
servers: [%q{TODO: Add the name of the production web server}]
|
|
95
|
+
foreman:
|
|
96
|
+
concurrency: 'web=1,worker=0,scheduler=0'
|
|
97
|
+
"""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'aruba/cucumber'
|
data/foreplay.gemspec
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
3
|
+
require 'foreplay/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "foreplay"
|
|
7
|
+
spec.version = Foreplay::VERSION
|
|
8
|
+
spec.authors = ["Xenapto"]
|
|
9
|
+
spec.email = ["developers@xenapto.com"]
|
|
10
|
+
spec.description = %q{Deploying Rails projects to Ubuntu using Foreman}
|
|
11
|
+
spec.summary = %q{Example: foreplay push to production}
|
|
12
|
+
spec.homepage = "https://github.com/Xenapto/foreplay"
|
|
13
|
+
spec.license = "MIT"
|
|
14
|
+
|
|
15
|
+
spec.files = `git ls-files`.split($/)
|
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
18
|
+
spec.require_paths = ["lib"]
|
|
19
|
+
|
|
20
|
+
spec.add_dependency 'activesupport'
|
|
21
|
+
spec.add_dependency 'colorize'
|
|
22
|
+
spec.add_dependency 'hashie'
|
|
23
|
+
spec.add_dependency 'foreman'
|
|
24
|
+
spec.add_dependency 'net-ssh-shell'
|
|
25
|
+
spec.add_dependency 'thor'
|
|
26
|
+
|
|
27
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
|
28
|
+
spec.add_development_dependency "rake"
|
|
29
|
+
spec.add_development_dependency "rspec", "~> 2.6"
|
|
30
|
+
spec.add_development_dependency "cucumber"
|
|
31
|
+
spec.add_development_dependency "aruba"
|
|
32
|
+
spec.add_development_dependency "gem-release"
|
|
33
|
+
end
|
data/foreplay.rake
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Syntax:
|
|
2
|
+
#
|
|
3
|
+
# rake ENV=production [ROLE=web] foreplay:command
|
|
4
|
+
#
|
|
5
|
+
# You can set the environment variables ENV and ROLE elsewhere if you wish.
|
|
6
|
+
# If ROLE is not defined then we deploy all roles.
|
|
7
|
+
#
|
|
8
|
+
# Dependencies:
|
|
9
|
+
#
|
|
10
|
+
# gem 'net-ssh-shell'
|
|
11
|
+
#
|
|
12
|
+
# You can constrain this to whatever group you use for initiating deployments, e.g.
|
|
13
|
+
#
|
|
14
|
+
# group :development do
|
|
15
|
+
# gem 'net-ssh-shell'
|
|
16
|
+
# end
|
|
17
|
+
|
|
18
|
+
namespace :foreplay do
|
|
19
|
+
desc 'Push app to deployment targets'
|
|
20
|
+
task :push => :environment do
|
|
21
|
+
Foreplay::push
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
desc 'Check deployment configuration'
|
|
25
|
+
task :check => :environment do
|
|
26
|
+
Foreplay::push true
|
|
27
|
+
end
|
|
28
|
+
end
|
data/foreplay.rb
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
require 'yaml'
|
|
3
|
+
require 'net/ssh'
|
|
4
|
+
|
|
5
|
+
class Foreplay
|
|
6
|
+
class << self
|
|
7
|
+
def push check_only = false
|
|
8
|
+
environment = ENV['ENV']
|
|
9
|
+
config_file = "#{Rails.root}/config/foreplay.yml"
|
|
10
|
+
config_all = YAML.load(File.read(config_file))
|
|
11
|
+
|
|
12
|
+
# This environment
|
|
13
|
+
raise RuntimeError, "No deployment environment defined. Set the ENV environment variable." if environment.blank?
|
|
14
|
+
raise RuntimeError, "No deployment configuration defined for #{environment} environment. Check #{config_file}" unless config_all.has_key? environment
|
|
15
|
+
config = config_all[environment]
|
|
16
|
+
|
|
17
|
+
# Establish defaults
|
|
18
|
+
# First the default defaults
|
|
19
|
+
defaults = {
|
|
20
|
+
'name' => Rails.application.class.parent_name.underscore,
|
|
21
|
+
'environment' => environment,
|
|
22
|
+
'env' => { 'RAILS_ENV' => environment }
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
defaults = deep_merge_with_arrays(defaults, config_all['defaults']) if config_all.has_key? 'defaults' # Then the global defaults
|
|
26
|
+
defaults = deep_merge_with_arrays(defaults, config['defaults']) if config.has_key? 'defaults' # Then the defaults for this environment
|
|
27
|
+
|
|
28
|
+
config.each do |role, additional_instructions|
|
|
29
|
+
next if role == 'defaults' # 'defaults' is not a role
|
|
30
|
+
next unless ENV['ROLE'].blank? || ENV['ROLE'] == role # Only deploy to the role we've specified (or all roles if none is specified)
|
|
31
|
+
|
|
32
|
+
instructions = deep_merge_with_arrays(defaults, additional_instructions).symbolize_keys
|
|
33
|
+
instructions[:role] = role
|
|
34
|
+
required_keys = [:name, :environment, :role, :servers, :path, :repository]
|
|
35
|
+
|
|
36
|
+
required_keys.each { |key| raise RuntimeError, "Required key #{key} not found in instructions for #{environment} environment. Check #{config_file}" unless instructions.has_key? key }
|
|
37
|
+
|
|
38
|
+
deploy_role instructions unless check_only
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
puts check_only ? 'Deployment configuration check was successful' : 'Finished deployment'
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def deploy_role instructions
|
|
45
|
+
servers = instructions[:servers]
|
|
46
|
+
puts "Deploying #{instructions[:name]} to #{servers.join(', ')} for the #{instructions[:role]} role in the #{instructions[:environment]} environment..." if servers.length > 1
|
|
47
|
+
servers.each { |server| deploy_to_server server, instructions }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def deploy_to_server server, instructions
|
|
51
|
+
name = instructions[:name]
|
|
52
|
+
environment = instructions[:environment]
|
|
53
|
+
role = instructions[:role]
|
|
54
|
+
path = instructions[:path]
|
|
55
|
+
repository = instructions[:repository]
|
|
56
|
+
user = instructions[:user]
|
|
57
|
+
|
|
58
|
+
instructions[:server] = server
|
|
59
|
+
|
|
60
|
+
puts "Deploying #{name} to #{server} for the #{role} role in the #{environment} environment"
|
|
61
|
+
|
|
62
|
+
# Substitute variables in the path
|
|
63
|
+
path.sub! '%u', user
|
|
64
|
+
path.sub! '%a', name
|
|
65
|
+
|
|
66
|
+
# Find out which port we're currently running on
|
|
67
|
+
steps = [ { :command => 'mkdir -p .foreplay && touch .foreplay/current_port && cat .foreplay/current_port', :silent => true } ]
|
|
68
|
+
|
|
69
|
+
current_port = execute_on_server(steps, instructions).strip!
|
|
70
|
+
puts "Current instance is using port #{current_port}"
|
|
71
|
+
|
|
72
|
+
# Switch ports
|
|
73
|
+
if current_port == '50000'
|
|
74
|
+
current_port = '51000'
|
|
75
|
+
former_port = '50000'
|
|
76
|
+
else
|
|
77
|
+
current_port = '50000'
|
|
78
|
+
former_port = '51000'
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Contents of .foreman file
|
|
82
|
+
current_service = '%s-%s' % [name, current_port]
|
|
83
|
+
former_service = '%s-%s' % [name, former_port]
|
|
84
|
+
|
|
85
|
+
instructions[:foreman]['app'] = current_service
|
|
86
|
+
instructions[:foreman]['port'] = current_port
|
|
87
|
+
instructions[:foreman]['user'] = user
|
|
88
|
+
|
|
89
|
+
# Commands to execute on remote server
|
|
90
|
+
steps = [
|
|
91
|
+
{ :command => "echo #{current_port} > .foreplay/current_port" },
|
|
92
|
+
{ :command => "mkdir -p #{path} && cd #{path} && rm -rf #{current_port} && git clone #{repository} #{current_port}",
|
|
93
|
+
:commentary => "Cloning repository #{repository}" },
|
|
94
|
+
{ :command => "rvm rvmrc trust #{current_port}" },
|
|
95
|
+
{ :command => "cd #{current_port}" },
|
|
96
|
+
{ :key => :env,
|
|
97
|
+
:delimiter => '=',
|
|
98
|
+
:prefix => '.',
|
|
99
|
+
:commentary => 'Building .env' },
|
|
100
|
+
{ :key => :foreman,
|
|
101
|
+
:delimiter => ': ',
|
|
102
|
+
:prefix => '.',
|
|
103
|
+
:commentary => 'Building .foreman' },
|
|
104
|
+
{ :key => :database,
|
|
105
|
+
:delimiter => ': ',
|
|
106
|
+
:suffix => '.yml',
|
|
107
|
+
:commentary => 'Building config/database.yml',
|
|
108
|
+
:before => ' ',
|
|
109
|
+
:header => "#{environment}:",
|
|
110
|
+
:path => 'config/' },
|
|
111
|
+
{ :command => "bundle install" },
|
|
112
|
+
{ :command => "sudo ln -f `which foreman` /usr/bin/foreman" },
|
|
113
|
+
{ :command => "sudo foreman export upstart /etc/init" },
|
|
114
|
+
{ :command => "sudo start #{current_service} || sudo restart #{current_service}",
|
|
115
|
+
:ignore_error => true },
|
|
116
|
+
{ :command => 'sleep 60',
|
|
117
|
+
:commentary => 'Waiting 60s to give service time to start' },
|
|
118
|
+
{ :command => "sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port #{current_port.to_i + 100}",
|
|
119
|
+
:commentary => "Adding firewall rule to direct incoming traffic on port 80 to port #{current_port}" },
|
|
120
|
+
{ :command => "sudo iptables -t nat -D PREROUTING -p tcp --dport 80 -j REDIRECT --to-port #{former_port.to_i + 100}",
|
|
121
|
+
:commentary => "Removing previous firewall directing traffic to port #{former_port}",
|
|
122
|
+
:ignore_error => true },
|
|
123
|
+
{ :command => "sudo iptables -t nat -L | grep REDIRECT",
|
|
124
|
+
:ignore_error => true,
|
|
125
|
+
:commentary => "Current firewall NAT configuration:" },
|
|
126
|
+
{ :command => "sudo stop #{former_service} || echo 'No previous instance running'",
|
|
127
|
+
:ignore_error => true },
|
|
128
|
+
]
|
|
129
|
+
|
|
130
|
+
execute_on_server steps, instructions
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def execute_on_server steps, instructions
|
|
134
|
+
server = instructions[:server]
|
|
135
|
+
user = instructions[:user]
|
|
136
|
+
password = instructions[:password]
|
|
137
|
+
keyfile = instructions[:keyfile]
|
|
138
|
+
key = instructions[:key]
|
|
139
|
+
|
|
140
|
+
keyfile.sub! '~' , ENV['HOME'] # Remote shell won't expand this for us
|
|
141
|
+
|
|
142
|
+
# SSH authentication methods
|
|
143
|
+
options = { :verbose => :warn }
|
|
144
|
+
|
|
145
|
+
if password.blank?
|
|
146
|
+
# If there's no password we must supply a private key
|
|
147
|
+
if key.blank?
|
|
148
|
+
raise RuntimeError, "No authentication methods supplied. You must supply a private key, key file or password in the configuration file" if keyfile.blank?
|
|
149
|
+
# Get the key from the key file
|
|
150
|
+
puts "Using private key from #{keyfile}"
|
|
151
|
+
key = File.read keyfile
|
|
152
|
+
else
|
|
153
|
+
puts "Using private key from the configuration file"
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
options[:key_data] = [key]
|
|
157
|
+
else
|
|
158
|
+
# Use the password supplied
|
|
159
|
+
options[:password] = password
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Capture output of last command to return to the calling routine
|
|
163
|
+
output = ''
|
|
164
|
+
|
|
165
|
+
# SSH connection
|
|
166
|
+
Net::SSH.start(server, user, options) do |ssh|
|
|
167
|
+
puts "Successfully connected to #{server}"
|
|
168
|
+
|
|
169
|
+
ssh.shell do |sh|
|
|
170
|
+
steps.each do |step|
|
|
171
|
+
# Output from this step
|
|
172
|
+
output = ''
|
|
173
|
+
previous = '' # We don't need or want the final CRLF
|
|
174
|
+
|
|
175
|
+
puts step[:commentary] || step[:command] unless step[:silent] == true
|
|
176
|
+
|
|
177
|
+
# Each step can be (1) a command or (2) a series of values to add to a file
|
|
178
|
+
if step.has_key? :key
|
|
179
|
+
step[:silent] = true
|
|
180
|
+
|
|
181
|
+
# Add values from the config file to a file on the remote machine
|
|
182
|
+
key = step[:key]
|
|
183
|
+
prefix = step[:prefix] || ''
|
|
184
|
+
suffix = step[:suffix] || ''
|
|
185
|
+
path = step[:path] || ''
|
|
186
|
+
before = step[:before] || ''
|
|
187
|
+
delimiter = step[:delimiter] || ''
|
|
188
|
+
after = step[:after] || ''
|
|
189
|
+
|
|
190
|
+
filename = '%s%s%s%s' % [path, prefix, key, suffix]
|
|
191
|
+
commands = step.has_key?(:header) ? ['echo "%s" >> %s' % [step[:header], filename]] : []
|
|
192
|
+
|
|
193
|
+
instructions[key].each { |k, v| commands << 'echo "%s%s%s%s%s" >> %s' % [before, k, delimiter, v, after, filename] }
|
|
194
|
+
else
|
|
195
|
+
# ...or just execute the command specified
|
|
196
|
+
commands = [step[:command]]
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
commands.each do |command|
|
|
200
|
+
process = sh.execute command
|
|
201
|
+
|
|
202
|
+
process.on_output do |p, o|
|
|
203
|
+
previous = o
|
|
204
|
+
output += previous
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
sh.wait!
|
|
208
|
+
|
|
209
|
+
if step[:ignore_error] == true || process.exit_status == 0
|
|
210
|
+
print "#{output}" unless step[:silent] == true
|
|
211
|
+
else
|
|
212
|
+
raise RuntimeError, output
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
output
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# Returns a new hash with +hash+ and +other_hash+ merged recursively, including arrays.
|
|
223
|
+
#
|
|
224
|
+
# h1 = { x: { y: [4,5,6] }, z: [7,8,9] }
|
|
225
|
+
# h2 = { x: { y: [7,8,9] }, z: 'xyz' }
|
|
226
|
+
# h1.deep_merge_with_arrays(h2)
|
|
227
|
+
# #=> {:x=>{:y=>[4, 5, 6, 7, 8, 9]}, :z=>[7, 8, 9, "xyz"]}
|
|
228
|
+
def deep_merge_with_arrays(hash, other_hash)
|
|
229
|
+
new_hash = hash.deep_dup
|
|
230
|
+
|
|
231
|
+
other_hash.each_pair do |k,v|
|
|
232
|
+
tv = new_hash[k]
|
|
233
|
+
|
|
234
|
+
if tv.is_a?(Hash) && v.is_a?(Hash)
|
|
235
|
+
new_hash[k] = deep_merge_with_arrays(tv, v)
|
|
236
|
+
elsif tv.is_a?(Array) || v.is_a?(Array)
|
|
237
|
+
new_hash[k] = Array.wrap(tv) + Array.wrap(v)
|
|
238
|
+
else
|
|
239
|
+
new_hash[k] = v
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
new_hash
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
end
|
data/lib/foreplay/cli.rb
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
require 'thor'
|
|
2
|
+
require 'foreplay'
|
|
3
|
+
require 'foreplay/generators/setup'
|
|
4
|
+
|
|
5
|
+
module Foreplay
|
|
6
|
+
class CLI < Thor
|
|
7
|
+
desc 'check', 'Checks if configuration is OK'
|
|
8
|
+
|
|
9
|
+
method_option :environment, :aliases => "-e"
|
|
10
|
+
method_option :role, :aliases => "-r"
|
|
11
|
+
method_option :server, :aliases => "-s"
|
|
12
|
+
|
|
13
|
+
def check
|
|
14
|
+
puts Foreplay::Config.check options
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
desc 'setup', 'Create the Foreplay config file'
|
|
18
|
+
|
|
19
|
+
def setup
|
|
20
|
+
Foreplay::Generators::Setup.start
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require 'active_support/inflector'
|
|
2
|
+
require 'colorize'
|
|
3
|
+
require 'hashie/mash'
|
|
4
|
+
|
|
5
|
+
module Foreplay
|
|
6
|
+
class Config
|
|
7
|
+
def self.check *args
|
|
8
|
+
options = Hashie::Mash[args.first]
|
|
9
|
+
|
|
10
|
+
# Explain what we're going to do
|
|
11
|
+
environments = explanatory_text options.environment, 'environment'
|
|
12
|
+
roles = explanatory_text options.role, 'role'
|
|
13
|
+
servers = explanatory_text options.server, 'server'
|
|
14
|
+
puts 'Checking configuration for %s, %s, %s' % [environments, roles, servers]
|
|
15
|
+
|
|
16
|
+
'Not finished'
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def self.explanatory_text(value, singular_word)
|
|
22
|
+
value.nil? ? "all #{singular_word.pluralize}" : "#{value.dup.yellow} #{singular_word}"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Format:
|
|
2
|
+
#
|
|
3
|
+
# There is a section for each environment that you will deploy to, plus a section that defines global default
|
|
4
|
+
# values for all environments, like this:
|
|
5
|
+
#
|
|
6
|
+
# defaults:
|
|
7
|
+
# ...
|
|
8
|
+
# production:
|
|
9
|
+
# ...
|
|
10
|
+
# staging:
|
|
11
|
+
# ...
|
|
12
|
+
#
|
|
13
|
+
# Within each section you can define the server roles for that environment: web, worker, database etc. (the
|
|
14
|
+
# names of these roles are up to you). You can also define environment-level defaults that apply to all roles.
|
|
15
|
+
# Like this:
|
|
16
|
+
#
|
|
17
|
+
# production:
|
|
18
|
+
# defaults:
|
|
19
|
+
# ...
|
|
20
|
+
# web:
|
|
21
|
+
# ...
|
|
22
|
+
# worker:
|
|
23
|
+
# ...
|
|
24
|
+
# scheduler:
|
|
25
|
+
# ...
|
|
26
|
+
# database:
|
|
27
|
+
# ...
|
|
28
|
+
# staging:
|
|
29
|
+
# defaults:
|
|
30
|
+
# ...
|
|
31
|
+
# web:
|
|
32
|
+
# ...
|
|
33
|
+
# worker:
|
|
34
|
+
# ...
|
|
35
|
+
# scheduler:
|
|
36
|
+
# ...
|
|
37
|
+
# database:
|
|
38
|
+
# ...
|
|
39
|
+
#
|
|
40
|
+
# Within each role section you can define how the deployment is configured for the servers in that role.
|
|
41
|
+
# Some of these values will normally be defined as a default, some will be specific to a particular role.
|
|
42
|
+
# The values you can configure are as follows:
|
|
43
|
+
#
|
|
44
|
+
# value Normally defined as Notes
|
|
45
|
+
# ------------- -------------------- -------------------------------------------------------------------
|
|
46
|
+
# name: Global default App name (if omitted then
|
|
47
|
+
# Rails.application.class.parent_name.underscore is used)
|
|
48
|
+
# user: Global default The username to connect with (must have SSH permissions)
|
|
49
|
+
# password: Global default The password to use to connect (not necessary if you've set up SSH
|
|
50
|
+
# keys - see below)
|
|
51
|
+
# keyfile: Global default A file containing a private key that allows the named user access
|
|
52
|
+
# to the server, or...
|
|
53
|
+
# key: Global default A private key that allows the named user access to the server
|
|
54
|
+
# path: Global default An absolute path to deploy the app on each server. %a will be
|
|
55
|
+
# translated to the application name. %u will be translated to the
|
|
56
|
+
# login user name
|
|
57
|
+
# database: Environment default The database.yml elements to write to the config folder
|
|
58
|
+
# key: value
|
|
59
|
+
# servers: [server1, server2, server3]
|
|
60
|
+
# Role level Which servers to deploy the app on
|
|
61
|
+
# env: Role level Contents of the .env file
|
|
62
|
+
# key: value Values will go into the .env file as key=value
|
|
63
|
+
# foreman: Role level Contents of the .foreman file
|
|
64
|
+
# key: value
|
|
65
|
+
#
|
|
66
|
+
defaults:
|
|
67
|
+
name: <%= @name %>
|
|
68
|
+
repository: %q{TODO: Add the git repository path}
|
|
69
|
+
user: %q{TODO: Add the user to logon to the deployment server}
|
|
70
|
+
password: %q{TODO: Add the password for the user on the deployment server}
|
|
71
|
+
path: %q{TODO: Add the path to deploy to on the deployment server}
|
|
72
|
+
production:
|
|
73
|
+
defaults:
|
|
74
|
+
database:
|
|
75
|
+
adapter: postgresql
|
|
76
|
+
encoding: utf8
|
|
77
|
+
database: %q{TODO: Add the database name}
|
|
78
|
+
pool: 5
|
|
79
|
+
host: %q{TODO: Add the database host name}
|
|
80
|
+
username: %q{TODO: Add the database user}
|
|
81
|
+
password: %q{TODO: Add the database user's password}
|
|
82
|
+
web:
|
|
83
|
+
servers: [%q{TODO: Add the name of the production web server}]
|
|
84
|
+
foreman:
|
|
85
|
+
concurrency: 'web=1,worker=0,scheduler=0'
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require 'thor/group'
|
|
2
|
+
|
|
3
|
+
module Foreplay
|
|
4
|
+
module Generators
|
|
5
|
+
class Setup < Thor::Group
|
|
6
|
+
include Thor::Actions
|
|
7
|
+
|
|
8
|
+
Rails ||= nil
|
|
9
|
+
|
|
10
|
+
def self.source_root
|
|
11
|
+
File.dirname(__FILE__)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def create_config_file
|
|
15
|
+
@name = Rails.nil? ? '%q{TODO: Add the app name}' : Rails.application.class.parent_name.underscore
|
|
16
|
+
template('foreplay.yml', 'config/foreplay.yml')
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
data/lib/foreplay.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: foreplay
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Xenapto
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2013-08-09 00:00:00.000000000 Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: activesupport
|
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
|
17
|
+
none: false
|
|
18
|
+
requirements:
|
|
19
|
+
- - ! '>='
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '0'
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
none: false
|
|
26
|
+
requirements:
|
|
27
|
+
- - ! '>='
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: '0'
|
|
30
|
+
- !ruby/object:Gem::Dependency
|
|
31
|
+
name: colorize
|
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
|
33
|
+
none: false
|
|
34
|
+
requirements:
|
|
35
|
+
- - ! '>='
|
|
36
|
+
- !ruby/object:Gem::Version
|
|
37
|
+
version: '0'
|
|
38
|
+
type: :runtime
|
|
39
|
+
prerelease: false
|
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
41
|
+
none: false
|
|
42
|
+
requirements:
|
|
43
|
+
- - ! '>='
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '0'
|
|
46
|
+
- !ruby/object:Gem::Dependency
|
|
47
|
+
name: hashie
|
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
|
49
|
+
none: false
|
|
50
|
+
requirements:
|
|
51
|
+
- - ! '>='
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0'
|
|
54
|
+
type: :runtime
|
|
55
|
+
prerelease: false
|
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
57
|
+
none: false
|
|
58
|
+
requirements:
|
|
59
|
+
- - ! '>='
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
62
|
+
- !ruby/object:Gem::Dependency
|
|
63
|
+
name: foreman
|
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
|
65
|
+
none: false
|
|
66
|
+
requirements:
|
|
67
|
+
- - ! '>='
|
|
68
|
+
- !ruby/object:Gem::Version
|
|
69
|
+
version: '0'
|
|
70
|
+
type: :runtime
|
|
71
|
+
prerelease: false
|
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
73
|
+
none: false
|
|
74
|
+
requirements:
|
|
75
|
+
- - ! '>='
|
|
76
|
+
- !ruby/object:Gem::Version
|
|
77
|
+
version: '0'
|
|
78
|
+
- !ruby/object:Gem::Dependency
|
|
79
|
+
name: net-ssh-shell
|
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
|
81
|
+
none: false
|
|
82
|
+
requirements:
|
|
83
|
+
- - ! '>='
|
|
84
|
+
- !ruby/object:Gem::Version
|
|
85
|
+
version: '0'
|
|
86
|
+
type: :runtime
|
|
87
|
+
prerelease: false
|
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
89
|
+
none: false
|
|
90
|
+
requirements:
|
|
91
|
+
- - ! '>='
|
|
92
|
+
- !ruby/object:Gem::Version
|
|
93
|
+
version: '0'
|
|
94
|
+
- !ruby/object:Gem::Dependency
|
|
95
|
+
name: thor
|
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
|
97
|
+
none: false
|
|
98
|
+
requirements:
|
|
99
|
+
- - ! '>='
|
|
100
|
+
- !ruby/object:Gem::Version
|
|
101
|
+
version: '0'
|
|
102
|
+
type: :runtime
|
|
103
|
+
prerelease: false
|
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
105
|
+
none: false
|
|
106
|
+
requirements:
|
|
107
|
+
- - ! '>='
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: '0'
|
|
110
|
+
- !ruby/object:Gem::Dependency
|
|
111
|
+
name: bundler
|
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
|
113
|
+
none: false
|
|
114
|
+
requirements:
|
|
115
|
+
- - ~>
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: '1.3'
|
|
118
|
+
type: :development
|
|
119
|
+
prerelease: false
|
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
+
none: false
|
|
122
|
+
requirements:
|
|
123
|
+
- - ~>
|
|
124
|
+
- !ruby/object:Gem::Version
|
|
125
|
+
version: '1.3'
|
|
126
|
+
- !ruby/object:Gem::Dependency
|
|
127
|
+
name: rake
|
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
|
129
|
+
none: false
|
|
130
|
+
requirements:
|
|
131
|
+
- - ! '>='
|
|
132
|
+
- !ruby/object:Gem::Version
|
|
133
|
+
version: '0'
|
|
134
|
+
type: :development
|
|
135
|
+
prerelease: false
|
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
137
|
+
none: false
|
|
138
|
+
requirements:
|
|
139
|
+
- - ! '>='
|
|
140
|
+
- !ruby/object:Gem::Version
|
|
141
|
+
version: '0'
|
|
142
|
+
- !ruby/object:Gem::Dependency
|
|
143
|
+
name: rspec
|
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
|
145
|
+
none: false
|
|
146
|
+
requirements:
|
|
147
|
+
- - ~>
|
|
148
|
+
- !ruby/object:Gem::Version
|
|
149
|
+
version: '2.6'
|
|
150
|
+
type: :development
|
|
151
|
+
prerelease: false
|
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
153
|
+
none: false
|
|
154
|
+
requirements:
|
|
155
|
+
- - ~>
|
|
156
|
+
- !ruby/object:Gem::Version
|
|
157
|
+
version: '2.6'
|
|
158
|
+
- !ruby/object:Gem::Dependency
|
|
159
|
+
name: cucumber
|
|
160
|
+
requirement: !ruby/object:Gem::Requirement
|
|
161
|
+
none: false
|
|
162
|
+
requirements:
|
|
163
|
+
- - ! '>='
|
|
164
|
+
- !ruby/object:Gem::Version
|
|
165
|
+
version: '0'
|
|
166
|
+
type: :development
|
|
167
|
+
prerelease: false
|
|
168
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
169
|
+
none: false
|
|
170
|
+
requirements:
|
|
171
|
+
- - ! '>='
|
|
172
|
+
- !ruby/object:Gem::Version
|
|
173
|
+
version: '0'
|
|
174
|
+
- !ruby/object:Gem::Dependency
|
|
175
|
+
name: aruba
|
|
176
|
+
requirement: !ruby/object:Gem::Requirement
|
|
177
|
+
none: false
|
|
178
|
+
requirements:
|
|
179
|
+
- - ! '>='
|
|
180
|
+
- !ruby/object:Gem::Version
|
|
181
|
+
version: '0'
|
|
182
|
+
type: :development
|
|
183
|
+
prerelease: false
|
|
184
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
185
|
+
none: false
|
|
186
|
+
requirements:
|
|
187
|
+
- - ! '>='
|
|
188
|
+
- !ruby/object:Gem::Version
|
|
189
|
+
version: '0'
|
|
190
|
+
- !ruby/object:Gem::Dependency
|
|
191
|
+
name: gem-release
|
|
192
|
+
requirement: !ruby/object:Gem::Requirement
|
|
193
|
+
none: false
|
|
194
|
+
requirements:
|
|
195
|
+
- - ! '>='
|
|
196
|
+
- !ruby/object:Gem::Version
|
|
197
|
+
version: '0'
|
|
198
|
+
type: :development
|
|
199
|
+
prerelease: false
|
|
200
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
201
|
+
none: false
|
|
202
|
+
requirements:
|
|
203
|
+
- - ! '>='
|
|
204
|
+
- !ruby/object:Gem::Version
|
|
205
|
+
version: '0'
|
|
206
|
+
description: Deploying Rails projects to Ubuntu using Foreman
|
|
207
|
+
email:
|
|
208
|
+
- developers@xenapto.com
|
|
209
|
+
executables:
|
|
210
|
+
- foreplay
|
|
211
|
+
extensions: []
|
|
212
|
+
extra_rdoc_files: []
|
|
213
|
+
files:
|
|
214
|
+
- .gitignore
|
|
215
|
+
- Gemfile
|
|
216
|
+
- LICENSE.txt
|
|
217
|
+
- README.md
|
|
218
|
+
- Rakefile
|
|
219
|
+
- bin/foreplay
|
|
220
|
+
- features/config.feature
|
|
221
|
+
- features/generator.feature
|
|
222
|
+
- features/support/setup.rb
|
|
223
|
+
- foreplay.gemspec
|
|
224
|
+
- foreplay.rake
|
|
225
|
+
- foreplay.rb
|
|
226
|
+
- lib/foreplay.rb
|
|
227
|
+
- lib/foreplay/cli.rb
|
|
228
|
+
- lib/foreplay/config.rb
|
|
229
|
+
- lib/foreplay/generators/foreplay.yml
|
|
230
|
+
- lib/foreplay/generators/setup.rb
|
|
231
|
+
- lib/foreplay/version.rb
|
|
232
|
+
- spec/foreplay_spec.rb
|
|
233
|
+
homepage: https://github.com/Xenapto/foreplay
|
|
234
|
+
licenses:
|
|
235
|
+
- MIT
|
|
236
|
+
post_install_message:
|
|
237
|
+
rdoc_options: []
|
|
238
|
+
require_paths:
|
|
239
|
+
- lib
|
|
240
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
241
|
+
none: false
|
|
242
|
+
requirements:
|
|
243
|
+
- - ! '>='
|
|
244
|
+
- !ruby/object:Gem::Version
|
|
245
|
+
version: '0'
|
|
246
|
+
segments:
|
|
247
|
+
- 0
|
|
248
|
+
hash: -929256133810576486
|
|
249
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
250
|
+
none: false
|
|
251
|
+
requirements:
|
|
252
|
+
- - ! '>='
|
|
253
|
+
- !ruby/object:Gem::Version
|
|
254
|
+
version: '0'
|
|
255
|
+
segments:
|
|
256
|
+
- 0
|
|
257
|
+
hash: -929256133810576486
|
|
258
|
+
requirements: []
|
|
259
|
+
rubyforge_project:
|
|
260
|
+
rubygems_version: 1.8.25
|
|
261
|
+
signing_key:
|
|
262
|
+
specification_version: 3
|
|
263
|
+
summary: ! 'Example: foreplay push to production'
|
|
264
|
+
test_files:
|
|
265
|
+
- features/config.feature
|
|
266
|
+
- features/generator.feature
|
|
267
|
+
- features/support/setup.rb
|
|
268
|
+
- spec/foreplay_spec.rb
|