shipment-deploy 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.rspec +4 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +82 -0
- data/Rakefile +2 -0
- data/bin/ship +9 -0
- data/lib/shipment.rb +6 -0
- data/lib/shipment/cli.rb +56 -0
- data/lib/shipment/credentials_checker.rb +23 -0
- data/lib/shipment/mooring.rb +156 -0
- data/lib/shipment/project/customizer.rb +99 -0
- data/lib/shipment/project/repo.rb +39 -0
- data/lib/shipment/rigging.rb +16 -0
- data/lib/shipment/server/initializer.rb +121 -0
- data/lib/shipment/server/ssh_client.rb +218 -0
- data/lib/shipment/slip.rb +18 -0
- data/lib/shipment/version.rb +4 -0
- data/shipment.gemspec +36 -0
- data/spec/spec_helper.rb +18 -0
- metadata +236 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 86469817a1abdb8afef74ba478c6fbcc4cd09e6d
|
4
|
+
data.tar.gz: c1d42b3aa527d742484ca4a0584ce949fdf67a03
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 26e85ca97548fb373f653d9e4316cd48a89661b7046637410b6ae3f452892b28d7fb35e0db99604e240f9db6d12c0766ae27241f5a4ec7ce553e18d34186b4cf
|
7
|
+
data.tar.gz: f326df3df0e58440109d16598e947469a2d92e2ebc16fbe3ddc9d0668c0293bd3db5dc2c25663e8fbf686e369753d98fba5b55effa548c6a73bbf7b60781e32c
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Logan Hasson
|
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,82 @@
|
|
1
|
+
# Shipment
|
2
|
+
|
3
|
+
Easy deployment using Docker
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
$ gem install shipment
|
8
|
+
|
9
|
+
## Usage
|
10
|
+
|
11
|
+
$ ship lash # Setup credentials
|
12
|
+
|
13
|
+
$ ship this # Prepare application for deployment
|
14
|
+
|
15
|
+
$ ship out # Deploy application
|
16
|
+
|
17
|
+
## What It Does
|
18
|
+
|
19
|
+
1. Sets up a droplet on DigitalOcean with the proper SSH key
|
20
|
+
2. Creates two Docker containers on the droplet: one for your
|
21
|
+
application, and one for a Postgres database
|
22
|
+
3. Sets up a deploy key for your application's repo on GitHub
|
23
|
+
4. Adds a `.shipment` file to your project containing
|
24
|
+
application-specific information
|
25
|
+
5. Adds an entry to a global `.shipment` file in your home directory to
|
26
|
+
keep track of all deployments
|
27
|
+
|
28
|
+
## Notes
|
29
|
+
|
30
|
+
1. Setup takes between 5 and 10 minutes, depending upon the speed of
|
31
|
+
DigitalOcean
|
32
|
+
2. During setup, green arrows indicate local ouptut, blue arrows
|
33
|
+
indicate commands run on the server, red arrows indicate failed
|
34
|
+
commands and/or connection timeouts, and unmarked output represents
|
35
|
+
stdout on the server.
|
36
|
+
3. Some output is printed in red, even though there is no error. Some
|
37
|
+
server output is being improperly redirected to stderr.
|
38
|
+
4. Currently, the only option is to set up a 2gb droplet in the NYC2
|
39
|
+
region.
|
40
|
+
5. Currently, the server runs on port 3000.
|
41
|
+
|
42
|
+
## Todo
|
43
|
+
|
44
|
+
1. Only add `.shipmet` to `.gitignore` once, duh!
|
45
|
+
2. Handle SSH authorization issues when re-setting up. DigitalOcean
|
46
|
+
allows adding multiple keys with the same name. Need to delete
|
47
|
+
existing key locally and remotely when running setup again. (Probably
|
48
|
+
should either remove `ship setup` command or do some checking when that
|
49
|
+
command is run.)
|
50
|
+
3. Need to deal with commit limit. Containers need to be exported every
|
51
|
+
once in a while to clean up Aufs layers.
|
52
|
+
4. Save and push application-specific docker images to user's docker
|
53
|
+
account.
|
54
|
+
5. Allow deploying from branches other than master.
|
55
|
+
6. Error handle *way* better. Need to be able to handle partial
|
56
|
+
setups/deploys.
|
57
|
+
7. Look into creating a base image that already includes ruby and
|
58
|
+
postgres containers. (Requires support from DigitalOcean.)
|
59
|
+
8. Look into using `docker attach` to allow viewing logs.
|
60
|
+
9. Better deal with committing and pushing on `ship this`. Right now,
|
61
|
+
rejected pushes would break everything.
|
62
|
+
10. Create simple way to symlink credentials from an `application.yml` or
|
63
|
+
`.env` file.
|
64
|
+
11. Fix messy output during setup and deploy. Also, deal with duplicate
|
65
|
+
'Done.'s when a command is re-run.
|
66
|
+
12. Speed everything up.
|
67
|
+
13. Clean up the classes. Most things are doing *way* too much.
|
68
|
+
14. Write specs!
|
69
|
+
15. Add cleanup option to remove all system config.
|
70
|
+
|
71
|
+
## Contributing
|
72
|
+
|
73
|
+
1. Fork it ( https://github.com/loganhasson/shipment/fork )
|
74
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
75
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
76
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
77
|
+
5. Create a new Pull Request
|
78
|
+
|
79
|
+
# Author
|
80
|
+
|
81
|
+
[@loganhasson](http://twitter.com/loganhasson)
|
82
|
+
|
data/Rakefile
ADDED
data/bin/ship
ADDED
data/lib/shipment.rb
ADDED
data/lib/shipment/cli.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'shipment/mooring'
|
3
|
+
require 'shipment/credentials_checker'
|
4
|
+
require 'shipment/rigging'
|
5
|
+
require 'shipment/slip'
|
6
|
+
|
7
|
+
module Shipment
|
8
|
+
|
9
|
+
class CLI < Thor
|
10
|
+
desc "lash", "Setup shipment with your GitHub and DigitalOcean credentials"
|
11
|
+
long_desc <<-LONGDESC
|
12
|
+
`ship lash` will collect your GitHub and DigitalOcean credentials.
|
13
|
+
LONGDESC
|
14
|
+
def lash
|
15
|
+
Shipment::Mooring.lash
|
16
|
+
end
|
17
|
+
|
18
|
+
desc "this", "Prepare your application for deployment"
|
19
|
+
long_desc <<-LONGDESC
|
20
|
+
`ship this` will setup all necessary files and prepare a remote server for
|
21
|
+
deployment.
|
22
|
+
|
23
|
+
This process includes adding the `mina` gem to your Gemfile, creating a new
|
24
|
+
`deploy.rb` file, altering your `database.yml` file for use on the remote
|
25
|
+
server, and ensuring that all necessary SSH keys are in place.
|
26
|
+
|
27
|
+
Alternate usage: `ship .`
|
28
|
+
LONGDESC
|
29
|
+
def this
|
30
|
+
if !Shipment::CredentialsChecker.verify
|
31
|
+
Shipment::Mooring.lash
|
32
|
+
end
|
33
|
+
|
34
|
+
Shipment::Rigging.rig
|
35
|
+
end
|
36
|
+
|
37
|
+
desc "out", "Deploy your application"
|
38
|
+
long_desc <<-LONGDESC
|
39
|
+
`ship out` will deploy your application's master branch to your DigitalOcean
|
40
|
+
server.
|
41
|
+
LONGDESC
|
42
|
+
def out
|
43
|
+
if !Shipment::CredentialsChecker.verify
|
44
|
+
Shipment::Mooring.lash
|
45
|
+
end
|
46
|
+
|
47
|
+
if !File.exist?('.shipment')
|
48
|
+
Shipment::Rigging.rig
|
49
|
+
end
|
50
|
+
|
51
|
+
Shipment::Slip.cast_off
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'netrc'
|
2
|
+
|
3
|
+
module Shipment
|
4
|
+
|
5
|
+
class CredentialsChecker
|
6
|
+
attr_reader :netrc
|
7
|
+
|
8
|
+
def self.verify
|
9
|
+
new.verify
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@netrc = Netrc.read
|
14
|
+
end
|
15
|
+
|
16
|
+
def verify
|
17
|
+
![netrc["shipment.gh"], netrc["shipment.do"]].flatten.any? do |cred|
|
18
|
+
cred.nil? || cred.empty? || cred == " "
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'netrc'
|
2
|
+
require 'sshkey'
|
3
|
+
require 'octokit'
|
4
|
+
require 'digitalocean'
|
5
|
+
require 'highline/import'
|
6
|
+
|
7
|
+
module Shipment
|
8
|
+
|
9
|
+
class Mooring
|
10
|
+
attr_accessor :gh_username, :gh_password, :gh_token,
|
11
|
+
:do_client_id, :do_api_key,
|
12
|
+
:ssh_key, :private_key, :public_key
|
13
|
+
def self.lash
|
14
|
+
new.lash
|
15
|
+
end
|
16
|
+
|
17
|
+
def lash
|
18
|
+
write_gh_creds
|
19
|
+
write_do_creds
|
20
|
+
do_ssh_success = setup_do_ssh_key
|
21
|
+
while !do_ssh_success
|
22
|
+
puts "Your DigitalOcean credentials are invalid. Please try again."
|
23
|
+
write_do_creds
|
24
|
+
do_ssh_success = setup_do_ssh_key
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def write_gh_creds
|
29
|
+
collect_gh_creds
|
30
|
+
write_creds(:gh)
|
31
|
+
end
|
32
|
+
|
33
|
+
def collect_gh_creds
|
34
|
+
self.gh_username = ask('GitHub Username: ')
|
35
|
+
self.gh_password = ask('GitHub Password (Never Stored): ') do |q|
|
36
|
+
q.echo = false
|
37
|
+
end
|
38
|
+
self.gh_token = get_gh_token
|
39
|
+
end
|
40
|
+
|
41
|
+
def get_gh_token
|
42
|
+
success = true
|
43
|
+
token_exists = false
|
44
|
+
|
45
|
+
client = Octokit::Client.new(login: gh_username, password: gh_password)
|
46
|
+
|
47
|
+
begin
|
48
|
+
authorization = client.create_authorization(
|
49
|
+
scopes: [
|
50
|
+
"user",
|
51
|
+
"repo",
|
52
|
+
"public_repo",
|
53
|
+
"write:public_key",
|
54
|
+
"read:public_key",
|
55
|
+
"read:org",
|
56
|
+
"read:repo_hook",
|
57
|
+
"write:repo_hook",
|
58
|
+
"repo_deployment",
|
59
|
+
"admin:public_key",
|
60
|
+
"admin:repo_hook"
|
61
|
+
],
|
62
|
+
note: "Shipment Token"
|
63
|
+
)
|
64
|
+
rescue Octokit::UnprocessableEntity => e
|
65
|
+
success = false
|
66
|
+
if !!e.message.match(/already_exists/)
|
67
|
+
token_exists = true
|
68
|
+
|
69
|
+
puts <<-MSG.gsub(/^ {12}/,'')
|
70
|
+
A token for Shipment already exists for your GitHub account. This
|
71
|
+
may or may not cause issues. Please visit your GitHub account,
|
72
|
+
delete the existing token, and try this setup again.
|
73
|
+
MSG
|
74
|
+
end
|
75
|
+
rescue Octokit::Unauthorized
|
76
|
+
success = false
|
77
|
+
puts "Your GitHub username and password are invalid. Please try again."
|
78
|
+
end
|
79
|
+
|
80
|
+
if success
|
81
|
+
return authorization[:token]
|
82
|
+
elsif token_exists
|
83
|
+
exit
|
84
|
+
else
|
85
|
+
collect_gh_creds
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def write_do_creds
|
90
|
+
collect_do_creds
|
91
|
+
write_creds(:do)
|
92
|
+
end
|
93
|
+
|
94
|
+
def collect_do_creds
|
95
|
+
self.do_client_id = ask("DigitalOcean Client ID: ")
|
96
|
+
self.do_api_key = ask("DigitalOcean API Key: ")
|
97
|
+
end
|
98
|
+
|
99
|
+
def write_creds(provider)
|
100
|
+
netrc = Netrc.read
|
101
|
+
|
102
|
+
if provider == :do
|
103
|
+
netrc["shipment.do"] = do_client_id, do_api_key
|
104
|
+
elsif provider == :gh
|
105
|
+
netrc["shipment.gh"] = gh_username, gh_token
|
106
|
+
end
|
107
|
+
|
108
|
+
netrc.save
|
109
|
+
end
|
110
|
+
|
111
|
+
def setup_do_ssh_key
|
112
|
+
create_key
|
113
|
+
transmit_do_key
|
114
|
+
end
|
115
|
+
|
116
|
+
def create_key
|
117
|
+
self.ssh_key = SSHKey.generate
|
118
|
+
self.private_key = write_private_key
|
119
|
+
self.public_key = write_public_key
|
120
|
+
end
|
121
|
+
|
122
|
+
def write_private_key
|
123
|
+
File.open("#{ENV['HOME']}/.ssh/shipment_rsa", "w+") do |f|
|
124
|
+
f.write ssh_key.private_key
|
125
|
+
end
|
126
|
+
|
127
|
+
`chmod 0600 #{ENV['HOME']}/.ssh/shipment_rsa`
|
128
|
+
return ssh_key.private_key
|
129
|
+
end
|
130
|
+
|
131
|
+
def write_public_key
|
132
|
+
File.open("#{ENV['HOME']}/.ssh/shipment_rsa.pub", "w+") do |f|
|
133
|
+
f.write ssh_key.ssh_public_key
|
134
|
+
end
|
135
|
+
|
136
|
+
return ssh_key.ssh_public_key
|
137
|
+
end
|
138
|
+
|
139
|
+
def transmit_do_key
|
140
|
+
Digitalocean.client_id = do_client_id
|
141
|
+
Digitalocean.api_key = do_api_key
|
142
|
+
|
143
|
+
response = Digitalocean::SshKey.create({
|
144
|
+
name: 'shipment',
|
145
|
+
ssh_pub_key: CGI::escape(public_key)
|
146
|
+
})
|
147
|
+
|
148
|
+
if response.status == "ERROR"
|
149
|
+
return false
|
150
|
+
else
|
151
|
+
return true
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'git'
|
2
|
+
require 'securerandom'
|
3
|
+
require 'shipment/server/ssh_client'
|
4
|
+
|
5
|
+
module Shipment
|
6
|
+
module Project
|
7
|
+
|
8
|
+
class Customizer
|
9
|
+
attr_accessor :repo_info
|
10
|
+
|
11
|
+
def self.customize
|
12
|
+
puts "-----> ".green + "Setting up deployment settings..."
|
13
|
+
new.customize
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
@repo_info = YAML.load(File.read('.shipment'))
|
18
|
+
end
|
19
|
+
|
20
|
+
def customize
|
21
|
+
puts "-----> ".green + "Checking for Redis and Sidqkiq..."
|
22
|
+
if has_sidekiq?
|
23
|
+
add_sidekiq
|
24
|
+
elsif has_redis?
|
25
|
+
add_redis
|
26
|
+
end
|
27
|
+
|
28
|
+
puts "-----> ".green + "Updating database info..."
|
29
|
+
update_db_info
|
30
|
+
puts "-----> ".green + "Adding .shipment file to .gitignore..."
|
31
|
+
add_shipment_to_gitignore
|
32
|
+
puts "-----> ".green + "Committing and pushing changes..."
|
33
|
+
commit_and_push
|
34
|
+
end
|
35
|
+
|
36
|
+
def commit_and_push
|
37
|
+
git = Git.open(FileUtils.pwd)
|
38
|
+
git.add(all: true)
|
39
|
+
git.commit("Setup for deployment with shipment")
|
40
|
+
git.push(git.remote("origin"))
|
41
|
+
end
|
42
|
+
|
43
|
+
def add_shipment_to_gitignore
|
44
|
+
File.open(".gitignore", "a") do |f|
|
45
|
+
f.write '.shipment'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def has_sidekiq?
|
50
|
+
!!File.read('Gemfile').match(/sidekiq/)
|
51
|
+
end
|
52
|
+
|
53
|
+
def add_sidekiq
|
54
|
+
repo_info[:sidekiq] = true
|
55
|
+
add_redis
|
56
|
+
end
|
57
|
+
|
58
|
+
def has_redis?
|
59
|
+
!!File.read('Gemfile').match(/redis/)
|
60
|
+
end
|
61
|
+
|
62
|
+
def add_redis
|
63
|
+
repo_info[:redis] = true
|
64
|
+
end
|
65
|
+
|
66
|
+
def update_shipment_file
|
67
|
+
File.open('.shipment', 'w+') do |f|
|
68
|
+
f.write repo_info.to_yaml
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def update_db_info
|
73
|
+
create_db_creds
|
74
|
+
update_shipment_file
|
75
|
+
update_db_yaml
|
76
|
+
end
|
77
|
+
|
78
|
+
def update_db_yaml
|
79
|
+
FileUtils.cp('config/database.yml', 'config/database.yml.bak')
|
80
|
+
database_yml = YAML.load(File.read('config/database.yml'))
|
81
|
+
database_yml["production"] = {
|
82
|
+
"url" => "postgres://#{repo_info[:database][:username]}:#{repo_info[:database][:password]}@#{repo_info[:ip_address]}:5432/#{repo_info[:database][:name]}"
|
83
|
+
}
|
84
|
+
File.open("config/database.yml", "w+") do |f|
|
85
|
+
f.write database_yml.to_yaml
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def create_db_creds
|
90
|
+
repo_info[:database] = {
|
91
|
+
username: SecureRandom.hex(5).chars.unshift('s').join,
|
92
|
+
password: SecureRandom.hex.chars.unshift('s').join,
|
93
|
+
name: SecureRandom.hex(10).chars.unshift('s').join
|
94
|
+
}
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'git'
|
2
|
+
require 'netrc'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module Shipment
|
6
|
+
module Project
|
7
|
+
|
8
|
+
class Repo
|
9
|
+
attr_accessor :repo, :url, :user, :name, :original_name
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
self.repo = Git.open(FileUtils.pwd)
|
13
|
+
parse_details
|
14
|
+
end
|
15
|
+
|
16
|
+
def parse_details
|
17
|
+
get_url
|
18
|
+
get_user
|
19
|
+
get_name
|
20
|
+
end
|
21
|
+
|
22
|
+
def get_url
|
23
|
+
self.url = repo.remotes.detect do |remote|
|
24
|
+
remote.name == "origin"
|
25
|
+
end.url
|
26
|
+
end
|
27
|
+
|
28
|
+
def get_user
|
29
|
+
self.user = url.match(/(?:https:\/\/|git@)github\.com(?:\:|\/)(.*)\/.+(?:\.git)?/)[1]
|
30
|
+
end
|
31
|
+
|
32
|
+
def get_name
|
33
|
+
self.original_name = url.match(/(?:https:\/\/|git@).*\/(.+)(?:\.git)?/)[1].gsub('.git', '')
|
34
|
+
self.name = url.match(/(?:https:\/\/|git@).*\/(.+)(?:\.git)?/)[1].gsub('.git', '').gsub('_','-')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'digitalocean'
|
2
|
+
require 'netrc'
|
3
|
+
require 'colorize'
|
4
|
+
require 'shipment/server/ssh_client'
|
5
|
+
require 'shipment/project/customizer'
|
6
|
+
require 'yaml'
|
7
|
+
|
8
|
+
module Shipment
|
9
|
+
module Server
|
10
|
+
|
11
|
+
class Initializer
|
12
|
+
attr_reader :repo_url, :repo_name, :repo_user, :repo
|
13
|
+
attr_accessor :droplet, :ip_address
|
14
|
+
|
15
|
+
def self.spin_up(repo)
|
16
|
+
new(repo).spin_up
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(repo)
|
20
|
+
@repo = repo
|
21
|
+
@repo_url, @repo_name, @repo_user = repo.url, repo.name, repo.user
|
22
|
+
netrc = Netrc.read
|
23
|
+
Digitalocean.client_id, Digitalocean.api_key = netrc["shipment.do"]
|
24
|
+
end
|
25
|
+
|
26
|
+
def spin_up
|
27
|
+
create_droplet
|
28
|
+
store_droplet_data
|
29
|
+
update_ssh_config
|
30
|
+
Shipment::Project::Customizer.customize
|
31
|
+
Shipment::Server::SSHClient.setup(
|
32
|
+
repo: repo,
|
33
|
+
ip_address: ip_address
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
def create_droplet
|
38
|
+
print "-----> ".green + "Creating Droplet: #{repo_name}"
|
39
|
+
self.droplet = Digitalocean::Droplet.create({
|
40
|
+
name: repo_name,
|
41
|
+
size_id: get_size_id,
|
42
|
+
image_id: get_image_id,
|
43
|
+
region_id: get_region_id,
|
44
|
+
ssh_key_ids: [get_ssh_key_id]
|
45
|
+
}).droplet
|
46
|
+
print_waiting
|
47
|
+
end
|
48
|
+
|
49
|
+
def store_droplet_data
|
50
|
+
puts "-----> ".green + "Storing droplet data..."
|
51
|
+
File.open("#{ENV['HOME']}/.shipment", "a") do |f|
|
52
|
+
f.write "#{droplet.id} : #{repo_name} : #{ip_address}\n"
|
53
|
+
end
|
54
|
+
|
55
|
+
puts "-----> ".green + "Creating .shipment file..."
|
56
|
+
yaml = {
|
57
|
+
id: droplet.id,
|
58
|
+
ip_address: ip_address,
|
59
|
+
name: repo_name,
|
60
|
+
user: repo_user,
|
61
|
+
url: repo_url,
|
62
|
+
secret: `rake secret`.strip
|
63
|
+
}.to_yaml
|
64
|
+
File.open(File.join(FileUtils.pwd, ".shipment"), "w+") do |f|
|
65
|
+
f.write yaml
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def update_ssh_config
|
70
|
+
puts "-----> ".green + "Setting up SSH config..."
|
71
|
+
File.open("#{ENV['HOME']}/.ssh/config", "a") do |f|
|
72
|
+
f.write <<-SSHCONFIG.gsub(/^ {10}/,'')
|
73
|
+
|
74
|
+
Host #{ip_address}
|
75
|
+
Hostname #{ip_address}
|
76
|
+
IdentityFile #{ENV['HOME']}/.ssh/shipment_rsa
|
77
|
+
User root
|
78
|
+
SSHCONFIG
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def get_size_id
|
83
|
+
Digitalocean::Size.all.sizes.detect {|size| size.slug == "2gb"}.id
|
84
|
+
end
|
85
|
+
|
86
|
+
def get_image_id
|
87
|
+
Digitalocean::Image.all.images.detect {|image| image.name.match(/Docker/)}.id
|
88
|
+
end
|
89
|
+
|
90
|
+
def get_region_id
|
91
|
+
Digitalocean::Region.all.regions.detect {|region| region.slug == "nyc2"}.id
|
92
|
+
end
|
93
|
+
|
94
|
+
def get_ssh_key_id
|
95
|
+
Digitalocean::SshKey.all.ssh_keys.detect {|key| key.name == "shipment"}.id
|
96
|
+
end
|
97
|
+
|
98
|
+
def droplet_created?
|
99
|
+
!!(remote_droplet.status == "active")
|
100
|
+
end
|
101
|
+
|
102
|
+
def get_ip_address
|
103
|
+
remote_droplet.ip_address
|
104
|
+
end
|
105
|
+
|
106
|
+
def remote_droplet
|
107
|
+
Digitalocean::Droplet.find(droplet.id).droplet
|
108
|
+
end
|
109
|
+
|
110
|
+
def print_waiting
|
111
|
+
while !droplet_created?
|
112
|
+
print "."
|
113
|
+
sleep 3
|
114
|
+
end
|
115
|
+
|
116
|
+
self.ip_address = get_ip_address
|
117
|
+
puts "Done."
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,218 @@
|
|
1
|
+
require 'net/ssh'
|
2
|
+
require 'netrc'
|
3
|
+
require 'highline/import'
|
4
|
+
require 'colorize'
|
5
|
+
|
6
|
+
module Shipment
|
7
|
+
module Server
|
8
|
+
|
9
|
+
class SSHClient
|
10
|
+
attr_reader :ip_address, :repo, :repo_url, :repo_name, :repo_user,
|
11
|
+
:repo_original_name, :gh_username, :gh_token, :db_user,
|
12
|
+
:db_password, :db_name, :secret_key_base
|
13
|
+
|
14
|
+
def self.setup(repo:, ip_address:)
|
15
|
+
new(repo, ip_address).setup
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.deploy(repo:, ip_address:)
|
19
|
+
new(repo, ip_address).deploy
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(repo, ip_address)
|
23
|
+
@gh_username, @gh_token = Netrc.read["shipment.gh"]
|
24
|
+
@ip_address = ip_address
|
25
|
+
@repo_url, @repo_name, @repo_user, @repo_original_name = repo.url, repo.name, repo.user, repo.original_name
|
26
|
+
db_info = YAML.load(File.read('.shipment'))[:database]
|
27
|
+
@secret_key_base = YAML.load(File.read('.shipment'))[:secret]
|
28
|
+
@db_user, @db_password, @db_name = db_info[:username], db_info[:password], db_info[:name]
|
29
|
+
end
|
30
|
+
|
31
|
+
def setup
|
32
|
+
add_to_known_hosts
|
33
|
+
puts "-----> ".green + "Preparing server..."
|
34
|
+
setup_database_container
|
35
|
+
setup_application_container
|
36
|
+
end
|
37
|
+
|
38
|
+
def deploy
|
39
|
+
puts "-----> ".green + "Deploying..."
|
40
|
+
kill_and_commit_old_server
|
41
|
+
start_new_server
|
42
|
+
puts "-----> ".green + "Done.\nYour application is accessable at: #{ip_address}"
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
def kill_and_commit_old_server
|
47
|
+
puts "-----> ".green + "Stopping existing server..."
|
48
|
+
run_remote_command("if sudo docker ps -a | grep application > /dev/null; then docker kill application && docker commit application #{repo_user}/#{repo_name} && docker rm application; fi")
|
49
|
+
end
|
50
|
+
|
51
|
+
def start_new_server
|
52
|
+
puts "-----> ".green + "Restarting server..."
|
53
|
+
redis_command, sidekiq_command = parse_redis_and_sidekiq
|
54
|
+
run_remote_command("docker run -d -p 80:3000 --name application -e SECRET_KEY_BASE=#{secret_key_base} #{repo_user}/#{repo_name} /bin/bash -c 'kill -9 $(pgrep -f sidekiq) > /dev/null 2>&1 && kill -9 $(pgrep -f redis-server) > /dev/null 2>&1 && kill -9 $(pgrep -f rails) > /dev/null 2>&1 && source /etc/profile.d/rvm.sh && rm -rf #{repo_name} && git clone #{repo_url} #{repo_name} && cd #{repo_name} && bundle install && RAILS_ENV=production bundle exec rake db:migrate#{redis_command}#{sidekiq_command} && rails server -p 3000 -e production'")
|
55
|
+
end
|
56
|
+
|
57
|
+
def parse_redis_and_sidekiq
|
58
|
+
# Code smell...
|
59
|
+
commands = []
|
60
|
+
if YAML.load(File.read('.shipment'))[:sidekiq]
|
61
|
+
commands << ' && redis-server /etc/redis/redis.conf'
|
62
|
+
commands << ' && bundle exec sidekiq -d -L log/sidekiq.log'
|
63
|
+
elsif YAML.load(File.read('.shipment'))[:redis]
|
64
|
+
commands << ' && redis-server /etc/redis/redis.conf'
|
65
|
+
end
|
66
|
+
|
67
|
+
return commands
|
68
|
+
end
|
69
|
+
|
70
|
+
def add_to_known_hosts
|
71
|
+
puts "-----> ".green + "Giving server time to prepare for SSH connections (this will take about 1 minute)..."
|
72
|
+
sleep 60
|
73
|
+
puts "-----> ".green + "Adding droplet to known hosts..."
|
74
|
+
FileUtils.touch('known_host.sh')
|
75
|
+
FileUtils.chmod('+x', 'known_host.sh')
|
76
|
+
File.open('known_host.sh', 'w+') do |f|
|
77
|
+
f.write <<-SCRIPT.gsub(/^ {10}/, '')
|
78
|
+
#!/bin/bash
|
79
|
+
|
80
|
+
/usr/bin/expect <<EOD
|
81
|
+
spawn ssh root@#{ip_address}
|
82
|
+
expect -re "(continue)"
|
83
|
+
send "yes\\n"
|
84
|
+
send "exit\\n"
|
85
|
+
expect eof
|
86
|
+
EOD
|
87
|
+
SCRIPT
|
88
|
+
end
|
89
|
+
`./known_host.sh && rm known_host.sh`
|
90
|
+
end
|
91
|
+
|
92
|
+
def setup_database_container
|
93
|
+
puts "-----> ".green + "Setting up database container..."
|
94
|
+
pull_postgres_image
|
95
|
+
setup_database_user
|
96
|
+
start_database_container
|
97
|
+
end
|
98
|
+
|
99
|
+
def pull_postgres_image
|
100
|
+
puts "-----> ".green + "Pulling Postgres image (this may take a few minutes)..."
|
101
|
+
run_remote_command("docker pull loganhasson/postgres_image")
|
102
|
+
end
|
103
|
+
|
104
|
+
def setup_database_user
|
105
|
+
puts "-----> ".green + "Setting up database user..."
|
106
|
+
run_remote_command("docker run --name database -e USER=#{db_user} -e PASSWORD=#{db_password} -e NAME=#{db_name} loganhasson/postgres_image /bin/bash -c 'wget https://gist.githubusercontent.com/loganhasson/d8f8a91875087407ea6a/raw/21bdbd6134e470397cef1ab0feeefdecee3fb6f0/database_setup_shipment.sql && service postgresql start && psql --set \"user=$USER\" --set \"password=$PASSWORD\" --file=database_setup_shipment.sql && createdb -O $USER $NAME && service postgresql stop && rm database_setup_shipment.sql && wget https://gist.githubusercontent.com/loganhasson/680f4b02c0f295dd7961/raw/18af1b0ccb2bb485dfc51e2fde169fce91393bca/update_postgres_config_shipment.sh && chmod +x update_postgres_config_shipment.sh && ./update_postgres_config_shipment.sh && rm update_postgres_config_shipment.sh' && docker commit database #{repo_user}/#{repo_name}_db && docker rm database && docker rmi loganhasson/postgres_image")
|
107
|
+
end
|
108
|
+
|
109
|
+
def start_database_container
|
110
|
+
puts "-----> ".green + "Starting postgres server..."
|
111
|
+
run_remote_command("docker run --name database -d -p 5432:5432 #{repo_user}/#{repo_name}_db su postgres -c '/usr/lib/postgresql/9.3/bin/postgres -D /var/lib/postgresql/9.3/main -c config_file=/etc/postgresql/9.3/main/postgresql.conf'")
|
112
|
+
end
|
113
|
+
|
114
|
+
def setup_application_container
|
115
|
+
puts "-----> ".green + "Setting up application container..."
|
116
|
+
pull_ruby_image
|
117
|
+
generate_ssh_key
|
118
|
+
add_deploy_key
|
119
|
+
clone_and_bundle
|
120
|
+
migrate
|
121
|
+
#save_docker_image
|
122
|
+
|
123
|
+
puts "-----> ".green + "Done.\nReady to deploy."
|
124
|
+
puts "Your server's IP Address is: #{ip_address}"
|
125
|
+
end
|
126
|
+
|
127
|
+
def pull_ruby_image
|
128
|
+
puts "-----> ".green + "Pulling Ruby image (this may take a few minutes)..."
|
129
|
+
run_remote_command("docker pull loganhasson/ruby_image")
|
130
|
+
end
|
131
|
+
|
132
|
+
def generate_ssh_key
|
133
|
+
puts "-----> ".green + "Generating deploy key..."
|
134
|
+
run_remote_command("docker run --name setup loganhasson/ruby_image /bin/bash -c 'ssh-keygen -t rsa -N \"\" -C \"#{repo_name}@shipment\" -f \"/root/.ssh/id_rsa\"' && docker commit setup #{repo_user}/#{repo_name} && docker rm setup && docker rmi loganhasson/ruby_image")
|
135
|
+
end
|
136
|
+
|
137
|
+
def add_deploy_key
|
138
|
+
puts "-----> ".green + "Adding deploy key to GitHub..."
|
139
|
+
run_remote_command("docker run --name deploy -e ACCESS_TOKEN=#{gh_token} -e REPO=#{repo_user}/#{repo_original_name} #{repo_user}/#{repo_name} /bin/bash -c 'source /etc/profile.d/rvm.sh && wget https://gist.githubusercontent.com/loganhasson/e4791b4abe2dc75bc82f/raw/342cd7c32cec4e58947c0eb1785f45abecf714c4/add_deploy_key_shipment.rb && ruby add_deploy_key_shipment.rb && rm add_deploy_key_shipment.rb' && docker commit deploy #{repo_user}/#{repo_name} && docker rm deploy")
|
140
|
+
end
|
141
|
+
|
142
|
+
def clone_and_bundle
|
143
|
+
puts "-----> ".green + "Cloning project into application container..."
|
144
|
+
run_remote_command("docker run --name bundle -e REPO_URL=#{repo_url} -e REPO_NAME=#{repo_name} #{repo_user}/#{repo_name} /bin/bash -c 'source /etc/profile.d/rvm.sh && wget https://gist.githubusercontent.com/loganhasson/5db07d7b5671a9bc5fef/raw/4a28de693b2631c69a888fada82d6b462e2fe09e/clone_with_expect_shipment.sh && chmod +x clone_with_expect_shipment.sh && ./clone_with_expect_shipment.sh && rm clone_with_expect_shipment.sh && cd #{repo_name} && bundle install && cd ..' && docker commit bundle #{repo_user}/#{repo_name} && docker rm bundle")
|
145
|
+
end
|
146
|
+
|
147
|
+
def migrate
|
148
|
+
puts "-----> ".green + "Migrating database..."
|
149
|
+
run_remote_command("docker run --name migrate #{repo_user}/#{repo_name} /bin/bash -c 'source /etc/profile.d/rvm.sh && cd #{repo_name} && RAILS_ENV=production bundle exec rake db:migrate' && docker rm migrate")
|
150
|
+
end
|
151
|
+
|
152
|
+
#def save_docker_image
|
153
|
+
#puts "-----> ".green + "Installing required packages (this may take a few minutes)..."
|
154
|
+
#run_remote_command("apt-get -y update && apt-get -y upgrade && apt-get -y udpate && apt-get -y install expect", true)
|
155
|
+
|
156
|
+
#puts "-----> ".green + "Saving docker images and pushing to Docker hub..."
|
157
|
+
#docker_username = ask("Docker username: ")
|
158
|
+
#docker_password = ask("Docker password: ") { |q| q.echo = false }
|
159
|
+
#docker_email = ask("Docker email address: ")
|
160
|
+
|
161
|
+
#run_remote_command(<<-SETUP
|
162
|
+
#/usr/bin/expect <<EOD
|
163
|
+
#spawn docker login
|
164
|
+
#expect "Username:"
|
165
|
+
#send "#{docker_username}\n"
|
166
|
+
#expect "Password:"
|
167
|
+
#send "#{docker_password}\n"
|
168
|
+
#expect "Email:"
|
169
|
+
#send "#{docker_email}\n"
|
170
|
+
#expect eof
|
171
|
+
#EOD
|
172
|
+
#SETUP
|
173
|
+
#)
|
174
|
+
#run_remote_command("docker push #{repo_user}/#{repo_name} && docker push #{repo_user}/#{repo_name}_db")
|
175
|
+
#end
|
176
|
+
|
177
|
+
def run_remote_command(command, silent=false)
|
178
|
+
puts "-----> ".blue + "#{command}"
|
179
|
+
|
180
|
+
begin
|
181
|
+
Net::SSH.start(ip_address, 'root', timeout: 500) do |ssh|
|
182
|
+
ssh.open_channel do |channel|
|
183
|
+
channel.exec "#{command}" do |ch, success|
|
184
|
+
raise "problem executing command: #{command}".red unless success
|
185
|
+
|
186
|
+
ch.on_data do |c, data|
|
187
|
+
if !silent
|
188
|
+
if !data.empty? && !(data == " ") && !(data == "\n") && !data.match(/ojbects|deltas/) && !(data == ".")
|
189
|
+
$stdout.puts " #{data.strip.chomp}"
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
ch.on_extended_data do |c, type, data|
|
195
|
+
if !data.empty? && !(data == " ") && !(data == "\n") && !(data == ".")
|
196
|
+
$stderr.puts " #{data.strip.chomp}".red
|
197
|
+
|
198
|
+
if data.strip.chomp.match(/Error pulling image/) || data.strip.chomp.match(/connection timed out/)
|
199
|
+
$stderr.puts "-----> ".red + "CONNECTION TO DOCKER TIMED OUT: ".red + "Trying again..."
|
200
|
+
run_remote_command(command, silent)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
#ch.on_close { puts "-----> ".green + "Done." }
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
rescue Errno::ETIMEDOUT
|
210
|
+
$stderr.puts "-----> ".red + "CONNECTION TO SERVER TIMED OUT: ".red + "Reconnecting..."
|
211
|
+
run_remote_command(command, silent)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'shipment/server/ssh_client'
|
3
|
+
require 'shipment/project/repo'
|
4
|
+
|
5
|
+
module Shipment
|
6
|
+
class Slip
|
7
|
+
def self.cast_off
|
8
|
+
new.cast_off
|
9
|
+
end
|
10
|
+
|
11
|
+
def cast_off
|
12
|
+
Shipment::Server::SSHClient.deploy(
|
13
|
+
repo: Shipment::Project::Repo.new,
|
14
|
+
ip_address: YAML.load(File.read('.shipment'))[:ip_address]
|
15
|
+
)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/shipment.gemspec
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'shipment/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "shipment-deploy"
|
8
|
+
spec.version = Shipment::VERSION
|
9
|
+
spec.authors = ["Logan Hasson"]
|
10
|
+
spec.email = ["logan.hasson@gmail.com"]
|
11
|
+
spec.summary = "Easy deployment using Docker"
|
12
|
+
spec.description = "Automatically deploy Rails apps to DigitalOcean in a \
|
13
|
+
Docker container."
|
14
|
+
spec.homepage = "http://github.com/loganhasson/shipment"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0")
|
18
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
|
+
spec.require_paths = ["lib", "bin"]
|
21
|
+
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
23
|
+
spec.add_development_dependency "rake"
|
24
|
+
spec.add_development_dependency "spec"
|
25
|
+
|
26
|
+
spec.add_runtime_dependency "net-ssh"
|
27
|
+
spec.add_runtime_dependency "digitalocean"
|
28
|
+
spec.add_runtime_dependency "octokit"
|
29
|
+
spec.add_runtime_dependency "netrc"
|
30
|
+
spec.add_runtime_dependency "thor"
|
31
|
+
spec.add_runtime_dependency "highline"
|
32
|
+
spec.add_runtime_dependency "sshkey"
|
33
|
+
spec.add_runtime_dependency "git"
|
34
|
+
spec.add_runtime_dependency "colorize"
|
35
|
+
end
|
36
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require_relative '../config/environment'
|
2
|
+
|
3
|
+
RSpec.configure do |config|
|
4
|
+
config.filter_run :focus
|
5
|
+
config.run_all_when_everything_filtered = true
|
6
|
+
|
7
|
+
config.order = :random
|
8
|
+
|
9
|
+
config.expect_with :rspec do |expectations|
|
10
|
+
expectations.syntax = :expect
|
11
|
+
end
|
12
|
+
|
13
|
+
config.mock_with :rspec do |mocks|
|
14
|
+
mocks.syntax = :expect
|
15
|
+
mocks.verify_partial_doubles = true
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
metadata
ADDED
@@ -0,0 +1,236 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: shipment-deploy
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Logan Hasson
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-08-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.6'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: spec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: net-ssh
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: digitalocean
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: octokit
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: netrc
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: thor
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: highline
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: sshkey
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :runtime
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: git
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :runtime
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: colorize
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :runtime
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
|
+
description: Automatically deploy Rails apps to DigitalOcean in a Docker
|
182
|
+
container.
|
183
|
+
email:
|
184
|
+
- logan.hasson@gmail.com
|
185
|
+
executables:
|
186
|
+
- ship
|
187
|
+
extensions: []
|
188
|
+
extra_rdoc_files: []
|
189
|
+
files:
|
190
|
+
- ".gitignore"
|
191
|
+
- ".rspec"
|
192
|
+
- Gemfile
|
193
|
+
- LICENSE.txt
|
194
|
+
- README.md
|
195
|
+
- Rakefile
|
196
|
+
- bin/ship
|
197
|
+
- lib/shipment.rb
|
198
|
+
- lib/shipment/cli.rb
|
199
|
+
- lib/shipment/credentials_checker.rb
|
200
|
+
- lib/shipment/mooring.rb
|
201
|
+
- lib/shipment/project/customizer.rb
|
202
|
+
- lib/shipment/project/repo.rb
|
203
|
+
- lib/shipment/rigging.rb
|
204
|
+
- lib/shipment/server/initializer.rb
|
205
|
+
- lib/shipment/server/ssh_client.rb
|
206
|
+
- lib/shipment/slip.rb
|
207
|
+
- lib/shipment/version.rb
|
208
|
+
- shipment.gemspec
|
209
|
+
- spec/spec_helper.rb
|
210
|
+
homepage: http://github.com/loganhasson/shipment
|
211
|
+
licenses:
|
212
|
+
- MIT
|
213
|
+
metadata: {}
|
214
|
+
post_install_message:
|
215
|
+
rdoc_options: []
|
216
|
+
require_paths:
|
217
|
+
- lib
|
218
|
+
- bin
|
219
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
220
|
+
requirements:
|
221
|
+
- - ">="
|
222
|
+
- !ruby/object:Gem::Version
|
223
|
+
version: '0'
|
224
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
225
|
+
requirements:
|
226
|
+
- - ">="
|
227
|
+
- !ruby/object:Gem::Version
|
228
|
+
version: '0'
|
229
|
+
requirements: []
|
230
|
+
rubyforge_project:
|
231
|
+
rubygems_version: 2.2.2
|
232
|
+
signing_key:
|
233
|
+
specification_version: 4
|
234
|
+
summary: Easy deployment using Docker
|
235
|
+
test_files:
|
236
|
+
- spec/spec_helper.rb
|