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 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
@@ -0,0 +1,16 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+
16
+ .ruby-version
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in deploku.gemspec
4
+ gemspec
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
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
data/bin/deploku ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib') unless $LOAD_PATH.include?(File.dirname(__FILE__) + '/../lib')
5
+ require 'bundler'
6
+ require 'deploku'
7
+
8
+ Deploku::Control.run ARGV.dup
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
@@ -0,0 +1,3 @@
1
+ module Deploku
2
+ VERSION = "0.0.1"
3
+ end
data/lib/deploku.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'ostruct'
2
+ require "deploku/runnable"
3
+ require "deploku/migration"
4
+ require "deploku/remote"
5
+ require "deploku/control"
6
+ require "deploku/version"
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: []