deploku 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.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +83 -0
- data/Rakefile +2 -0
- data/bin/deploku +8 -0
- data/deploku.gemspec +22 -0
- data/lib/deploku/control.rb +39 -0
- data/lib/deploku/migration.rb +44 -0
- data/lib/deploku/remote.rb +180 -0
- data/lib/deploku/runnable.rb +26 -0
- data/lib/deploku/version.rb +3 -0
- data/lib/deploku.rb +6 -0
- metadata +86 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1b90f4f46ecfa0ec3e8e33e50d54e27d96c221e4
|
4
|
+
data.tar.gz: e261d9d031ef208958ba83ea8d00c946ce442779
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b2d16b62ebf298926353dfd2adbabd7158202fd61b201897d25cfc7a6d222a6d3713f130096822a08feaeec4353fc715f19497c2e0f679a9b3417d4f489d8b84
|
7
|
+
data.tar.gz: e5c71b23e37dd4130303ca9c061cea9c82fde8e49db5dd4bf18d91dfb2fcf12c35f75829dd4a8d4e6142649b2b83c0f4e68885c12df73af27a334883934f46d1
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Bill Horsman
|
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,83 @@
|
|
1
|
+
# Deploku
|
2
|
+
|
3
|
+
A quick and easy way to deploy the current branch to a Heroku. It copes with multiple remotes and runs migrations as necessary. It assumes you are running a Unix like system that will execute commands like `git rev-list abcdef7.. | wc -l`, for example.
|
4
|
+
|
5
|
+
You should also look at [jbrunton/heroploy](https://github.com/jbrunton/heroploy) which is very similar to this gem. Heroploy needs some configuration (Deploku needs none) but it does do a lot more (e.g. Travis). It's also stricter about which branch can be deployed where. I encourage you to check out Heroploy as well to see if it better suits your needs.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'deploku'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install deploku
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
Just run the command and it will look for any git remotes referencing Heroku:
|
26
|
+
|
27
|
+
```
|
28
|
+
[master]$ deploku
|
29
|
+
2 Heroku remotes found:
|
30
|
+
production
|
31
|
+
staging
|
32
|
+
```
|
33
|
+
|
34
|
+
Then choose a remote and run it again:
|
35
|
+
|
36
|
+
```
|
37
|
+
[master]$ deploku production
|
38
|
+
Heroku app my-app-production is running commit abcdef7
|
39
|
+
It is 3 commits behind your local master branch
|
40
|
+
There are no pending migrations
|
41
|
+
```
|
42
|
+
|
43
|
+
If you want to deploy then add the `deploy` command:
|
44
|
+
|
45
|
+
```
|
46
|
+
[master]$ deploku production deploy
|
47
|
+
Heroku app my-app-production is running commit abcdef7
|
48
|
+
It is 3 commits behind your local master branch
|
49
|
+
There are no pending migrations
|
50
|
+
The following command will be run:
|
51
|
+
|
52
|
+
1. git push production master:master
|
53
|
+
|
54
|
+
Proceed? (y/N):
|
55
|
+
```
|
56
|
+
|
57
|
+
Enter `y` to proceed or anything else to abort. Nothing will happen unless you enter `y`.
|
58
|
+
|
59
|
+
If there are migrations pending then you will be forced to either pass in either the `maintenance` or `maintenance:skip` options.
|
60
|
+
|
61
|
+
```
|
62
|
+
[master]$ deploku production deploy maintenance
|
63
|
+
Heroku app my-app-production is running commit abcdef7
|
64
|
+
It is 3 commits behind your local master branch
|
65
|
+
There is 1 pending migration
|
66
|
+
The following commands will be run:
|
67
|
+
|
68
|
+
1. heroku maintenance:on --app my-app-production
|
69
|
+
2. git push production master:master
|
70
|
+
3. heroku run rake db:migrate --app my-app-production
|
71
|
+
4. heroku restart --app my-app-production
|
72
|
+
5. heroku maintenance:off --app my-app-production
|
73
|
+
|
74
|
+
Proceed? (y/N):
|
75
|
+
```
|
76
|
+
|
77
|
+
## Contributing
|
78
|
+
|
79
|
+
1. Fork it ( https://github.com/billhorsman/deploku/fork )
|
80
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
81
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
82
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
83
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/bin/deploku
ADDED
data/deploku.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'deploku/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "deploku"
|
8
|
+
spec.version = Deploku::VERSION
|
9
|
+
spec.authors = ["Bill Horsman"]
|
10
|
+
spec.email = ["bill@logicalcobwebs.com"]
|
11
|
+
spec.summary = %q{Convenient way of deploying to Heroku}
|
12
|
+
spec.homepage = ""
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0")
|
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_development_dependency "bundler", "~> 1.7"
|
21
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
22
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Deploku
|
2
|
+
|
3
|
+
module Control
|
4
|
+
extend Deploku::Runnable
|
5
|
+
|
6
|
+
def self.run(args)
|
7
|
+
matching_remotes = remotes & args
|
8
|
+
case matching_remotes.size
|
9
|
+
when 0
|
10
|
+
puts "#{remotes.size} Heroku remote#{'s' if remotes.size > 1} found:"
|
11
|
+
puts *remotes
|
12
|
+
exit 0
|
13
|
+
when 1
|
14
|
+
remote = matching_remotes[0]
|
15
|
+
args.delete remote
|
16
|
+
commands = %w[status deploy] & args
|
17
|
+
commands << "status" if commands.size == 0
|
18
|
+
if commands.size > 1
|
19
|
+
puts "Choose just one command"
|
20
|
+
exit 1
|
21
|
+
else
|
22
|
+
args.delete commands[0]
|
23
|
+
Deploku::Remote.new(remote).send(commands[0], args)
|
24
|
+
end
|
25
|
+
else
|
26
|
+
puts "Please choose just one remote out of #{remotes.join(" or ")}"
|
27
|
+
exit 1
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.remotes
|
32
|
+
@remotes ||= run_command("git remote -v | grep heroku | grep push").split("\n").map {|line|
|
33
|
+
line.match(/^(.*)\t/)[1]
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Deploku
|
2
|
+
class Migration < OpenStruct
|
3
|
+
|
4
|
+
attr_accessor :local_status, :remote_status
|
5
|
+
|
6
|
+
def pending?
|
7
|
+
local_status != "up" || remote_status != "up"
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
message = case local_status
|
12
|
+
when "down"
|
13
|
+
case remote_status
|
14
|
+
when "down"
|
15
|
+
"Pending locally and remotely. Why?"
|
16
|
+
when "up"
|
17
|
+
"Pending locally BUT up remotely. Why?"
|
18
|
+
else
|
19
|
+
"Pending locally AND needs to be deployed. Migrate locally first?"
|
20
|
+
end
|
21
|
+
when "up"
|
22
|
+
case remote_status
|
23
|
+
when "down"
|
24
|
+
"Pending remotely"
|
25
|
+
when "up"
|
26
|
+
"Up both locally and remotely"
|
27
|
+
else
|
28
|
+
"Needs to be deployed"
|
29
|
+
end
|
30
|
+
else
|
31
|
+
case remote_status
|
32
|
+
when "down"
|
33
|
+
"Pending remotely but missing locally!"
|
34
|
+
when "up"
|
35
|
+
"Up remotely but pending locally!"
|
36
|
+
else
|
37
|
+
"Missing locally and remotely!!"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
" #{version} - #{message}"
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,180 @@
|
|
1
|
+
module Deploku
|
2
|
+
|
3
|
+
class Remote
|
4
|
+
include Runnable
|
5
|
+
|
6
|
+
attr_reader :remote, :maintenance, :force
|
7
|
+
|
8
|
+
def initialize(remote)
|
9
|
+
@remote = remote
|
10
|
+
end
|
11
|
+
|
12
|
+
def app_name
|
13
|
+
@app_name ||= run_command("git remote -v | grep #{remote} | grep push").match(/heroku\.com:(.*)\.git/)[1]
|
14
|
+
end
|
15
|
+
|
16
|
+
def remote_commit_exists_locally?
|
17
|
+
test_command("git show #{remote_commit}")
|
18
|
+
end
|
19
|
+
|
20
|
+
def database_configured?
|
21
|
+
test_command("rake db:migrate:status")
|
22
|
+
end
|
23
|
+
|
24
|
+
def behind
|
25
|
+
@behind ||= count_rev_list("#{remote_commit}..")
|
26
|
+
end
|
27
|
+
|
28
|
+
def ahead
|
29
|
+
@ahead ||= count_rev_list("..#{remote_commit}")
|
30
|
+
end
|
31
|
+
|
32
|
+
def count_rev_list(range)
|
33
|
+
run_command("git rev-list #{range}").slice("\n").size
|
34
|
+
end
|
35
|
+
|
36
|
+
def remote_commit
|
37
|
+
@remote_commit ||= run_command("git ls-remote #{remote} 2> /dev/null").chomp.split(' ').first
|
38
|
+
end
|
39
|
+
|
40
|
+
def status(args)
|
41
|
+
puts "Heroku app #{app_name} is running commit #{remote_commit.slice(0, 7)}"
|
42
|
+
if remote_commit_exists_locally?
|
43
|
+
if behind == 0 && ahead == 0
|
44
|
+
puts "It is up to date"
|
45
|
+
else
|
46
|
+
if ahead == 0
|
47
|
+
puts "It is #{behind} commit#{"s" if behind > 1} behind your local #{local_branch} branch"
|
48
|
+
elsif behind == 0
|
49
|
+
puts "It is #{ahead} commit#{"s" if ahead > 1} ahead of your local #{local_branch} branch"
|
50
|
+
else
|
51
|
+
puts "It is #{behind} commit#{"s" if behind > 1} behind and #{ahead} commit#{"s" if ahead > 1} ahead of your local #{local_branch} branch"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
else
|
55
|
+
puts "Warning! The commit #{remote_commit.slice(0, 7)} is not present in the local repo. Why?"
|
56
|
+
end
|
57
|
+
if database_configured?
|
58
|
+
case pending_migration_count
|
59
|
+
when 0
|
60
|
+
puts "There are no pending migrations"
|
61
|
+
when 1
|
62
|
+
puts "There is 1 pending migration"
|
63
|
+
else
|
64
|
+
puts "There are #{pending_migration_count} pending migrations"
|
65
|
+
end
|
66
|
+
if pending_migration_count > 0
|
67
|
+
pending_migrations.each do |migration|
|
68
|
+
puts migration
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def deploy(args)
|
75
|
+
status(args)
|
76
|
+
@force = !!args.delete("force")
|
77
|
+
if args.delete("maintenance")
|
78
|
+
@maintenance = :use
|
79
|
+
elsif args.delete("maintenance:skip")
|
80
|
+
@maintenance = :skip
|
81
|
+
end
|
82
|
+
if args.any?
|
83
|
+
puts "Unknown argument(s): #{args.join(", ")}"
|
84
|
+
exit 1
|
85
|
+
end
|
86
|
+
puts "The following command#{'s' if deploy_commands.size > 1} will be run:"
|
87
|
+
puts
|
88
|
+
deploy_commands.each_with_index do |command, index|
|
89
|
+
puts " #{index + 1}. #{command}"
|
90
|
+
end
|
91
|
+
print "\nProceed? (y/N): "
|
92
|
+
proceed = STDIN.gets.strip
|
93
|
+
if proceed == "y"
|
94
|
+
puts ""
|
95
|
+
deploy_commands.each_with_index do |command, index|
|
96
|
+
puts " #{index + 1}. #{command} ..."
|
97
|
+
run_command command
|
98
|
+
end
|
99
|
+
puts
|
100
|
+
else
|
101
|
+
puts "Abort"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def deploy_commands
|
106
|
+
return @deploy_commands if @deploy_commands
|
107
|
+
maintenance_mode = pending_migration_count > 0 && maintenance == :use
|
108
|
+
list = []
|
109
|
+
if pending_migration_count > 0
|
110
|
+
case maintenance
|
111
|
+
when :use
|
112
|
+
maintenance_mode = true
|
113
|
+
when :skip
|
114
|
+
maintenance_mode = false
|
115
|
+
else
|
116
|
+
puts "There are migrations to run. Please either choose maintenance or maintenance:skip"
|
117
|
+
exit 1
|
118
|
+
end
|
119
|
+
end
|
120
|
+
list << "heroku maintenance:on --app #{app_name}" if maintenance_mode
|
121
|
+
list << "git push#{force ? " --force" : ""} #{remote} #{local_branch}:master"
|
122
|
+
list << "heroku run rake db:migrate --app #{app_name}" if pending_migration_count > 0
|
123
|
+
list << "heroku restart --app #{app_name}" if pending_migration_count > 0
|
124
|
+
list << "heroku maintenance:off --app #{app_name}" if maintenance_mode
|
125
|
+
@deploy_commands = list
|
126
|
+
end
|
127
|
+
|
128
|
+
def local_branch
|
129
|
+
@local_branch ||= run_command("git symbolic-ref HEAD").chomp.sub(/^\/?refs\/heads\//, '')
|
130
|
+
end
|
131
|
+
|
132
|
+
def pending_migration_count
|
133
|
+
pending_migrations.size
|
134
|
+
end
|
135
|
+
|
136
|
+
def migrations
|
137
|
+
local_migrations && remote_migrations # Triggers building of @migrations
|
138
|
+
@migrations.sort_by(&:version)
|
139
|
+
end
|
140
|
+
|
141
|
+
def pending_migrations
|
142
|
+
migrations.select(&:pending?)
|
143
|
+
end
|
144
|
+
|
145
|
+
def add_migration(hash)
|
146
|
+
@migrations ||= []
|
147
|
+
migration = @migrations.detect {|m| m.version == hash[:version] } || Deploku::Migration.new(version: hash[:version])
|
148
|
+
if hash[:location] == :local
|
149
|
+
migration.local_status = hash[:status]
|
150
|
+
else
|
151
|
+
migration.remote_status = hash[:status]
|
152
|
+
end
|
153
|
+
@migrations << migration
|
154
|
+
end
|
155
|
+
|
156
|
+
def local_migrations
|
157
|
+
@local_migrations ||= extract_migrations(run_command("rake db:migrate:status"), :local)
|
158
|
+
end
|
159
|
+
|
160
|
+
def remote_migrations
|
161
|
+
@remote_migrations ||= extract_migrations(run_command("heroku run rake db:migrate:status --app #{app_name}"), :remote)
|
162
|
+
end
|
163
|
+
|
164
|
+
def extract_migrations(output, location)
|
165
|
+
output.split("\n").
|
166
|
+
select {|line|
|
167
|
+
line =~ /^\s*(up|down)/
|
168
|
+
}.map {|line|
|
169
|
+
values = line.split(" ")
|
170
|
+
add_migration(
|
171
|
+
location: location,
|
172
|
+
version: values[1],
|
173
|
+
status: values[0],
|
174
|
+
)
|
175
|
+
}
|
176
|
+
end
|
177
|
+
|
178
|
+
end
|
179
|
+
|
180
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Deploku
|
2
|
+
module Runnable
|
3
|
+
|
4
|
+
def run_command(command)
|
5
|
+
Bundler.with_clean_env {
|
6
|
+
out = `#{command}`
|
7
|
+
if $?.success?
|
8
|
+
out
|
9
|
+
else
|
10
|
+
puts "Error running command:"
|
11
|
+
puts command
|
12
|
+
puts out
|
13
|
+
exit $?.exitstatus
|
14
|
+
end
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_command(command)
|
19
|
+
Bundler.with_clean_env {
|
20
|
+
`#{command} 2> /dev/null`
|
21
|
+
$?.success?
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
data/lib/deploku.rb
ADDED
metadata
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: deploku
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Bill Horsman
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-11-06 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.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
description:
|
42
|
+
email:
|
43
|
+
- bill@logicalcobwebs.com
|
44
|
+
executables:
|
45
|
+
- deploku
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- ".gitignore"
|
50
|
+
- Gemfile
|
51
|
+
- LICENSE.txt
|
52
|
+
- README.md
|
53
|
+
- Rakefile
|
54
|
+
- bin/deploku
|
55
|
+
- deploku.gemspec
|
56
|
+
- lib/deploku.rb
|
57
|
+
- lib/deploku/control.rb
|
58
|
+
- lib/deploku/migration.rb
|
59
|
+
- lib/deploku/remote.rb
|
60
|
+
- lib/deploku/runnable.rb
|
61
|
+
- lib/deploku/version.rb
|
62
|
+
homepage: ''
|
63
|
+
licenses:
|
64
|
+
- MIT
|
65
|
+
metadata: {}
|
66
|
+
post_install_message:
|
67
|
+
rdoc_options: []
|
68
|
+
require_paths:
|
69
|
+
- lib
|
70
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '0'
|
80
|
+
requirements: []
|
81
|
+
rubyforge_project:
|
82
|
+
rubygems_version: 2.2.2
|
83
|
+
signing_key:
|
84
|
+
specification_version: 4
|
85
|
+
summary: Convenient way of deploying to Heroku
|
86
|
+
test_files: []
|