Add heroku push implementation
This commit is contained in:
parent
d4058130e4
commit
c16dc5c9c9
|
@ -0,0 +1,21 @@
|
|||
module VagrantPlugins
|
||||
module HerokuPush
|
||||
module Errors
|
||||
class Error < Vagrant::Errors::VagrantError
|
||||
error_namespace("heroku_push.errors")
|
||||
end
|
||||
|
||||
class CommandFailed < Error
|
||||
error_key(:command_failed)
|
||||
end
|
||||
|
||||
class GitNotFound < Error
|
||||
error_key(:git_not_found)
|
||||
end
|
||||
|
||||
class NotAGitRepo < Error
|
||||
error_key(:not_a_git_repo)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,110 @@
|
|||
require "vagrant/util/safe_exec"
|
||||
require "vagrant/util/subprocess"
|
||||
require "vagrant/util/which"
|
||||
|
||||
require_relative "errors"
|
||||
|
||||
module VagrantPlugins
|
||||
module HerokuPush
|
||||
class Push < Vagrant.plugin("2", :push)
|
||||
def push
|
||||
# Expand any paths relative to the root
|
||||
dir = File.expand_path(config.dir, env.root_path)
|
||||
|
||||
# Verify git is installed
|
||||
verify_git_bin!(config.git_bin)
|
||||
|
||||
# Verify we are operating in a git repo
|
||||
verify_git_repo!(dir)
|
||||
|
||||
# Check if we need to add the git remote
|
||||
if !has_git_remote?(config.remote, dir)
|
||||
add_heroku_git_remote(config.remote, config.app, dir)
|
||||
end
|
||||
|
||||
# Push to Heroku
|
||||
git_push_heroku(config.remote, config.branch, dir)
|
||||
end
|
||||
|
||||
# Verify that git is installed.
|
||||
# @raise [Errors::GitNotFound]
|
||||
def verify_git_bin!(path)
|
||||
if Vagrant::Util::Which.which(path).nil?
|
||||
raise Errors::GitNotFound, bin: path
|
||||
end
|
||||
end
|
||||
|
||||
# Verify that the given path is a git directory.
|
||||
# @raise [Errors::NotAGitRepo]
|
||||
# @param [String]
|
||||
def verify_git_repo!(path)
|
||||
if !File.directory?(git_dir(path))
|
||||
raise Errors::NotAGitRepo, path: path
|
||||
end
|
||||
end
|
||||
|
||||
# The git directory for the given path.
|
||||
# @param [String] path
|
||||
# @return [String]
|
||||
def git_dir(path)
|
||||
"#{path}/.git"
|
||||
end
|
||||
|
||||
# Push to the Heroku remote.
|
||||
# @param [String] remote
|
||||
# @param [String] branch
|
||||
def git_push_heroku(remote, branch, path)
|
||||
execute!("git",
|
||||
"--git-dir", git_dir(path),
|
||||
"--work-tree", path,
|
||||
"push", remote, branch,
|
||||
)
|
||||
end
|
||||
|
||||
# Check if the git remote has the given remote.
|
||||
# @param [String] remote
|
||||
# @return [true, false]
|
||||
def has_git_remote?(remote, path)
|
||||
result = execute!("git",
|
||||
"--git-dir", git_dir(path),
|
||||
"--work-tree", path,
|
||||
"remote",
|
||||
)
|
||||
remotes = result.stdout.split(/\r?\n/).map(&:strip)
|
||||
remotes.include?(remote.to_s)
|
||||
end
|
||||
|
||||
# Add the Heroku to the current repository.
|
||||
# @param [String] remote
|
||||
# @param [String] app
|
||||
def add_heroku_git_remote(remote, app, path)
|
||||
execute!("git",
|
||||
"--git-dir", git_dir(path),
|
||||
"--work-tree", path,
|
||||
"remote", "add", remote, heroku_git_url(app),
|
||||
)
|
||||
end
|
||||
|
||||
# The URL for this project on Heroku.
|
||||
# @return [String]
|
||||
def heroku_git_url(app)
|
||||
"git@heroku.com:#{app}.git"
|
||||
end
|
||||
|
||||
# Execute the command, raising an exception if it fails.
|
||||
# @return [Vagrant::Util::Subprocess::Result]
|
||||
def execute!(*cmd)
|
||||
result = Vagrant::Util::Subprocess.execute(*cmd)
|
||||
|
||||
if result.exit_code != 0
|
||||
raise Errors::CommandFailed,
|
||||
cmd: cmd.join(" "),
|
||||
stdout: result.stdout,
|
||||
stderr: result.stderr
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,279 @@
|
|||
require_relative "../../../base"
|
||||
|
||||
require Vagrant.source_root.join("plugins/pushes/heroku/push")
|
||||
|
||||
describe VagrantPlugins::HerokuPush::Push do
|
||||
include_context "unit"
|
||||
|
||||
before(:all) do
|
||||
I18n.load_path << Vagrant.source_root.join("plugins/pushes/heroku/locales/en.yml")
|
||||
I18n.reload!
|
||||
end
|
||||
|
||||
let(:env) { isolated_environment }
|
||||
let(:config) do
|
||||
double("config",
|
||||
app: "bacon",
|
||||
dir: "lib",
|
||||
git_bin: "git",
|
||||
remote: "heroku",
|
||||
branch: "master",
|
||||
)
|
||||
end
|
||||
|
||||
subject { described_class.new(env, config) }
|
||||
|
||||
describe "#push" do
|
||||
let(:root_path) { "/handy/dandy" }
|
||||
let(:dir) { "#{root_path}/#{config.dir}" }
|
||||
|
||||
before do
|
||||
allow(subject).to receive(:verify_git_bin!)
|
||||
allow(subject).to receive(:verify_git_repo!)
|
||||
allow(subject).to receive(:has_git_remote?)
|
||||
allow(subject).to receive(:add_heroku_git_remote)
|
||||
allow(subject).to receive(:git_push_heroku)
|
||||
allow(subject).to receive(:execute!)
|
||||
|
||||
allow(env).to receive(:root_path)
|
||||
.and_return(root_path)
|
||||
end
|
||||
|
||||
it "verifies the git bin is present" do
|
||||
expect(subject).to receive(:verify_git_bin!)
|
||||
.with(config.git_bin)
|
||||
subject.push
|
||||
end
|
||||
|
||||
it "verifies the directory is a git repo" do
|
||||
expect(subject).to receive(:verify_git_repo!)
|
||||
.with(dir)
|
||||
subject.push
|
||||
end
|
||||
|
||||
context "when the heroku remote exists" do
|
||||
before do
|
||||
allow(subject).to receive(:has_git_remote?)
|
||||
.and_return(true)
|
||||
end
|
||||
|
||||
it "does not add the heroku remote" do
|
||||
expect(subject).to_not receive(:add_heroku_git_remote)
|
||||
subject.push
|
||||
end
|
||||
end
|
||||
|
||||
context "when the heroku remote does not exist" do
|
||||
before do
|
||||
allow(subject).to receive(:has_git_remote?)
|
||||
.and_return(false)
|
||||
end
|
||||
|
||||
it "adds the heroku remote" do
|
||||
expect(subject).to receive(:add_heroku_git_remote)
|
||||
.with(config.remote, config.app, dir)
|
||||
subject.push
|
||||
end
|
||||
end
|
||||
|
||||
it "pushes to heroku" do
|
||||
expect(subject).to receive(:git_push_heroku)
|
||||
.with(config.remote, config.branch, dir)
|
||||
subject.push
|
||||
end
|
||||
end
|
||||
|
||||
describe "#verify_git_bin!" do
|
||||
context "when git does not exist" do
|
||||
before do
|
||||
allow(Vagrant::Util::Which).to receive(:which)
|
||||
.with("git")
|
||||
.and_return(nil)
|
||||
end
|
||||
|
||||
it "raises an exception" do
|
||||
expect {
|
||||
subject.verify_git_bin!("git")
|
||||
} .to raise_error(VagrantPlugins::HerokuPush::Errors::GitNotFound) { |error|
|
||||
expect(error.message).to eq(I18n.t("heroku_push.errors.git_not_found",
|
||||
bin: "git",
|
||||
))
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
context "when git exists" do
|
||||
before do
|
||||
allow(Vagrant::Util::Which).to receive(:which)
|
||||
.with("git")
|
||||
.and_return("git")
|
||||
end
|
||||
|
||||
it "does not raise an exception" do
|
||||
expect { subject.verify_git_bin!("git") }.to_not raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#verify_git_repo!" do
|
||||
context "when the path is a git repo" do
|
||||
before do
|
||||
allow(File).to receive(:directory?)
|
||||
.with("/repo/path/.git")
|
||||
.and_return(false)
|
||||
end
|
||||
|
||||
it "raises an exception" do
|
||||
expect {
|
||||
subject.verify_git_repo!("/repo/path")
|
||||
} .to raise_error(VagrantPlugins::HerokuPush::Errors::NotAGitRepo) { |error|
|
||||
expect(error.message).to eq(I18n.t("heroku_push.errors.not_a_git_repo",
|
||||
path: "/repo/path",
|
||||
))
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
context "when the path is not a git repo" do
|
||||
before do
|
||||
allow(File).to receive(:directory?)
|
||||
.with("/repo/path/.git")
|
||||
.and_return(true)
|
||||
end
|
||||
|
||||
it "does not raise an exception" do
|
||||
expect { subject.verify_git_repo!("/repo/path") }.to_not raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#git_push_heroku" do
|
||||
let(:dir) { "." }
|
||||
|
||||
before { allow(subject).to receive(:execute!) }
|
||||
|
||||
it "executes the proper command" do
|
||||
expect(subject).to receive(:execute!)
|
||||
.with("git",
|
||||
"--git-dir", "#{dir}/.git",
|
||||
"--work-tree", dir,
|
||||
"push", "bacon", "hamlet",
|
||||
)
|
||||
subject.git_push_heroku("bacon", "hamlet", dir)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#has_git_remote?" do
|
||||
let(:dir) { "." }
|
||||
|
||||
let(:process) do
|
||||
double("process",
|
||||
stdout: "origin\r\nbacon\nhello"
|
||||
)
|
||||
end
|
||||
|
||||
before do
|
||||
allow(subject).to receive(:execute!)
|
||||
.and_return(process)
|
||||
end
|
||||
|
||||
it "executes the proper command" do
|
||||
expect(subject).to receive(:execute!)
|
||||
.with("git",
|
||||
"--git-dir", "#{dir}/.git",
|
||||
"--work-tree", dir,
|
||||
"remote",
|
||||
)
|
||||
subject.has_git_remote?("bacon", dir)
|
||||
end
|
||||
|
||||
it "returns true when the remote exists" do
|
||||
expect(subject.has_git_remote?("origin", dir)).to be(true)
|
||||
expect(subject.has_git_remote?("bacon", dir)).to be(true)
|
||||
expect(subject.has_git_remote?("hello", dir)).to be(true)
|
||||
end
|
||||
|
||||
it "returns false when the remote does not exist" do
|
||||
expect(subject.has_git_remote?("nope", dir)).to be(false)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#add_heroku_git_remote" do
|
||||
let(:dir) { "." }
|
||||
|
||||
before do
|
||||
allow(subject).to receive(:execute!)
|
||||
allow(subject).to receive(:heroku_git_url)
|
||||
.with("app")
|
||||
.and_return("HEROKU_URL")
|
||||
end
|
||||
|
||||
it "executes the proper command" do
|
||||
expect(subject).to receive(:execute!)
|
||||
.with("git",
|
||||
"--git-dir", "#{dir}/.git",
|
||||
"--work-tree", dir,
|
||||
"remote", "add", "bacon", "HEROKU_URL",
|
||||
)
|
||||
subject.add_heroku_git_remote("bacon", "app", dir)
|
||||
end
|
||||
end
|
||||
|
||||
describe "#heroku_git_url" do
|
||||
it "returns the proper string" do
|
||||
expect(subject.heroku_git_url("bacon"))
|
||||
.to eq("git@heroku.com:bacon.git")
|
||||
end
|
||||
end
|
||||
|
||||
describe "#git_dir" do
|
||||
it "returns the .git directory for the path" do
|
||||
expect(subject.git_dir("/path")).to eq("/path/.git")
|
||||
end
|
||||
end
|
||||
|
||||
describe "#execute!" do
|
||||
let(:exit_code) { 0 }
|
||||
let(:stdout) { "This is the output" }
|
||||
let(:stderr) { "This is the errput" }
|
||||
|
||||
let(:process) do
|
||||
double("process",
|
||||
exit_code: exit_code,
|
||||
stdout: stdout,
|
||||
stderr: stderr,
|
||||
)
|
||||
end
|
||||
|
||||
before do
|
||||
allow(Vagrant::Util::Subprocess).to receive(:execute)
|
||||
.and_return(process)
|
||||
end
|
||||
|
||||
it "creates a subprocess" do
|
||||
expect(Vagrant::Util::Subprocess).to receive(:execute)
|
||||
expect { subject.execute! }.to_not raise_error
|
||||
end
|
||||
|
||||
it "returns the resulting process" do
|
||||
expect(subject.execute!).to be(process)
|
||||
end
|
||||
|
||||
context "when the exit code is non-zero" do
|
||||
let(:exit_code) { 1 }
|
||||
|
||||
it "raises an exception" do
|
||||
klass = VagrantPlugins::HerokuPush::Errors::CommandFailed
|
||||
cmd = ["foo", "bar"]
|
||||
|
||||
expect { subject.execute!(*cmd) }.to raise_error(klass) { |error|
|
||||
expect(error.message).to eq(I18n.t("heroku_push.errors.command_failed",
|
||||
cmd: cmd.join(" "),
|
||||
stdout: stdout,
|
||||
stderr: stderr,
|
||||
))
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue