Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Break some of the build logic out from pipelines #7

Merged
merged 7 commits into from
Jan 11, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/escobar.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ def self.github_api_token
require_relative "./escobar/github/client"
require_relative "./escobar/heroku/app"
require_relative "./escobar/heroku/build"
require_relative "./escobar/heroku/build_request"
require_relative "./escobar/heroku/client"
require_relative "./escobar/heroku/coupling"
require_relative "./escobar/heroku/pipeline"
4 changes: 4 additions & 0 deletions lib/escobar/heroku/app.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ def locked?
response = client.heroku.get("/apps/#{id}/config-vars")
response["id"] == "two_factor"
end

def build_request_for(pipeline)
Escobar::Heroku::BuildRequest.new(pipeline, self)
end
end
end
end
154 changes: 154 additions & 0 deletions lib/escobar/heroku/build_request.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
module Escobar
module Heroku
# Class representing a heroku build request
class BuildRequest
# Class representing some failure when requesting a build
class Error < StandardError
attr_accessor :build_request

def self.new_from_build_request(build_request, message)
error = new(message)
error.build_request = build_request
error
end

def dashboard_url
"https://dashboard.heroku.com/apps/#{build_request.app.name}"
end
end

attr_reader :app, :github_deployment_url, :pipeline, :sha

attr_accessor :environment, :ref, :forced, :custom_payload

def initialize(pipeline, app)
@app = app
@pipeline = pipeline
end

def error_for(message)
Error.new_from_build_request(self, message)
end

def create(task, environment, ref, forced, custom_payload)
if app.locked?
raise error_for("Application requires second factor: #{app.name}")
end

@environment = environment
@ref = ref
@forced = forced
@custom_payload = custom_payload

create_in_api(task)
end

def create_in_api(task)
create_github_deployment(task)

build = create_heroku_build
if build["id"] =~ Escobar::UUID_REGEX
process_heroku_build(build)
else
raise error_for(
"Unable to create heroku build for #{app.name}: #{build['message']}"
)
end
end

def process_heroku_build(build)
heroku_build = Escobar::Heroku::Build.new(
app.client, app, build["id"]
)

create_github_pending_deployment_status(heroku_build)

heroku_build.github_url = github_deployment_url
heroku_build.pipeline_name = pipeline.name
heroku_build.sha = sha

heroku_build
end

def create_heroku_build
body = {
source_blob: {
url: github_client.archive_link(sha),
version: sha[0..7],
version_description: "#{pipeline.github_repository}:#{sha}"
}
}
app.client.heroku.post("/apps/#{app.name}/builds", body)
end

def handle_github_deployment_response(response)
unless response["sha"]
raise error_for(
"Unable to create GitHub deployments for " \
"#{pipeline.github_repository}: #{response['message']}"
)
end

@sha = response["sha"]
@github_deployment_url = response["url"]
response
end

def create_github_deployment(task)
options = {
ref: ref,
task: task,
auto_merge: !forced,
payload: custom_payload.merge(custom_deployment_payload),
environment: environment,
required_contexts: required_commit_contexts
}
response = github_client.create_deployment(options)
handle_github_deployment_response(response)
end

def create_deployment_status(url, payload)
github_client.create_deployment_status(url, payload)
end

def create_github_pending_deployment_status(heroku_build)
create_github_deployment_status(
github_deployment_url,
heroku_build.dashboard_build_output_url,
"pending",
"Build running.."
)
end

def create_github_deployment_status(url, target_url, state, description)
payload = {
state: state,
target_url: target_url,
description: description
}
create_deployment_status(url, payload)
end

def custom_deployment_payload
{ name: app.name, pipeline: pipeline.to_hash, provider: "slash-heroku" }
end

def required_commit_contexts
return [] if forced
github_client.required_contexts.map do |context|
if context == "continuous-integration/travis-ci"
context = "continuous-integration/travis-ci/push"
end
context
end
end

def github_client
@github_client ||= Escobar::GitHub::Client.new(
app.client.github_token,
pipeline.github_repository
)
end
end
end
end
57 changes: 10 additions & 47 deletions lib/escobar/heroku/pipeline.rb
Original file line number Diff line number Diff line change
Expand Up @@ -92,43 +92,20 @@ def reap_build(app_id, build_id)
end

# rubocop:disable Metrics/LineLength
def create_deployment_from(app, github_deployment, sha, build)
case build["id"]
when "two_factor"
description = "A second factor is required. Use your configured authenticator app or yubikey."
create_github_deployment_status(github_deployment["url"], nil, "failure", description)
raise ArgumentError, build["message"]
when Escobar::UUID_REGEX
heroku_build = Escobar::Heroku::Build.new(
client, app, build["id"]
)
heroku_build.github_url = github_deployment["url"]
heroku_build.pipeline_name = name
heroku_build.sha = sha

create_github_deployment_status(
github_deployment["url"],
heroku_build.dashboard_build_output_url,
"pending",
"Build running.."
)

heroku_build
else
raise ArgumentError, "Unable to create heroku build for #{name}"
end
end

def create_deployment(ref, environment, force = false, custom_payload = {})
app = environments[environment] && environments[environment].last
return({ error: "No '#{environment}' environment for #{name}." }) unless app
unless app
raise ArgumentError, "No '#{environment}' environment for #{name}."
end

