foreplay 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|