heroku_app = app.app

github_deployment = create_github_deployment("deploy", ref, environment, force, custom_payload)
return({ error: github_deployment["message"] }) unless github_deployment["sha"]
build_request = heroku_app.build_request_for(self)
heroku_build = build_request.create(
"deploy", environment, ref, force, custom_payload
)

sha = github_deployment["sha"]
build = create_heroku_build(app.name, sha)
create_deployment_from(app, github_deployment, sha, build)
heroku_build
end
# rubocop:enable Metrics/LineLength

Expand Down Expand Up @@ -160,20 +137,6 @@ def custom_deployment_payload
{ name: name, pipeline: self.to_hash, provider: "slash-heroku" }
end

def create_github_deployment(task, ref, environment, force, extras = {})
required_contexts = required_commit_contexts(force)

options = {
ref: ref,
task: task,
auto_merge: !force,
payload: extras.merge(custom_deployment_payload),
environment: environment,
required_contexts: required_contexts
}
github_client.create_deployment(options)
end

def create_github_deployment_status(url, target_url, state, description)
payload = {
state: state,
Expand Down
2 changes: 1 addition & 1 deletion lib/escobar/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module Escobar
VERSION = "0.3.1".freeze
VERSION = "0.3.2".freeze
end
29 changes: 9 additions & 20 deletions spec/lib/escobar/heroku/pipeline_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@
stub_heroku_response("#{pipeline_path}/pipeline-couplings")
stub_kolkrabbi_response("#{pipeline_path}/repository")

stub_request(:get, "https://api.heroku.com/apps/b0deddbf-cf56-48e4-8c3a-3ea143be2333/config-vars")
.to_return(status: 200, body: "", headers: {})

response = fixture_data("api.github.com/repos/atmos/slash-heroku/index")
stub_request(:get, "https://api.github.com/repos/atmos/slash-heroku")
.with(headers: default_github_headers)
Expand Down Expand Up @@ -121,8 +124,9 @@

pipeline = Escobar::Heroku::Pipeline.new(client, id, name)
deployment = pipeline.create_deployment("master", "production")

expect(deployment.id).to eql("01234567-89ab-cdef-0123-456789abcdef")
expect(deployment.app_id).to eql("e539d5b3-2ede-4e51-80e3-3b5b47678bf4")
expect(deployment.app_id).to eql("b0deddbf-cf56-48e4-8c3a-3ea143be2333")
expect(deployment.github_url).to eql("https://api.github.com/repos/atmos/slash-heroku/deployments/22062424")
expect(deployment.dashboard_build_output_url).to eql(
"https://dashboard.heroku.com/apps/slash-heroku-production/activity/builds/01234567-89ab-cdef-0123-456789abcdef"
Expand All @@ -134,7 +138,7 @@
sha: "8115792777a8d60fcf1c5e181ce3c3bc34e5eb1b",
name: "slash-heroku",
repo: "atmos/slash-heroku",
app_id: "e539d5b3-2ede-4e51-80e3-3b5b47678bf4",
app_id: "b0deddbf-cf56-48e4-8c3a-3ea143be2333",
app_name: "slash-heroku-production",
build_id: "01234567-89ab-cdef-0123-456789abcdef",
command_id: nil,
Expand Down Expand Up @@ -166,30 +170,15 @@
.with(headers: default_github_headers)
.to_return(status: 200, body: response, headers: {})

tarball_headers = {
"Location": "https://codeload.github.com/atmos/slash-heroku/legacy.tar.gz/8115792777a8d60fcf1c5e181ce3c3bc34e5eb1b"
}
stub_request(:head, "https://api.github.com/repos/atmos/slash-heroku/tarball/8115792777a8d60fcf1c5e181ce3c3bc34e5eb1b")
.with(headers: default_github_headers)
.to_return(status: 200, body: nil, headers: tarball_headers)

response = fixture_data("api.heroku.com/failed-2fa")
stub_request(:post, "https://api.heroku.com/apps/slash-heroku-production/builds")
.with(body: "{\"source_blob\":{\"url\":\"https://codeload.github.com/atmos/slash-heroku/legacy.tar.gz/8115792777a8d60fcf1c5e181ce3c3bc34e5eb1b\",\"version\":\"81157927\",\"version_description\":\"atmos/slash-heroku:8115792777a8d60fcf1c5e181ce3c3bc34e5eb1b\"}}")
stub_request(:get, "https://api.heroku.com/apps/b0deddbf-cf56-48e4-8c3a-3ea143be2333/config-vars")
.to_return(status: 403, body: response, headers: {})

response = fixture_data("api.github.com/repos/atmos/slash-heroku/deployments/22062424/statuses/pending-1")
stub_request(:post, "https://api.github.com/repos/atmos/slash-heroku/deployments/22062424/statuses")
.with(headers: default_github_headers)
.to_return(status: 200, body: response, headers: {})

pipeline = Escobar::Heroku::Pipeline.new(client, id, name)
expect { pipeline.create_deployment("master", "production") }
.to raise_error(
ArgumentError,
"A second authentication factor or pre-authorization is required " \
"for this request. Your account has either two-factor or a YubiKey " \
"registered."
Escobar::Heroku::BuildRequest::Error,
"Application requires second factor: slash-heroku-production"
)
end
# rubocop:enable Metrics/LineLength
Expand Down